Presentation is loading. Please wait.

Presentation is loading. Please wait.

7. UNIX PROCESS CONTROL ▷ fork : 새로운 프로세스 생성 ▷ exit : 프로세스 실행을 종료

Similar presentations


Presentation on theme: "7. UNIX PROCESS CONTROL ▷ fork : 새로운 프로세스 생성 ▷ exit : 프로세스 실행을 종료"— Presentation transcript:

1 7. UNIX PROCESS CONTROL ▷ fork : 새로운 프로세스 생성 ▷ exit : 프로세스 실행을 종료
○ 프로세스 문맥(context)을 제어하는 시스템 호출의 사용 및 구현 방식 소개 ▷ fork : 새로운 프로세스 생성 ▷ exit : 프로세스 실행을 종료 ▷ wait : parent 프로세스의 실행을 child 프로세스의 exit와 동기화 ▷ brk : 프로세스에게 메모리를 동적으로 할당 ▷ signal : 프로세스들에게 asynchronous하게 이벤트 발생을 알림 ▷ exec : - 그림 7.1 참조

2 Objectives The Use and Implementation of System Calls
Process Creation Process Execution process Signaling Process Termination System Booting Progress System Boot and Init Process Process System Calls

3 7.1 프로세스 생성 Fork system call
pid = fork(); Only a process can create another process. Process 0 is only process not created via fork. Only one way to create process The swappable image of the created process (child) is almost an exact copy for the following: The child process has unique process ID The child process has a different parent ID The child process share a common file pointer with the correspond file descriptor of the parent The child process’s utime, stime, cutime, cstime are set to 0

4 Fork return 0 to the child process
Fork returns the PID of child process to the parent Implementation of the ‘fork’ system call Check to see if enough swap memory is available Allocate process table entry and create copy of the swappable image of the parent (newproc()) Initialize accounting parameters in the user structure of the child Return zero : process ID of child to parent ▷ fork의 동작 순서 1. 새로운 프로세스를 위해 프로세스 테이블에 슬롯을 할당 2. 자식 프로세스에게 유일한 프로세스 ID no.를 할당 3. 부모 프로세스의 context 복사 4. 파일 및 inode 테이블의 카운트 증가 5. 부모 프로세스는 자식 프로세스의 ID no.를 리턴, 자식 프로세스에게는 0을 리턴

