12 장고급응용 0
수행중인프로그램 프로세스 모든프로세스는유일한프로세스식별번호 (PID) 를가짐 유닉스에서는 ps 명령을사용하여프로세스목록을볼수있음 12-1
프로세스 $ ps -aux USER PID %CPU %MEM SZ RSS TT STAT START TIME COMMAND blufox 17725 34.0 1.6 146 105 i2 R 15:13 0:00 ps-aux amber 17662 1.4 7.0 636 469 j5 S 15:10 0:08 vi it.c root 143 0.5 0.1 5 3? S Jul 31 10:17 find... 12-2
fork() 새로운프로세스를생성하는함수 부모프로세스 / 자식프로세스 fork() 가호출될때, 자식프로세스라는새로운프로세스가생성 새로운프로세스는자신의프로세스식별번호를갖는다는것만제외하고는호출한프로세스를그대로복사한것임 fork() 는자식프로세스에게 0 을리턴하고, 부모프로세스에게자식프로세스 ID 를리턴함 변수들은공유하지않지만, 사용되고있던파일포인터는공유함 12-3
fork() 12-4
프로그램 #include <stdio.h> int main(void){ } 출력 int fork() 예제프로그램 fork(void), value; value = fork(); /* new process */ printf("in main: value = %d\n", value); return 0; In main: value = 17219 부모프로세스의출력 In main: value = 0 자식프로세스의출력 12-5
wait() 자식프로세스가수행될동안프로세스를멈추게함 자식프로세스가끝나면부모프로세스가수행시작 함수원형 pid_t wait(int *statloc) - 리턴값 : process ID 또는 -1 - statloc : 널이아니면끝나는프로세스의상태를저장할곳을나타냄 ( 상위비트에저장 ) 12-6
예제프로그램 main() { int pid, status, exit_status; if ((pid = fork()) < 0 exit(1); if (pid == 0) { sleep(4); exit(5); } if (wait(&status) < 0) exit(1); if ((status & 0xFF)!= 0) printf("error occurred"); else { exit_status = status >> 8; exit_status &= 0xFF; printf("exit status from %d was %d", pid, exit_status); } } 결과 Exit status from 14963 was 5 12-7
Exec () 부류 #include <unistd.h> int execl(const char *path, const char *arg0,..., const char *argn, (char *) 0); int execv(const char *path, char *const argv[]); int execle(const char *path, char *const arg0[],..., const char *argn, (char *) 0, char *const envp[]); int execve(const char *path, char *const argv[], char *const envp[]); int execlp(const char *file, const char *arg0,..., const char *argn, (char *) 0); int execvp(const char *file, char *const argv[]); 12-8
Exec () 부류 arg0 : 프로그램이름, arg1~argn : 인자목록 리턴값 : 없음또는 -1 ( 오류발생시 ) 이것을호출한프로세스는새로운프로그램으로대치됨 새로운프로그램은 main() 함수부터수행됨 프로세스 ID 는변하지않음 12-9
예제프로그램 main() { char *av[3]; av[0] = "ls ; av[1] = "-l"; av[2] = (char *)0; printf("executing ls"); execv("/bin/ls", av); printf("execl error"); } < amin:os 17 > ls -l -rwxrwxr-x 1 kmh sslab 5260 10월 2일 15:31 a.out* -rw-rw-r-- 1 kmh sslab 117 10월 2일 15:31 exec.c < amin:os 18 > a.out executing ls -rwxrwxr-x 1 kmh sslab 5260 10월 2일 15:31 a.out* -rw-rw-r-- 1 kmh sslab 117 10월 2일 15:31 exec.c 12-10
Exec () 호출 12-11
fork() 와 Exec () 보통이두함수는같이수행되어새로운프로그램을자식프로세스로만듦 main() { int pid; pid = fork(); if (pid > 0) { wait((int *)0); printf("ls completed"); exit(0); } if (pid == 0) { execl("/bin/ls", "ls", "-l", (char *)0); printf("execl error"); } printf("fork error"); exit(1); } 12-12
Fork and Exec Call 12-13
파이프 FIFO 이고데이터를동기화해서보냄 $ cat temp.c lp $ cat [a-za-z]* grep process 이름있는파이프와이름없는파이프가있음 이름있는파이프 - 초기화 : open() 시스템호출 - 접근권한 : 파일허가와유사하게결정됨 이름없는파이프 - 초기화 : pipe() 시스템호출 - 접근권한 : 관계가있는프로세스로국한됨 ( 자식 / 부모프로세스 ) read, write, close 시스템호출을사용할수있음 12-14
이름없는파이프 - pipe() 함수원형 #include <unistd.h> int pipe(int filedes[2]); 프로세스간채널을생성함 리턴값 : 0, -1 filedes 인자를통해두개의파일지시자가리턴됨 - filedes[0] : 읽기를위해열림 - filedes[1] : 쓰기를위해열림 12-15
예제프로그램 #include <stdio.h> #define MAX 16 char *msg1 = "hello, world #1"; char *msg2 = "hello, world #2"; char *msg3 = "hello, world #3"; main() { char inbuf[max]; int fd[2], j; if (pipe(fd) < 0) exit(1); write(fd[1], msg1, MAX); write(fd[1], msg2, MAX); write(fd[1], msg3, MAX); for (j = 0; j < 3; j++) { read(fd[0], inbuf, MAX); printf("%s\n", inbuf); } exit(0); } 12-16
예제프로그램결과 enterprise:[~/os ]56 a.out hello, world #1 hello, world #2 hello, world #3 enterprise:[~/os ]57 12-17
예제프로그램 2 #include <stdio.h> #define MAX 16 char *msg1 ="hello, world #1"; char *msg2 ="hello, world #2"; char *msg3 ="hello, world #3"; main() { char inbuf[max]; int fd[2], j, pid; if (pipe(fd) < 0) exit(1); if ((pid = fork()) < 0) exit(2); if (pid > 0){ write(fd[1], msg1, MAX); write(fd[1], msg2, MAX); write(fd[1], msg3, MAX); wait((int *)0); } if (pid == 0) { for (j = 0; j < 3; j++) { read(fd[0], inbuf, MAX); printf("%s\n", inbuf); } } } 12-18
예제프로그램 2 결과 enterprise:[~/os ]56 a.out hello, world #1 hello, world #2 hello, world #3 enterprise:[~/os ]57 12-19
신호 예외적인조건또는비정상적인사건에의해생성 - 인터럽트 (ctrl-c) : 커널이 SIGINT 이라는신호를프로세스에게보냄 - 잘못된연산 : 커널이 SIGKILL 이라는신호를프로세스에게보냄 $ test Illegal instruction - core dumped - kill 명령 : 커널이신호를프로세스에게보냄 $ test& 1024 $ kill -9 1024 1024 terminated 12-20
signal() 함수 프로세스가신호를받으면디폴트로그프로세스는종료함 signal() 함수를사용하여신호를받았을때의동작을지정할수있음 12-21
함수원형 signal() 함수 #include <signal.h> void (*signal(int sig, void (*func)(int)))(int); - sig 신호가발생하면 func() 함수를수행하게함 - 리턴값 : 함수포인터 자주사용되는매크로 #define SIG_DFL ((void (*)(int)) 0) #define SIG_IGN ((void (*)(int)) 1) 12-22
예제프로그램 /* Using a signal handler to catch a control-c. */ #include <stdio.h> #include <signal.h> #include <stdlib.h> #define MAXSTRING 100 void cntrl_c_handler(int sig); int fib(int n); int main(void){ int i; signal(sigint, cntrl_c_handler); for (i = 0; i < 46; ++i) printf("fib(%2d) = %d\n", i, fib(i)); return 0; } 12-23
예제프로그램 void cntrl_c_handler(int sig){ char answer[maxstring]; printf("\n\n%s%d\n\n%s", "Interrupt received! Signal = ", sig, "Do you wish to continue or quit? "); scanf("%s", answer); if (*answer == 'c') signal(sigint, cntrl_c_handler); else exit(1); } 12-24
상태리턴 main() 함수의리턴값은운영체제가사용함 main() 함수가리턴한값은쉘에서 status 라는쉘변수를보면알수있음 예 main() {... exit(5) ; // return 5; } $ a.out $ echo $status 5 $ 12-25