제 3 부프로그램의실행 I 7. 프로그램의실행기법 프로세스의일생 메모리관리 프로세스에의한메모리확보 프로세스간통신 프로세스환경 프로세스스케줄링
소개 프로그램이란컴퓨터가할일을명시한것이다. 결국컴퓨터의임무는프로그램을수행시킴으로서달성된다. 그러면어떻게프로그램을수행시키는것이효율적일까? 그해답이바로 프로세스 란개념을이용하는것이다. 프로세스 (process) 란프로그램을처리장치에서수행할때의모습이다. 이 모습 은실행중의프로그램의실체로서메모리상의코드, CPU 에서사용하는제반레지스터 ( 임시데이터 ), 운영체제가실행을위해필요로하는자료구조등을포함한다. 따라서컴퓨터의일차적인목표는이들프로세스를효율적으로관리하는것이다. 프로세스관리를위해서는프로세스에관련된자료구조의관리, 메모리관리, 프로세스스케줄링등의작업이필요하다. 이장에서는프로세스를중심으로한프로그램의실행과정과실행특성을살펴본다. 프로세스는컴퓨터의모든작업과연관되어있으므로프로세스를알면다른모든컴퓨터동작특성도파악할수있다. 그러므로프로세스에대한지식은컴퓨터의동작을이해하는데핵심이다. Spring 2010 System Programming (Shin) 2
7.1 프로세스의일생
Linux 의초기동작 1 단계 : init 프로세스의생성 로더 (loader) 에의해디스크상의커널프로그램을메모리에적재 커널은일련의프로세스를생성 마지막으로 /sbin/init 을 exec 하여 init 프로세스를생성 2 단계 : login 프로세스의생성 init 프로세스에의해각종프로세스가생성됨 디몬 (daemon) 프로세스의생성 daemon 프로세스란어느한단말 ( 혹은사용자 ) 에속하지않고배경에서서비스를제공하는서버 서버는통상컴퓨터가동작하는동안계속해서동작하면서클라이언트에게서비스를제공 login 프로세스의생성 login 프로세스는사용자로부터의로그인을받아들여사용자의 shell 프로그램을다시 exec 함 Spring 2010 System Programming (Shin) 4
[ 註 例 ] Linux 의초기동작 Linux 동작시키기 1 단계 1. 컴퓨터전원이켜지면레지스터들이미리정해진값으로설정되어 boot ROM 에저장된코드 (bootstrap loader) 를수행 2. 이부트로더가디스크블록 0 에있는 boot block 을메모리에적재 3. 이 boot block program 이커널라이브러리 ( 예 : /boot/vmlinux) 를적재. 적재후제어를이커널로넘김 4. 커널은 process 0 를직접만듦. 5. process 0 는자식프로세스로서 process 1 을생성 (fork) 6. process 1 은 /sbin/init 를 exec 함 Process 0 직접작성된최초의커널프로세스 Process 0 는자식프로세스로서 Process 1 을생성 (fork) Process 1 init exec Process 1 은 /sbin/init 을 exec 함 Spring 2010 System Programming (Shin) 5
[ 註 例 ] Linux 의초기동작 Linux 동작시키기 2 단계 1. init 프로세스는 daemon 을생성함 (fork 와 exec 이용 ) 2. 단말을위하여 getty 프로세스를생성함 (fork 와 exec 이용 ) 3. getty 프로세스는다시 login 프로세스를 exec 함 Process 0 Daemons e.g. ftpd, httpd init init getty login exec exec daemon 과 getty 생성 Spring 2010 System Programming (Shin) 6
프로세스의생성과종료 프로세스의생성과종료과정 fork, exec ( 선택적 ), wait, exit parent 시간 parent wait() fork() [ exec() ] child exit() Spring 2010 System Programming (Shin) 7
[ 註 例 ] 프로세스의생성과종료 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(int argc, char *argv[]) { pid_t pid; if (pid == 0) { /* child */ execl(argv[1], argv[1], argv[2], NULL); /* error if the next line is executed */ perror(argv[1]); exit(99); } else { /* parent */ int status; if (argc!= 3) { fprintf(stderr, "Usage:?\n", argv[0]); exit(1); } pid = fork(); if (pid < 0) { fprintf(stderr, "fork() failed\n"); exit(1); } /* to be continued */ } } waitpid(pid, &status, 0); printf("child (PID=%d) has exited: ", pid); /* may check the exit status to see how the child is terminated, and then take appropriate actions. */ exit(0); exec() 이정상적으로실행되는경우, 호출프로그램으로복귀하지않음 (no return) Spring 2010 System Programming (Shin) 8
Linux 프로세스의계통도 Linux 프로세스의상호관계 Process 0 init Daemons Login shell getty s Child Child Child Grandchild Grandchild Grandchild Spring 2010 System Programming (Shin) 9
[ 註 例 ] Linux 프로세스의상호관계 프로세스그룹 process group shell 을위한존재 signal 에대해그룹내의모든프로세스가반응 각프로세스는한개의프로세스그룹에속함. 통상, command pipeline 에속한프로세스들이같은그룹에속함 예 : ls grep id wc -w 프로세스 / 그룹의 id 획득및설정 getpid() [ 자신 ] tty4 getppid() [ 부모 ] getpgrp() [ 프로세스그룹 ] setpgid(): [ 프로세스그룹 id 변경 ] session process group session 사용자가한단말에서로그인해서마칠때까지의흐름을관리하기위한개념 즉, 한단말에서작업하던사용자가 logout 혹은중단하면어떻게할것인가? 로그인쉘로부터 ( 같은단말에서 ) 생성된모든프로세스그룹의집합 세션을생성한프로세스가 session leader ( 통상 shell) 가됨 세션 ID 는세션리더의 ID 한세션이종료하게되면, 이를 signal 에의해세션리더에게알림. 세션리더는소속프로세스그룹을중단시킴 (kill) 세션과연관된단말을제어단말 (controlling terminal) 이라함 만일 shell 이먼저중단되면? (init process takes up shell s position) Spring 2010 System Programming (Shin) 10
[ 註 例 ] Linux 프로세스의상호관계 Process groups linux> pstree -p (-p: pid 표시 ) Background init(1)-+-acpid(2108) -httpd(2605)-+-httpd(29741) -httpd(17157) -httpd(17158) `-httpd(23987) -sendmail(2572) acpid pid=2108 pgrp=2108 -sshd(2201)---sshd(12530)---sshd(12535)---bash(12536)---pstree(12644) httpd pid=29741 pgrp=2605 Background httpd pid=2605 pgrp=2605 httpd pid=17157 pgrp=2605 init pid=1 pgrp=0 httpd pid=17158 pgrp=2605 Background sendmail pid=2572 pgrp=2572 Foreground sshd pid=2201 pgrp=2201 shinhs@pts/1 sshd pid=12535 pgrp=12530 pstree pid=12644 pgrp=12644 Background shinhs sshd pid=12530 pgrp=12530 bash pid=12536 pgrp=12536 Spring 2010 System Programming (Shin) 11
디몬프로세스 Daemons 특징 배경 (background) 에서사용자와대화없이실행됨 시스템전체나사용자프로그램에주어진서비스를제공 디몬은일반적으로프로세스그룹 leader 이며 session leader 임 디몬의부모는 init process (pid=1) 디몬의생성절차 (1) fork() 에의해새로운 ( 자식 ) 프로세스생성. 부모프로세스는 exit (2) 새로운세션을생성 : setsid() 디몬이새로운세션의리더인동시에프로세스그룹의리더가됨 단말 (terminal) 과의연계를해제 : 더이상 controlling terminal 이없음 (3) root directory 를 cwd(current working directory) 로함 연계된파일시스템이없게하여파일시스템을제거해도영향이없음 (4) umask(user file creation mode mask) 를 0 으로함 디몬에서새로운파일을생성할때영향이없게함 (5) 부모프로세스로부터전달받은파일디스크립터를모두닫음 통상, stdin, stdout, stderr umask: 파일의접근허용 (permission) 모드를새로설정할때사용하는 mask. 예 : 사용자의 umask가 8진법으로 042라면, 새파일을만들때 777 (default) - 042 (umask) = 735 ( 새파일의접근허용값 ) Spring 2010 System Programming (Shin) 12
/* lpedated.c Simple timestamping daemon */ #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <syslog.h> int main(void) { pid_t pid, sid; time_t timebuf; int fd, len; /* Open the system log */ openlog("lpedated", LOG_PID, LOG_DAEMON); pid = fork(); (1) if(pid < 0) { syslog(log_err, "%s\n", perror); exit(exit_failure); } [ 註 例 ] Daemons if(pid > 0) /* In the parent, let's bail */ exit(exit_success); (1) /* In the child... */ /* First, start a new session */ if((sid = setsid()) < 0) { (2) syslog(log_err, "%s\n", "setsid"); exit(exit_failure); } /* Next, make / the current dir */ if((chdir("/")) < 0) { (3) syslog(log_err, "%s\n", "chdir"); exit(exit_failure); } /* Reset the file mode */ umask(0); (4) /* Close stdin, etc. */ close(stdin_fileno); (5) close(stdout_fileno); close(stderr_fileno); } /* Finally, do our work */ len = strlen(ctime(&timebuf)); while(1) { char *buf = malloc(sizeof(char) * (len + 1)); if(buf == NULL) { syslog(log_err, "malloc"); exit(exit_failure); } if((fd = open("/var/log/lpedated.log", O_CREAT O_WRONLY O_APPEND, 0600)) < 0) { syslog(log_err, "open"); exit(exit_failure); } time(&timebuf); strncpy(buf, ctime(&timebuf), len + 1); write(fd, buf, len + 1); close(fd); sleep(60); } closelog(); exit(exit_success); Kurt Wall, Linux Programming by Examples. Que, 2000, pp. 194-195 Spring 2010 System Programming (Shin) 13
7.2 메모리관리
프로그램의실행과메모리 프로그램의적재 (loading) 및실행 적재 : 디스크상의프로그램파일을주기억으로옮김. 실행 : 메모리상의코드및데이터를참조하여야함. 메모리와관련된문제점 물리메모리의기억공간이해당파일전체를수용하기에충분하지않을수있음 디스크에서페이지단위로읽어와야함. 페이지가물리메모리의어디에위치하게될지모름 실행시참조하는메모리주소는가상기억의주소임 가상기억관리기법에의한해결책 물리메모리와가상기억 ( 혹은디스크공간 ) 을일정한크기의페이지단위로관리 물리메모리공간에맞추어, 디스크에서페이지단위로프로그램파일의필요한부분만을메모리에적재가능 가상주소공간과물리주소공간사이를연계하는주소변환작업을수행 - 주소사상 (mapping) 기법 Spring 2010 System Programming (Shin) 15
[ 註 例 ] 프로그램의실행과메모리 프로그램의적재시 프로그램 물리메모리 파일 페이지단위로디스크의프로그램을사용가능한물리메모리에적재. 이때, 물리메모리도디스크의페이지와같은크기의 블록 (block) 단위로구성됨 프로그램의실행시메모리주소의참조 프로그램 ( 가상주소공간 ) 주소변환메커니즘 물리메모리 ( 물리주소공간 ) CPU 가상주소 (virtual address) 물리주소 (physical address) Spring 2010 System Programming (Shin) 16
주소의변환 동기 가상기억의기억공간 ( 즉, 주소공간 ) 과물리메모리기억공간과의괴리 실행되기전까지는문제가없지만, 실행시디스크에저장된프로그램과데이터를주기억으로적재하여실행하여야함 왜냐하면, CPU 에서실행되는프로그램은가상기억의주소밖에모름 이들사이의불일치를어떻게해결하여야하는가 운영체제가실행중인프로그램을위해서가상기억주소를주기억주소로변환 주소변환 address translation 가상주소를물리주소로사상하는작업 주소공간의정의 가상주소공간 virtual address space V = {v: v 는정수, 0 v N 1} 물리주소공간 physical address space P = {p: p 는정수, 0 p M 1} 단, M N 주소변환함수 T 의정의 T: V P U ( 는공집합 ) T(v) : 이경우는가상주소 v 의데이터가물리메모리에존재하지않을때. 즉, 데이터가디스크에서주기억으로적재되지않았거나아예없는경우 이경우를 page fault 라부름 Spring 2010 System Programming (Shin) 17
주소의변환 주소포맷의구성 주소 : 페이지번호 + 페이지내에서의변위 (offset) 페이지크기 P = 2 p (bytes), 최대가상주소 N = 2 n, 최대물리주소 M = 2 m n 1 p p 1 0 virtual page number virtual page offset 가상주소 m 1 physical page number p p 1 physical page offset 0 물리주소 페이지테이블을이용한주소변환 가상페이지 페이지테이블 물리페이지 ( 실제메모리 ) 페이지테이블 (page table) 의구성 인덱스 (index): 가상페이지번호 페이지테이블엔트리 ( 내용 ): 물리페이지번호, 즉블록번호 단, 페이지내에서의오프셋은변하지않음 주소변환메커니즘의구현 memory management unit(mmu) 란하드웨어에의해구현됨 Spring 2010 System Programming (Shin) 18
[ 註 例 ] 주소의변환 처리과정 memory hit: 원하는페이지가주기억에존재할때 주소변환메커니즘을통하여참조주소에액세스 memory miss: 원하는페이지가주기억에없을때 이경우를 page fault 라함 운영체제가해당페이지를디스크에서가져오며, 그동안프로그램수행은중단됨. Hit CPU 1 MMU 2 Memory/ Cache 경우에따라서는, 일단주기억상의페이지를디스크에써넣은후삭제하고나서, 디스크에서원하는 페이지를가져올수있음 가상주소 물리주소 Miss CPU 1 MMU 4 Memory/ Cache 2 3 Disk 페이지폴트 2 I/O unit 2 Spring 2010 System Programming (Shin) 19
[ 註 例 ] 주소의변환 페이지테이블에주소변환기법의개념 프로세스마다별개의페이지테이블 프로그램에서사용하는모든페이지를포함하며테이블에대한인덱스가가상페이지번호가됨 테이블의내용 (entry) 해당가상페이지에대한물리페이지번호 페이지에대한정보 페이지의유효성 (valid) 여부표시. 즉해당페이지가주기억에존재하는지여부 접근권리 (access right) 필드는접근허용여부를표시 Page table base register n 1 p p 1 0 virtual page number virtual page offset 가상주소 valid? physical page no. 1 0x412a8f0 Page table 0 - - - access right m 1 p p 1 0 physical page number physical page offset 물리주소 Spring 2010 System Programming (Shin) 20
주소변환의고속화 페이지테이블이용의문제점 페이지테이블을접근하는것도메모리를한번접근하는것임. 즉, 메모리를두번접근해야원하는데이터를인출 2 page table index address Page table CPU 1 virtual address MMU 3 physical page number 4 physical address data Memory/ Cache Translation Lookaside Buffer (TLB) 고속메모리를이용한페이지테이블의고속화 메모리관리장치 (MMU) 내의소규모하드웨어캐쉬 즉, TLB 는캐쉬化된축소형페이지테이블로서주소변환을고속화함 Spring 2010 System Programming (Shin) 21
[ 註 例 ] 주소변환의고속화 TLB 를이용한주소변환 TLB hit 의경우 : 주소변환이완료되어물리메모리주소가결정됨 TLB miss 의경우 : 주기억에있는페이지테이블을참조하여주소변환 Miss 2 3 page table CPU 1 virtual address MMU TLB Hit 4 2 data Memory/ Cache 단, 페이지테이블은주기억에만위치한다고가정 Spring 2010 System Programming (Shin) 22
7.3 프로세스에의한메모리확보
Linux/C 에서메모리확보방법 접근방법 실행파일생성시에결정되는메모리크기를 data 및 bss 영역에확보 전역변수 (global variable), 함수, 등의정적인데이터에대한정적인할당 (static allocation) 실행파일생성시에결정되는메모리크기를스택영역에확보 로컬변수의정적인할당 실행시에결정되는메모리크기를히프 (heap) 영역에확보 프로그램실행시에 malloc() 함수를이용하여메모리를동적인할당 (dynamic allocation) 실행시에결정되는메모리크기를스택영역에확보 프로그램실행시에 alloca() 함수를이용하여스택공간을동적할당 Spring 2010 System Programming (Shin) 24
[ 註 例 ] Linux/C 에서메모리확보방법 Linux 메모리구성 system only 커널영역 stack 공유라이브러리가매핑된부분 %esp: stack pointer push/pop 명령에의해변경됨 run-time heap 초기화되지않은데이터 (.bss) 초기화된데이터 (.data) 실행프로그램코드 (.text) brk 포인터 heap 의최상위주소의다음주소를가리킴. heap memory 의크기는 brk() 와 sbrk() 함수에의해증가할수있음. 0 Spring 2010 System Programming (Shin) 25
동적인메모리할당 Dynamic memory allocation 관련작업 메모리할당 memory allocation 메모리할당요청에대하여주어진히프 (heap) 메모리에서블록단위로배정 메모리추가할당도가능 메모리해제 free 할당된메모리를할당가능한메모리로변경 해제의의무 : 프로그래머? 시스템? 시스템인경우 ( 묵시적할당 ) garbage collector 가할당공간을해제함 관련시스템콜 malloc(): 메모리할당 realloc(): 메모리추가할당 calloc(): 메모리할당후 0 으로초기화 brk(), sbrk(): heap 영역의확장 Spring 2010 System Programming (Shin) 26
[ 註 例 ] 동적인메모리할당 (1/3) malloc 관련시스템콜 #include <stdlib.h> void *malloc(size_t size) 메모리를히프영역에할당 성공시 ( 포인터타입으로 cast 했을때 ) 메모리블록을가리키는포인터를리턴 size 가 0 이면 NULL 을리턴 실패시 NULL 을리턴하고에러번호를설정 void *realloc(void *p, size_t size) 포인터 p 가가리키는블록의크기를 size 만큼변경하고 ( 만일블록의위치를바꾸면 ) 새로운블록에의포인터를리턴함 p 가바뀌더라도 p 가가리키던블록의내용은바뀌지않음 메모리공간이부족하면 NULL 을리턴하고에러번호를설정 void *calloc(size_t nmemb, size_t size) size nmemb 바이트의메모리를히프영역에할당 할당시, 내용값을 0 으로함 Spring 2010 System Programming (Shin) 27
[ 註 例 ] 동적인메모리할당 (2/3) malloc 관련시스템콜 ( 계속 ) #include <stdlib.h> void free(void *p) pointer p 가가리키는블록을할당가능하도록함 p 는 malloc/calloc/realloc 에의해설정된것임 int brk(void *end_data_segment) void *sbrk(ptrdiff_t increment) brk() 는데이터세그먼트의상단, 즉히프의상단을 end_data_segment 값으로설정. sbrk() 는히프의상단을 increment 바이트만큼증가시킴. sbrk() 는시스템콜이아니고 C library wrapper 임. brk() 는성공시 0 를, sbrk() 는성공시증가된영역의시작주소를리턴하고에러가발생하면 -1 을리턴 Spring 2010 System Programming (Shin) 28
[ 註 例 ] 동적인메모리할당 (3/3) malloc 사용예 getcwd() 현재프로세스자신이속해있는디렉토리의경로이름 (path name) 문자열을버퍼에저장. 버퍼크기가부족하면 ERANGE 란에러리턴. #include <stdlib.h> #include <unistd.h> #include <errno.h> #define INIT_BUFSIZE 1024 char* getcwd_new(void) { char *buf, *newbuf; size_t size = INIT_BUFSIZE; } buf = malloc(size); /* 디렉토리문자열저장용 */ if (!buf) return NULL; for (;;) { errno = 0; if (getcwd(buf, size)) return buf; /* 버퍼크기 OK */ if (errno!= ERANGE) break; size *= 2; /* 버퍼크기가작으면두배로 */ newbuf = realloc(buf, size); /* 버퍼를재할당 */ if (!newbuf) break; buf = newbuf; } free(buf); /* 할당된버퍼해제 */ return NULL; Spring 2010 System Programming (Shin) 29
동적메모리관리개념 동적인메모리할당 / 해제 allocation/deallocation 히프 (heap) 메모리안에서프로그램이요청하는메모리를할당및해제 문제점 : 할당과해제가반복되어, 할당된영역과할당되지않은영역을관리 ( 추적, 할당, 재할당, 인접영역의합병, 등 ) 하기어려움 목표 : 정확성 ( 오류없음 ), 효율성 ( 최대한가용공간활용 ), 성능 ( 할당속도 ) 보장 접근방법 메모리는바이트단위가아니라 ( 예를들면 ) 워드단위로할당 새로할당되는메모리영역은하나의 블록 으로관리 블록의시작주소는정렬됨. 예 ) 8-byte alignment 블록에대한정보 ( 할당여부, 인접블록에대한포인터, 블록의크기, 등 ): 블록에포함 자유 (free) 메모리 할당되지않은영역. 자유블록 (free block) 으로구성됨 메모리할당은자유메모리로부터가능함 메모리해제시자유메모리로합병됨 Spring 2010 System Programming (Shin) 30
[ 註 例 ] 동적메모리관리개념 (1/2) malloc() 에의한메모리할당예 b1 = malloc(6*sizeof(int)); b2 = malloc(3*sizeof(int)); b3 = malloc(5*sizeof(int)); b4 = malloc(2*sizeof(int)); free(b2) 할당됨 (allocated) 할당되지않음 (free) sizeof(int) word # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 block_1: 할당됨 (6 words) block_2: 할당안됨 (4 words) block_3 (5 words) block_4 (2 words) block_5 (6 words) 블록의구조 블록의앞에워드를추가하여블록의속성을표시할수있음 블록의속성 : 할당되었는지여부, 블록의크기등 자유블록의경우, 다음자유블록의주소를링크로서포함하여, 자유블록의 linked list 로서자유메모리를구성할수있음 (free list) Spring 2010 System Programming (Shin) 31
[ 註 例 ] 동적메모리관리개념 (2/2) malloc() 에의한메모리할당방법 free list word # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 allocated free not owned by malloc 블록의구조예 (pointer to next free block while free) size allocated space bp = malloc(n*sizeof(int)); size (n+2)*sizeof(int) bp header 블록의할당방법 malloc() 에의해메모리할당요청이있으면, free list 를스캔하여적절한크기의자유블록을찾아냄 요청한크기보다크면, 남는부분은 free list 에삽입 요청한크기의자유블록이없으면, OS 에메모리할당요청 free() 에의해메모리해제요청이있으면, free list 의적절한위치에해제된블록을삽입 만일기존의자유블록과인접해있는경우는서로합병함 Spring 2010 System Programming (Shin) 32
7.4 프로세스간통신 Interprocess Communication
기본적인프로세스간통신기법 데이터스트림과파이프 data stream and pipe 정보의송수신에사용되는일련의연속된데이터 a sequence of data used to send or receive information 예 : 프로세스와파일 ( 파일디스크립터을통하여 ) 을연결해주는데이터스트림 한쪽방향으로만가능함. 파일의경우입력, 출력의양방향연산을하므로쓰기 / 읽기각각의디스크립터를사용 파이프 : 프로세스사이를연결해주는데이터스트림 한프로세스의출력을다른프로세스의입력으로연결함 [Unix/Linux] 두종류의파이프 : (unnamed) pipe, named pipe unnamed pipe: 통상 pipe 라고부름 파일시스템에존재하지않고, 커널버퍼에의해데이터저장 - 인출 named pipe: FIFO(First In First Out) 라고부름 named pipe 는파일시스템에파일로서존재. 그러나실제디스크에는저장되지않고디렉토리에이름만존재하며, 파일의내용은주기억에저장됨. 파이프메커니즘에는 file position 이없음. 즉, lseek() 사용불가. 프로세스 1 파이프 프로세스 2 Spring 2010 System Programming (Shin) 34
파이프 pipe 파이프란? 프로세스사이를연결해주는데이터스트림 한프로세스의출력을다른프로세스의입력으로연결함 연결수단은읽기와쓰기파일디스크립터에의존함 데이터는커널의버퍼 (pipe buffer) 에저장되어전달되며, 외부에서접근할수없음. 두프로세스 (producer, consumer) 는파이프버퍼를사이에두고데이터에대한동기화가이루어짐. 즉, 한프로세스가버퍼에순차적으로데이터를써넣으면, 다른프로세스가차례로읽어감. 실제, 부모 ( 조상 )- 자식 ( 자손 ), 자식 - 자식프로세스사이의통신만가능 예 : Linux commands: prompt> ls l more 기호 은파이프를나타냄. ls 와 more 는 shell 의자식프로세스 파이프의제약점 하나의작업에서한방향으로만데이터전달가능 (half duplex) 즉, 읽고쓰기를섞어서할수없고, 한번읽기연산을하면그작업이끝날때까지읽기만가능함. 쓰기를하려면, 읽기가끝난후가능. 부모 - 자손, 조상이같은자손 - 자손프로세스사이에서만가능. 즉, file descriptor inheritance 가적용되는경우에만가능 예를들면, 디몬 (daemon) 과의데이터전달이불가능함 Spring 2010 System Programming (Shin) 35
pipe 관련시스템콜 #include <unistd.h> 파이프 int pipe(int fd[2]) 프로세스자신에주어지는파일디스크립터 (file descriptor, fd) 를설정함. 파일입출력과같은방법으로스트림을읽고씀. fd[0]: 읽기용, fd[1]: 쓰기용 read/write 는일반적인파일입출력을따름. 즉, ssize_t read(int fd, void *buf, size_t count) ssize_t write(int fd, const void *buf, size_t count) 한연산에서, 둘중하나만가능 file descriptor user process pipe(fd) fd[0] = 3 fd[1] = 4 pipe buffer ( 주기억에존재 ) kernel 어떤파일을접근하기위한추상적인키 (key). 이것이지정하는자료구조에의해다음정보를제공 : 파일에의포인터, 접근권리, 읽기 / 쓰기모드, 파일포지션, 등. fork 혹은 exec 에의해생성된자식프로세스는부모의 file descriptor 를물려받음. Spring 2010 System Programming (Shin) 36
[ 註 例 ] 파이프 파이프의사용방법 ( 예 ) 1. pipe() 호출 // fd[0](read), fd[1](write) 생성 2. fork() 호출 // 부모 (P), 자식 (C) 프로세스생성 3. 데이터전달 // 한방향으로만가능 half duplex (a) P sends to C: P 는 fd[0] 를닫고, fd[1] 에쓰기. C 는 fd[1] 을닫고, fd[0] 에서읽기 (b) C sends to P: C 는 fd[0] 를닫고, fd[1] 에쓰기. P 는 fd[1] 를닫고, fd[0] 에서읽기. full duplex pipe? 파이프는한작업에서한방향으로만데이터를전달할수있으므로 (half duplex), 만일양방향으로데이터를전달하려고하면 (full duplex) 미리두개의파이프를생성 ( 즉 pipe() 호출을두번수행 ) 하여, 각파이프를각방향으로지정함 pipe quiz (case 1) the producer does a close() on the pipe s write end, then can the consumer read any data buffered in the pipe? {Y} (case 2) the consumer does a close() on the pipe s read end, then can the producer write data to the write end? {N} Spring 2010 System Programming (Shin) 37
[ 註 例 ] 파이프 예제 : echo printing 이예에서는파이프하나를양방향으로사용하되, 부모가보내기작업이끝나면자식이보내기작업을시작함. 시나리오 1. 부모프로세스는표준입력으로부터입력된문자열을파이프를이용하여자식프로세스로보냄. 2. 자식프로세스는부모프로세스가보내온문자열을복사한다음, 다시파이프를이용하여부모프로세스에반송함 (echo) 3. 부모프로세스는자식에게서보내온문자열을표준출력으로출력함 부모프로세스 parent.c 다음쪽에계속 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main() { char string_in[32], string_echo[32]; int fd[2], status; } if (pipe(fd) < 0) { exit(exit_failure); } fgets(string_in, sizeof(string_in), stdin); /* Step 1 */ if (fork() == 0) { if (execl("./child", "child", fd[0], fd[1], NULL) < 0) { exit(exit_failure); } } write(fd[1], &string_in, sizeof(string_in)); /* Step 1 */ wait(&status); read(fd[0], &string_echo, sizeof(string_echo)); /* Step 3 */ printf("echoed: %s\n", &string_echo); /* Step 3 */ close(fd[0]); close(fd[1]); return EXIT_SUCCESS; Spring 2010 System Programming (Shin) 38
#include <stdlib.h> #include <unistd.h> #include <string.h> int main(int argc, char *argv[]) { char str_in[32], str_echo[32]; int read_fd, write_fd; read_fd = atoi(argv[1]); write_fd = atoi(argv[2]); [ 註 例 ] 파이프 자식프로세스 child.c ( 실행파일 : child) /* Step 2: echo */ read(read_fd, &str_in, sizeof(str_in)); strncpy(str_echo, str_in, sizeof(str_echo)); write(write_fd, &str_echo, sizeof(str_echo)); } close(read_fd); close(write_fd); return EXIT_SUCCESS; stdin stdout 부모프로세스 parent pipe() fork() write() read() fd[0] fd[1] pipe buffer fd[0] fd[1] 자식프로세스 child echo Spring 2010 System Programming (Shin) 39
FIFOs (Named Pipes) FIFO 혹은 named pipe FIFO(First In First Out): unnamed pipe 의문제점을해결 일시적인버퍼를사용하지않고파일시스템에존재하는 persistent 객체를이용함 서로 ancestor-descendent 관계가없어도서로데이터를교환할수있음 named pipe(fifo) 의속성 named pipe 는파일시스템에파일로서존재. 그러나실제디스크에는저장되지않고디렉토리에이름만존재하며, 파일의내용은주기억에저장됨. 이파일은특수한파일로서한프로세스 (Pp, producer) 가이파일에데이터를차례로써넣으면, 다른프로세스 (Pc, consumer) 가써진순서대로읽어감. 일단읽으면, 데이터는지워짐. 즉, Pp 와 Pc 는동기화되어, Pp 는 Pc 가읽을때까지기다리고, Pc 는 Pp 가쓸때까지기다림. Spring 2010 System Programming (Shin) 40
FIFOs (Named Pipes) shell 에서의예 mkfifo [-m permission_mode] names default permission mode: 666 8 $ mkfifo m 600 fifo1 $ cat <fifo1 wc l & {background 실행 } $ cat /etc/passwd >fifo1 위의예에서 cat <fifo1 에서아직 fifo1 이비어있으므로, 데이터가입력될때까지기다림 ( 동기화 ). producer-consumer 의예 : 위의예에서 cat <fifo1 wc l & 명령을두번입력하여실행하면 첫번째는 fifo1 가다찼을때수행되므로 passwd 파일의라인수가출력됨 두번째는첫번째실행에서 fifo1 의데이터가다소비되어, 빈파일이므로 0 이출력됨 system call int mkfifo(const char *pathname, mode_t mode); fifo special file 에대한 open/read/write 등은파일에대한일반적인명령을사용함 Spring 2010 System Programming (Shin) 41
[ 註 例 ] FIFOs (Named Pipes) 시나리오 rdfifo : 읽기를위한 fifo 를생성하여오픈한후, fifo 의출력을 stdout 에보임 wrfifo : 쓰기를위해 fifo 를오픈하고쓰기연산을수행 /* wrfifo.c - Write to a "well-known" FIFO */ int main(void) { int fd; /* Descriptor for FIFO */ int len; /* Bytes written to FIFO */ char buf[pipe_buf]; /* Ensure atomic writes */ time_t tp; /* For time call */ /* rdfifo.c - Create a FIFO and read from it */ int main(void) #include <sys/types.h> printf("i am %d\n", getpid()); /* Identify myself */ { #include <sys/stat.h> /* Open the FIFO write-only */ if((fd = open("fifo1", O_WRONLY)) < 0) { int fd; /* Descriptor for FIFO */ #include <unistd.h> perror("open"); int len; /* Bytes read from FIFO */ #include <errno.h> exit(exit_failure); char buf[pipe_buf]; #include <stdio.h> } mode_t mode = 0666; #include <stdlib.h> /* Generate some data to write */ if((mkfifo("fifo1", mode)) < 0) { #include <fcntl.h> while(1) { time(&tp); /* Get the current time */ perror("mkfifo"); #include <limits.h> /* Create the string to write */ exit(exit_failure); len = sprintf(buf, "wrfifo %d sends %s", } getpid(), ctime(&tp)); /* Open the FIFO read-only */ /* Use (len + 1) because sprintf does not count if((fd = open("fifo1", O_RDONLY)) < 0) { the terminating null */ perror("open"); if((write(fd, buf, len + 1)) < 0) { exit(exit_failure); perror("write"); } close(fd); /* Read and display the FIFO's output until EOF */ exit(exit_failure); while((len = read(fd, buf, PIPE_BUF - 1)) > 0) } printf("rdfifo read: %s", buf); sleep(3); close(fd); } close(fd); exit(exit_success); Source: Kurt Wall, Linux Programming by exit(exit_success); } Example, Que, 2000, pp. 333-336 } Spring 2010 System Programming (Shin) 42
시그널 signals 시그널 (signal) 이란? 사용자 ( 단말 ) 나커널이프로세스에연락하는방법 작은메시지로서, 시스템에서어떤이벤트가발생했음을프로세스에게알려줌 예외 (exception) 나인터럽트를커널레벨에서표현한것 (kernel abstraction) signal 에의해주어지는정보는단지 signal id( 정수 ) 와도착사실임 ID Name Default Action Corresponding Event 2 SIGINT Terminate ( 종료 ) Interrupt from keyboard (ctl-c) 9 SIGKILL Terminate Kill program (cannot override or ignore) 11 SIGSEGV Terminate & Dump Segmentation violation 14 SIGALRM Terminate Timer signal 17 SIGCHLD Ignore Child stopped or terminated Spring 2010 System Programming (Shin) 43
시그널 시그널의전송 커널이시그널을목적지프로세스 (destination process) 에보낸다는것은그프로세스의문맥에서어떤상태를갱신하는것 커널은다음의이유로시그널을보냄 커널이 divide-by-zero (SIGFPE) 혹은자식프로세스의종료 (SIGCHLD) 등의시스템이벤트를검출했을때 다른프로세스가목적지프로세스에시그널을보낼것을커널에명시적으로요청하기위해 kill system call 을호출했을때 시그널의수신 목적지프로세스가시그널을수신한다는것은커널이시그널을보낸데대해반응을보이는것임 다음과같이반응함 시그널을무시 (do nothing) 프로세스를종료 signal handler 라불리는사용자레벨의함수를실행함으로서 signal 을받음 (catch) - 인터럽트핸들러와유사함 프로세스의 core dump 를작성하고이상 ( 異常 ) 종료 Spring 2010 System Programming (Shin) 44
[ 註 例 ] 시그널 키보드에서시그널전송 ctrl-c (ctrl-z) 을키보드에서입력하면 SIGTERM (SIGTSTP) 를 foreground process group 에속하는모든작업에전송. SIGTERM 디폴트액션은각프로세스를종료하는것 SIGTSTP 디폴트액션은각프로세스를중단 (suspend) 하는것 사용예 : ctrl-c 와 ctrl-z linux> traceroute www.snu.ac.kr traceroute to moose.snu.ac.kr (147.46.10.48) 30 hops max, 38 byte packets 1 147.46.114.1 (147.46.114.1) 0.421 2 147.47.20.125 (147.47.20.125) 0.561 ms 3 * * * <typed ctrl-z> [1]+ Stopped traceroute www.snu.ac.kr linux> ps -l F S UID PID PPID TIME CMD 0 S 800 12536 12535 00:00:00 bash 4 T 800 14060 12536 00:00:00 traceroute 0 R 800 14066 12536 00:00:00 ps linux> fg traceroute www.snu.ac.kr 4 * * * <typed ctrl-c> linux> ps -l F S UID PID PPID TIME CMD 0 S 800 12536 12535 00:00:00 bash 0 R 800 14095 12536 00:00:00 ps Spring 2010 System Programming (Shin) 45
그밖의프로세스간통신기법 공유기억 ( 공유메모리 ) 를이용한프로세스간통신 여러개의프로세스가공통으로이용할수있는메모리공간제공 다수의프로세서가주기억 ( 메모리 ) 을공유하는경우, 멀티프로세서 (multiprocessors) 라부름 메시지를이용한프로세스간통신 메시지란일반적으로정보의집합체로서소스에서목적지로전달됨 두개의프로세스사이에서메시지를이용하여데이터송수신 이때프로세스는서로다른컴퓨터나프로세서에위치할수있음. 네트워크로연결된분산시스템에서원격프로세스간통신에이용될수있음 원격프로시져호출 (remote procedure calling) 네트워크로연결된원격컴퓨터의프로그램을호출 ( 실행 ) 하는메커니즘 메시지를이용하여요청 / 응답을송신 클라이언트 - 서버시스템에이용가능 Spring 2010 System Programming (Shin) 46
[ 註 例 ] 그밖의프로세스간통신기법 프로세스 프로세스 공유메모리 프로세스 프로세스 공유메모리를이용한통신 발신프로세스 메시지큐에저장 message queue 메시지큐에서인출 수신프로세스 메시지큐를이용한메시지통신 ( 단일컴퓨터시스템 ) Spring 2010 System Programming (Shin) 47
7.5 프로세스환경
환경변수 환경변수란? environment variables 부모프로세스에의해자식프로세스에전달되는변수로서프로세스의실행에관련된속성이나옵션을나타냄. 예를들면, 프로세스의부모. 자식관계를통하여전달되는전역변수 (global variable) 와같은것 환경변수에의액세스는전역변수 environ(type: char **) 을이용함 시스템콜 char *getenv(const char *name): 환경변수 name 의값검색 - 인출 int putenv(char *string): 환경변수의값을설정. name=value 의형식으로입력 Spring 2010 System Programming (Shin) 49
environ 의구조 [ 註 例 ] 환경변수 environ HOME EDITOR HOME=/home/system2 EDITOR=vi PAGER=less TERM=vt100 PWD=/usr/ LOGNAME=test NULL A user s login directory The user s preferred utility to edit text files 프로세스의모든 환경변수를출력 #include <stdio.h> #include <stdlib.h> extern char **environ; PAGER The user's preferred utility to display text files TERM The terminal type for which output is to be prepared PWD The current working directory LOGNAME The name of the logged-in user SHELL The file name of the user's login shell BROWSER The user's preferred utility to browse URLs int main(int argc, char *argv[]) { char **p; } for (p = environ; *p; p++) { printf("%s\n", *p); } exit(0); Spring 2010 System Programming (Shin) 50
시간관리 시간의표현 Unix time, Posix time Unix epoch(1970 년 1 월 1 일오전 0 시 ) 기준으로경과된초 ( 秒 ) 수산정 UTC (Coordinated universal time) 세계표준시간 local time, GMT time, etc. 시스템콜 #include <time.h> time_t time(time_t *tptr): Unix epoch 로부터현재까지의경과시간 ( 초 ) int gettimeofday(struct timeval *tv, struct timezone *tz): Unix epoch 로부터현재까지의경과시간 ( 초, 밀리초 ) struct tm *localtime(const time_t *timep): local time struct tm *gmttime(const time_t *timep): GMT time time_t mktime(struct tm *tm): local time UTC char *asctime(const struct tm *tm): calendar date/time char *ctime(const time_t *timep): calendar date/time, UTC Spring 2010 System Programming (Shin) 51
[ 註 例 ] 시간관리 커널 time() gettimeofday() struct timeval Unix epoch 로부터의경과시간, UTC struct tm { int tm_sec; /* seconds */ int tm_min; /* minutes */ int tm_hour; /* hours */ int tm_mday; /* day of the month */ int tm_mon; /* month */ int tm_year; /* year */ int tm_wday; /* day of the week */ int tm_yday; /* day in the year */ int tm_isdst; /* daylight saving time */ }; /* specified in <time.h> */ int time_t Unix epoch 로부터의경과초수, UTC ctime() 문자열 gmttime() localtime() mktime() struct tm 시간을 calendar time 으로유지, time zone (TZ) asctime() 문자열 struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; /* specified in <sys/time.h> */ type time_t : represents calendar time When interpreted as an absolute time value it represents the number of seconds elapsed since Unix epoch. (specified in <sys/types.h>) Spring 2010 System Programming (Shin) 52
로그인과인증 로그인과정 1. init 프로세스가단말의수만큼 getty 명령을수행 (/etc/inittab 의설정에의거 ) getty 은 tty 포트를오픈하고 login 명령을수행하여로그인프롬프트를표시 2. getty 명령은단말로부터사용자의이름이입력되기를대기 3. login 명령이사용자를인증 4. 쉘 (shell) 을시작 init 단말수 fork 단말 file descriptor 0, 1, 2 getty login (shell) exec exec Spring 2010 System Programming (Shin) 53
[ 註 例 ] 로그인과인증 Shell programs shell 이란응용프로그램으로서사용자를대신해서프로그램을수행시킴 sh original Unix Bourne Shell csh BSD Unix C Shell tcsh Enhanced C Shell bash Bourne-Again Shell shell program 의수행은순차적으로 read/evaluate 스텝을계속하는것임 만일 shell 에서 foreground job 만을 reap 한다면 (cntl-c 등에의해 ), background job 을 reap 하기위해 signal 이란메커니즘이요구됨 (kill 명령에의해 ) int main() { char cmdline[maxline]; while (1) { /* read */ printf("> "); fgets(cmdline, MAXLINE, stdin); if (feof(stdin)) exit(0); } } /* evaluate */ eval(cmdline); Spring 2010 System Programming (Shin) 54
7.6 프로세스스케줄링
프로세스스케줄링 프로세스의상태 (states) 프로세스가 ready 상태나 waiting 상태일때는주어진큐에서다음상태가될때까지기다림 시작 (initiated) 준비완료 (ready) 디스팻치 입출력완료 타이머인터럽트 ( 일시중지 ) 대기중 (waiting) 입출력요구 ( 일시중지 ) 실행중 (running) 종료 (terminated) Spring 2010 System Programming (Shin) 56
프로세스스케줄링 스케쥴링 scheduling 여러개의프로세스가실행되려고할때순번을정함 ( 아래의디스팻칭개념을포함할수도있음 ) 디스팻칭 dispatching 실행되도록스케쥴된프로세스를 CPU 상에서실행시킴 스케줄링의목표 컴퓨터시스템의계산목표에따름. 예를들면, 실시간시스템 : 응답시간 (response time) 뱃치 (batch) 처리시스템 : 처리율혹은처리량 (throughput) 시분할시스템 (timesharing system): 프로세서의효율성, 응답시간 스케줄러의설계목표 프로세스간 CPU 시간을공평하게공유 프로세서를효율적으로이용 스케줄링오버헤드가낮음 프로세스의중요도나긴급도에따른우선순위의설정 어떤프로세스도영원히스케줄링에서제외되면안됨 (no starvation) Spring 2010 System Programming (Shin) 57
[ 註 例 ] 프로세스스케줄링 새프로세스 Ready queue CPU 계산을종료한프로세스 일시중지 (timer interrupt, I/O request) I/O I/O queue I/O request Time slice expired Child terminates Child executes Fork a child 프로세스스케쥴링 Interrupt occurs Wait for an interrupt CPU 에서수행중이러한일이발생하면프로세스는해당일을마친후다시 ready queue 에들어간다. Spring 2010 System Programming (Shin) 58
스케줄링할때고려사항 우선순위 priorities 프로세스의계산작업의긴급성혹은중요성등에의해결정 우선순위가높은것을우선적으로 CPU 에스케줄링 통상각각의우선순위마다별개의스케줄큐를만듦 예 ) 휴대전화운영체제의스케줄러에서 전화통화, 문자서비스 (SMS), 일정관리 순으로우선순위를설정 다음에수행될프로세스의선정방법 예 : 다음의값이가장높은프로세스를선정 : p = f(w, e, s), 단, 스케줄링시스템에도착한후경과시간 (w) 현재까지 CPU 에서수행된시간 (e) 해당프로세스가요구하는총실행시간 (s) 스케줄시점에서의프로세스교체여부의결정방법 선점 (preemptive): 우선순위높은프로세스가낮은프로세스를교체함 비선점 (nonpreemptive): 우선순위가높더라도현재 CPU 에서수행중인 [ 낮은우선순위의 ] 프로세스가종료될때까지기다림 Spring 2010 System Programming (Shin) 59
Priority queuing [ 註 例 ] 스케줄링할때고려사항 Source: W. Stallings, Operating Systems, 6e, 2008, Chapter 9 Spring 2010 System Programming (Shin) 60
문맥교환 프로세스의교체 ( 문맥교환 ) process switching, context switching 문맥교환에의해제어권이한프로세스에서다른프로세스로넘어감 발생시기 round robin 스케줄링에서시간슬라이스종료시 입출력요청시 인터럽트처리시 조치 현재실행중인프로세스의실행환경 ( 상태 ) 을저장 프로세스상태 (process state) memory image + register values + program counter 다음실행할프로세스를선정한후디스팻칭 Spring 2010 System Programming (Shin) 61
[ 註 例 ] 문맥교환 Process A code Process B code 시간 user code (A) kernel code user code (B) kernel code user code (A) context switch context switch Spring 2010 System Programming (Shin) 62
스케줄링정책 스케쥴링정책 ( 예 ) scheduling policies shortest job first, shortest process next 가장실행시간이짧은작업을우선실행 실행시간을미리결정가능하거나예측가능하여야함 round robin 모든프로세스가일정시간만큼의실행시간 (time slice) 을배당받음. 준비상태인프로세스가차례로실행됨 할당된시간종료시선점 (preemption) 됨 프로세스의교체 시분할시스템에적합 FCFS 스케줄큐에도착한순서대로스케줄링 오버헤드가가장낮음 수행시간이짧은프로세스에불리함 프로세스스케줄링예제 프로세스 도착시간 총실행시간 A 0 3 B 2 6 C 4 4 D 6 5 E 8 2 Spring 2010 System Programming (Shin) 63
[ 註 例 ] 스케줄링정책 Source: W. Stallings, Operating Systems, 6e, 2008, Chapter 9 Spring 2010 System Programming (Shin) 64