5 알고리즘 fork 입력: 없음, 출력: 부모 프로세스에게는 자식의 pid 번호, 자식 프로세스에게는 0 { 커널 자원이 이용 가능한지 검사한다; 빈 프로세스 테이블항과 고유한 pid 번호를 얻는다; 사용자가 너무 많은 프로세스를 실행중이 아닌 지 검사한다; 자식의 상태를 “생성중”으로 기록한다; 부모 프로세스 테이블 항으로부터 데이터를 새로운 자식의 항에 복사한다; 현행 디렉토리 inode와 변경된 루트(해당될 경우)의 계수를 증가시킨다; 부모 문맥(u area, 텍스트, 데이터, 스택)을 메모리에 복사한다; 자식 시스템 수준 문맥에 모조의 시스템 수준 문맥 층을 push; 모조 문맥은 자식 프로세스가 자신을 인식하도록 하는 데이터를 포함하며 스케쥴되었을 때 여기서부터 실행을 시작한다; if (실행 프로세스가 부모 프로세스이면) { 자식의 상태를 “수행 대기”로 바꾼다; return(자식 id); /* 시스템에서 사용자로 복귀 */ } else /* 실행 프로세스가 자식 프로세스이다 */ { u area의 시간 필드를 초기화한다; return(0); /* 사용자로 복귀 */ 그림 7.2 fork 알고리즘

6 #include <stdio.h>
#define MSGSIZE 16 char *msg1 = “hello, world #1”; char *msg2 = “hello, world #2”; char *msg3 = “hello, world #3”; main() { char inbuf[MSGSIZE]; int p[2], j, pid; if (pipe(p) < 0 ) { printf(“pipe call error\n”); exit(1); } if ((pid = fork() ) < 0 ) { printf(“fork error\n”); exit(1); if (pid > 0 ) { close(p[0]); write(p[1], msg1, MSGSIZE); write(p[1], msg2, MSGSIZE); write(p[1], msg3, MSGSIZE); wait((int *) 0 );

7 read(p[0], inbuf, MSGSIZE); printf(“%s\n”, inbuf); }
if (pid ==0 ) { close(p[1]); for ( j = 0; j < 3 ; j ++ ) read(p[0], inbuf, MSGSIZE); printf(“%s\n”, inbuf); } exit(0); * Pipe를 이용한 fork 사용 example

8 #include <fcntl.h>
main() { int fd; /* file descriptor */ int pid; /* process id */ char buf[10]; /* buffer to hold file data */ if ( (fd = open(“sample.txt”, O_RDONLY) ) < 0 ) { printf(“file open error\n”); exit(1); } read(fd, buf, 10); printpos(“Before fork”, fd); if ( (pid = fork() ) < 0 ) { printf(“fork error\n”); exit(1) ; } else if ( pid == 0 ) /* child */ { printpos(“Child before read”, fd); printpos(“Child after read”, fd);

9 else /* parent */ { wait( (int *)0 ); printpos(“Parent after wait”, fd); } printpos(string, filedes) char *string; int filedes; { long pos, lseek(); if ( (pos = lseek(filedes, 0L, 1)) < 0L) printf(“error: lseek failed\n”); printf(“%s: %ld\n”, string, pos);

10 7.2 Signals How/When a signal is posted Posted Signals
kill() system call int kill(pid, sig) int pid, sig; Interrupt or quit from terminal Death of child System call error Trap Posted Signals Recorded in p_sig of receiving process nth rightmost bit corresponds to signal n Not queued

11 Signal Processing : 2 phase
Posting Phase : Post signal for process Posting by kernel routine psignal() Receive Phase : Signal processing #include (signal.h) oldfunc = signal (signum, function); int sig; int (*func)(); return value 0: 커널에서 exit 수행, core image 덤프 1: 앞으로의 신호를 무시 - 그림 7.7 신호인식 알고리즘

12 Performs action related to signal when:
Process is switched in After system call or user mode trap Default : Terminate process Ignore : Ignore signal Catch : Use u_signal [i] as the address of a function to be executed

13 알고리즘 psig /* 신호의 존재를 인식후 신호를 처리 */ 입력 : 없음 출력 : 없음
{ 프로세스 테이블 항에 설정된 신호 번호를 얻음; 프로세스 테이블 항의 신호 번호를 지움; if(사용자가 이 신호를 무시하도록 signal 시스템 호출을 호출했다면) return; /* 완료 */ if(신호를 처리하도록 사용자가 함수를 지정했음) { u area에 저장된 신호 캐쳐(처리함수)의 사용자 가상주소를 얻음; /* 다음 문장은 바람직하지 않은 부수 효과(side effect)를 갖는다. */ 신호 캐쳐의 주소를 지정한 u area의 항을 지운다; 사용자 수준 문맥을 수정; 신호 캐쳐 함수에 대한 호출을 흉내내도록 사용자 스택 프레임을 인공적으로 생성; return; } if(시스템이 프로세스의 코어 이미지를 덤프해야 하는 신호 타입이면) { 현행 디렉토리에 “core”란 이름의 파일을 생성; 사용자 수준 문맥의 내용을 “core”에 기록; } exit 알고리즘을 즉시 호출; } 그림 7.8 신호를 처리하는 알고리즘

14 #include <signal.h> sigcatcher( )
{ printf(“PID %d caught one\n”, getpid( ) ); /*프로세스 id를 인쇄 */ signal(SIGINT, sigcatcher); } main( ) { int ppid; signal(SIGINT, sigcatcher); if(fork( ) == 0 ) { /* 두 프로세스가 준비하는 데 충분한 시간을 준다. */ sleep(5); /* 5초간 지연시키는 라이브러리 함수 */ ppid=getppid( ); /* 부모의 id를 얻음 */ for( ; ; ) if(kill(ppid, SIGINT) == -1) exit( ); } /* 우선순위가 낮을수록 경쟁을 보일 가능성이 증대 */ nice(10); for( ; ; ) ; } 그림 7.12 신호를 포착할때 경쟁 상황을 보이는 프로그램

15 #include <signal.h> main() { register int i; setpgrp();
for(i=0; i<10; i++) { if(fork()==0) { /* 자식 프로세스 */ if(i & 1) printf(“pid=%d pgrp=%d\n”, getpid(), getpgrp()); pause(); /* 실행을 중단하는 시스템 호출 */ } } kill(0, SIGINT); } 그림 7.13 setpgrp의 사용 예

16 Process Termination How/When is a process terminted?
Normal – ‘exit’ sytem exit(status) int status; abnormal – Receipt of a signal with an action that is set to default Termination Processing : 2 Phase Process turns itself into a zombie process(exit()) Parent or stepparent lays the zombie to rest(wait())

17 알고리즘 exit 입력 : 부모 프로세스를 위한 복귀 코드 출력 : 없음 { 모든 신호를 무시하라; if(연관된 제어 터미널을 갖는 프로세스 그룹 리더) 프로세스 그룹의 모든 구성원에게 hangup 신호를 보냄; 모든 구성원의 프로세스 그룹을 0으로 재설정; } 모든 열린 파일을 닫음(알고리즘 close의 내부 버전); 현행 디렉토리를 반납(알고리즘 iput); 현행 (변경된) 루트가 존재하면 그것을 반납(알고리즘 iput); 프로세스와 연관된 영역과 메모리를 반납(알고리즘 freereg); 계정(accounting) 레코드를 기록; 그림 7.14 Exit 알고리즘

18 모든 자식 프로세스의 부모 프로세스 ID를 init 프로세스(1)로 지정;
프로세스를 시체(zombie) 상태로 만듬; 모든 자식 프로세스의 부모 프로세스 ID를 init 프로세스(1)로 지정; 만약 어떤 자식 프로세스가 시체 상태(zombie)이면 자식의 죽음(death of child) 신호를 init에 보냄; 부모 프로세스에 자식의 죽음(death of child) 신호를 보냄; 문맥 교환; } 그림 7.14 Exit 알고리즘

19 Implementation Set action of all signals to ignore Close open files
Deallocate swappable image p_stat = SZOMB Notify parent(send SIGCLD signal) Disown children(ppids =1) Notify process 1 if zombie children Switch to a new process

20 main() { int child; if ((child=fork())==0) { printf(“child PID %d\n”, getpid()); pause(); } /* 부모 */ printf(“child PID %d\n”, child); exit(child); 그림 7.15 Exit 사용의 예

21 Awaiting Process Termination
wait system call pid = wait(stat_loc); int *stat_loc; Synchronized with ‘exit’ system call Parent decides whether child process stopped or termintated from 16 bits of information called ‘status’ If child process stopped, the high order 8 bits is 0 and low 8 bits is 0177 If child process terminated due to exit call, the high order byte contain the low byte of data passed via exit call and the low order 8 bits is 0 If child byte is 0 and low order byte contain the number of signal which cause the termination

22 알고리즘 wait 입력 : 종료하는 프로세스의 상태를 저장할 변수의 주소 출력 : 자식 ID, 자식의 exit 코드 { if(기다리는 (waiting) 프로세스가 자식 프로세스를 갖고 있지 않음) return(error); for( ; ; ) /* 내부 루프로부터 복귀할 때까지 루프 수행 */ if(기다리는 프로세스가 시체(zombie) 상태의 자식 프로세스를 가짐) 임의의 시체 상태인 자식 프로세스를 선택; 부모 프로세스에 자식 프로세스의 CPU 사용량을 더함; 자식의 프로세스 테이블 항을 자유롭게 함; return(자식 ID, 자식의 exit코드); } if(프로세스가 자식 프로세스를 갖지 않음) 인터럽트 가능 우선순위로 수면(sleep)(사건:자식 프로세스가 exit); 그림 7.16 Wait 알고리즘

23 #include <signal.h>
main(argc, argv) int argc; char *argv[ ]; { int i, ret_val, ret_code; if(argc > 1) signal(SIGCLD, SIG_IGN); /* 자식들의 죽음을 무시하라 */ for(i=0; i<15; i++) if(fork()==0) /* 자식 프로세스 */ printf(“child proc %x\n”, getpid( )); exit(i); } ret_val=wait(&ret_code); printf(“wait ret_val %x ret_code %x\”, ret_val, ret_code); 그림 7.17 Wait와 자식의 죽음 신호를 무시하는 예

24 #include <signal.h>
main(argc, argv) { char buf[256]; if (argc!=1) signal(SIGCLD,SIG_IGN); while(read(0, buf, 256)) if (fork()==0) { /* 자식 프로세스가 이 부분에서 buf를 가지고 작업을 수행 */ exit(0); } 그림 7.18 자식의 죽음 신호의 이유를 설명하는 예

25 Program Execution Exec system call
int execl(path, arg0, arg1……., argn, 0) char *path, *arg0….. ; int execv(path, argv) char *path, *argv[]; int execle(path, arg0, …. , argn, 0, envp) char *path, *arg0, …. , *argn, *envp[]); int execve(path, argv, envp) char *path, *argv[], *envp[]; Overlay text and data area with that of the new program

26 Implementation of the ‘exec’ system call
Translation name into inode pointer Check execute permission for the executable file Check the size of the program Collect and check the size of the argument list, storing it on swap device Set up memory for execute file Read text segment handling case of shared text Read data segment Copy argument list back into stack segment and deallocate swap space for argument Set all caught signal actions to deault Reset register

27 알고리즘 exec 입력 : (1)화일이름 (2)인수 리스트 (3) 환경(environment)변수 리스트 출력 : 없음 { inode를 얻는다(알고리즘 namei); 파일이 수행 가능 화일인지 사용자가 수행 허가(permission)를 가졌는지 확인; 파일 헤더(header)를 일고 그것이 로드 모듈인지 검사; 구 주소 공간으로부터 시스템 공간으로 exec의 인수를 복사; for(프로세스에 부착된 모든영역) 모든 구 영역을 분리함(알고리즘(detach); } for(로드 모듈에 지정된 각 영역) 새로운 영역을 할당(알고리즘 allocreg);

28 적절하다면 영역을 메모리로 적재(알고리즘 loadreg); } 새로운 사용자 스택 영역으로 exec의 인수를 복사;
그영역을 부착함(알고리즘 attachreg); 적절하다면 영역을 메모리로 적재(알고리즘 loadreg); } 새로운 사용자 스택 영역으로 exec의 인수를 복사; setuid 프로그램과 트레이싱(tracing)을 위한 특별한 처리; 사용자 모드로 복귀를 위해 사용자 레지스터 저장 지역(area)을 초기화; 파일의 inode를 방출함(알고리즘 iput); 그림 7.19 Exec 알고리즘

29 #include <signal.h>
main() { int i, *p; extern f(), sigcatch(); ip=(int *)f; for(i=0; i<20;; i++) signal(i, sigcatch); *ip=1; printf(“after assign to ip\n”); f(); } f() { } sigcatch(n) int n; { printf(“caught sig %d\n”, n); exit(1); } 그림 7.22 자신의 텍스트에 겹쳐쓰려는 프로그램 예

30 알고리즘 xalloc /* 텍스트 영역을 할당하고 초기화한다*/
입력 : 실행가능 파일의 inode 출력 : 없음 { if(실행가능 파일이 분리된 텍스트 영역을 가지지 않는다) return; if(활성 영역 리스트의 inode와 실행가능 파일의 inode가 일치) /* 텍스트 영역이 이미 존재 … 그곳에 부착한다 */ 영역을 잠근다(lock); while(영역의 내용이 아직 준비 완료되지 않음) /* 참조계수의 조작은 영역의 완전 제거를 막아준다 */ 영역의 참조계수를 증가; 영역을 풀어준다(unlock); sleep(영역 내용이 준비 완료되는 시간); 영역 참조계수를 감소; }

31 영역을 프로세스에 부착(알고리즘 attachreg);
영역을 풀어준다(unlock); return; } /* 그러한 텍스트 영역이 존재하지 않는다… 한 영역을 생성 */ 텍스트 영역을 할당(알고리즘 allocreg); /* 영역이 잠겨(lock)짐 */ if(inode 모드의 sticky 비트가 세트되어 있다) 영역 sticky 플래그를 켠다; 영역을 inode 파일 헤더에 있는 가상주소에 부착(알고리즘 attachreg); if(파일이 페이징 시스템용으로 특별히 포멧됨) /* 이 경우는 9장에서 다룬다 */ else /* 페이징 시스템이 아닌 경우 */ 파일 텍스트를 영역으로 읽어들인다(알고리즘 loadreg); 프로세스당(per process)영역테이블의 영역 보호를 읽기 전용으로 바꾼다; 영역을 풀어준다 (unlock); 그림 7.23 텍스트 영역 할당을 위한 알고리즘

32 #include <fcntl.h>
main() { int uid, euid, fdmjb, fdmaury; uid = getuid(); /* 실제(real) UID를 얻음 */ euid = geteuid(); /* 유효(effective) UID를 얻음 */ printf(“uid %d euid %d\n”, uid, euid); fdmjb = open(“mjb”, O_RDONLY); fdmaury = open(“maury”, O_RDONLY); printf(“fdmjb” %d fdmaury %d\n”, fdmjb, fdmaury); setuid(uid); printf(“after setuid(%d):uid %d euid %d\n”, uid, getuid(), geteuid()); fdmjb = open((“mjb”, O_RDONLY); setuid(euid); printf(“after setuid(%d):uid %d euid %d\n”, euid, getuid(), geteuid()); } 그림 Setuid 프로그램의 수행 예

33 알고리즘brk 입력:새로운 break 값 출력:구 break 값 { 프로세스의 데이터 영역을 잠근다(lock); if(영역의 크기가 증가) if(영역의 새 크기가 불법) 데이터 영역을 풀어준다(unlolck); return(에러); } 영역의 크기를 변경(알고리즘 growreg); 새 데이터 공간의 내용을 0 으로 만듬; 프로세스의 데이터 영역을 풀어준다(unlock); 그림 7.26 brk 알고리즘

34 #include<signal.h>
char *cp; int callno; main() { char *sbrk(); extern catcher(); signal(SIGSEGV,catcher); cp=sbrk(0); printf(“original brk value %u\n”,cp); for(;;) *cp++=1; } catcher(signo) int signo callno++; printf(“caught sig %d %dth call at addr %u\n”,signo,callno,cp); sbrk (256); 그림 7.27 brk 사용 및 출력 예

35 Original brk value Caught sig 11 1th call at addr Caught sig 11 2th call at addr Caught sig 11 3th call at addr …(10번째호출까지 동일한 주소가 프린트됨) Caught sig 11 10th call at addr Caught sig 11 11th call at addr …(18번째 호출까지 동일한 주소가 프린트됨) Caught sig 11 18h call at addr Caught sig 11 19th call at addr :

36 /* ”파일의 끝(end of file)” 까지 명령행을 읽어라 */
While(read(stdin,buffer,numchars)) { /*명령행을 파스(parse)한다 */ if(/* 명령행이 & 을 포함 */) amper=1; else amper=0; /*쉘 명령언어의 일부가 아닌 명령일 경우 */ if(fork()==0) /*입출력의 재지정? */ if(/*출력을 재지정 */) fd=create(newfile,fmask); close(stdout); dup(fd); close(fd); } if(/* 파이핑 */) pipe(fiees);

37 execlp(command1,command1,0); } /* 명령행의 두번째 구성 요소*/
if(fork()==0) { /*명령 행의 첫 번째 구성 요소 */ close(stdout); dup(fildes[1]); close(fildes[1]); close(fildes[0]); /*표준 출력은 이제 파이프로 가게 됨*/ execlp(command1,command1,0); } /* 명령행의 두번째 구성 요소*/ close(stdin); dup(fildes[0]); close(fildes[0]); close(fildes[1]); /*표준 입력이 이제 파이프로부터 오게 됨 */ execve(command2,command2,0); /* 부모가 이 위치에서 계속됨… *필요한 경우 자식이 exit하기를 기다림 */ If(amper==0) retid = wait(&status); 그림 7.28 shell으 메인 루프(계속)

38 System Boot and Init Process
Bootstrap Procedure Load a bootstrap program from system’s microcode Read the boot block (block 0) of disk Loads it into memory and execute Its executions loads the kernel from root file system Transfers control to the start address of the kerner The kernel starts running (start function)

39 Process 0 (Primary Swapper)
Responsible for scheduling memory It is a process (not a program) Always in kernel mode. Executes sched() Highest priority (PSWP = 0) Cannot be swapped out When active, it will swap in as many runnable process as it can Process 1 (Init Process) Child of process 0 Ancestor of all other process Stepparent of all orphan process Wait for its (step) children to termniate

40 Process 2 (Auxiliary Swapper)
Activate when a process needs to be swapped out: Memory expansion Process creation Process not program Execute from xsched() Only swaps out Potentially activates the primary swapper Init Process (Process 1)


Download ppt "7. UNIX PROCESS CONTROL ▷ fork : 새로운 프로세스 생성 ▷ exit : 프로세스 실행을 종료"

Similar presentations


Ads by Google