61
12 12 장 장장장장 장 장장장장 1

12 장 프로세스

Embed Size (px)

DESCRIPTION

12 장 프로세스. 12.1 프로그램 시작 및 종료. 프로그램 실행 시작. exec 시스템 호출 C 시작 루틴에 명령줄 인수와 환경 변수를 전달하고 프로그램을 실행시킨다 C 시작 루틴 (start-up routine) main 함수를 호출하면서 명령줄 인수 , 환경 변수를 전달 exit( main( argc, argv ) ); 실행이 끝나면 반환값을 받아 OS 로 exit 한다. 프로그램 실행 시작. exit( main( argc, argv ) );. 명령줄 인수 / 환경 변수. - PowerPoint PPT Presentation

Citation preview

Page 1: 12 장 프로세스

1212 장 프로세스장 프로세스

1

Page 2: 12 장 프로세스

12.1 12.1 프로그램 시작 및 종료프로그램 시작 및 종료

2

Page 3: 12 장 프로세스

프로그램 실행 시작프로그램 실행 시작 exec 시스템 호출

C 시작 루틴에 명령줄 인수와 환경 변수를 전달하고 프로그램을 실행시킨다

C 시작 루틴 (start-up routine) main 함수를 호출하면서 명령줄 인수 , 환경 변수를 전달

exit( main( argc, argv ) );

실행이 끝나면 반환값을 받아 OS 로 exit 한다

3

Page 4: 12 장 프로세스

프로그램 실행 시작프로그램 실행 시작

4

exit( main( argc, argv ) );

Page 5: 12 장 프로세스

명령줄 인수명령줄 인수 //환경 변수환경 변수

5

int main(int argc, char *argv[]);

argc : 명령줄 인수의 수

argv[] : 명령줄 인수 리스트를 나타내는 포인터 배열

argv: myecho\0

hello\0

world!\0

0

0

1

2

3

argc: 3

Page 6: 12 장 프로세스

printall.cprintall.c

#include <stdio.h>#include <stdlib.h>/* 모든 명령줄 인수와 환경 변수를 프린트한다 */int main(int argc, char *argv[]){ int i; char **ptr; extern char **environ;

for (i = 0; i < argc; i++) /* 모든 명령줄 인수 프린트 */ printf("argv[%d]: %s \n", i, argv[i]);

for (ptr = environ; *ptr != 0; ptr++) /* 모든 환경 변수 값 프린트 */ printf("%s \n", *ptr); exit(0);}

6

Page 7: 12 장 프로세스

프로그램 종료프로그램 종료 정상 종료 (normal termination)

main() 실행을 마치고 리턴하면 C 시작 루틴은 이 리턴값을 가지고 exit() 을 호출

프로그램 내에서 직접 exit() 을 호출 프로그램 내에서 직접 _exit() 을 호출

비정상 종료 (abnormal termination) abort()

• 프로세스에 SIGABRT 시그널을 보내어 프로세스를 비정상적으로 종료

시그널에 의한 종료

7

Page 8: 12 장 프로세스

프로그램 종료프로그램 종료 exit()

모든 열려진 스트림을 닫고 (fclose), 출력 버퍼의 내용을 디스크에 쓰는 (fflush) 등의 뒷정리 후 프로세스를 정상적으로 종료

종료 코드 (exit code) 를 부모 프로세스에게 전달한다

_exit()

8

#include <stdlib.h>void exit(int status);뒷정리를 한 후 프로세스를 정상적으로 종료시킨다 .

#include <stdlib.h>void _exit(int status);뒷정리를 하지 않고 프로세스를 즉시 종료시킨다 .

Page 9: 12 장 프로세스

12.2 12.2 프로세스 구조프로세스 구조

9

Page 10: 12 장 프로세스

프로세스프로세스 프로세스는 실행중인 프로그램이다

프로그램 실행을 위해서는 프로그램의 코드 , 데이터 , 스택 , 힙 , U- 영역 등이 필요하다

프로세스 이미지 ( 구조 ) 는 메모리 내의 프로세스 레이아웃

프로그램 자체가 프로세스는 아니다 !

10

Page 11: 12 장 프로세스

프로세스 구조프로세스 구조 프로세스 구조

코드 (code): executable object code

데이터 (data) : global variable & static variable

힙 (heap): dymanic memory allocation

스택 (stack): runtime stack

U- 영역 (user-area): open files

교재 373 참조(root 권한으로 ) $ cat /proc/1/maps

11

Page 12: 12 장 프로세스

(( 참고참고 ) Linux ) Linux 가상 메모리 구조가상 메모리 구조

운영체제

BSS(Block Started by Symbol)

하나의 태스크마다 4GB 주소 공간을 할당 각 태스크에게 3GB 할당 나머지 1GB 는 모든

태스크들의 공통 영역으로서 커널 공간으로 할당

$ cat /proc/1/maps

Page 13: 12 장 프로세스

(( 참고참고 ) Linux ) Linux 메모리 관리 자료구조메모리 관리 자료구조task_struct 구조체내의 struct mm_struct *mm

/usr/src/kernels/linux-2.6.35.6/include/linux/sched.h (1193 행 CentOS Linux 2.6.32.71)

/usr/src/kernels/linux-2.6.35.6/include/linux/mm_types.h (222 행 , 131 행 ) vm_area_struct 안의 struct mm_struct * vm_mm 은 메모리 디스크립터 포인터

운영체제

Page 14: 12 장 프로세스

12.3 12.3 프로세스 생성프로세스 생성

14

Page 15: 12 장 프로세스

프로세스 프로세스 IDID 각 프로세스는 프로세스를 구별하는 번호인 프로세스 ID 를 갖고

있다

15

int getpid( ); 프로세스의 ID 를 리턴한다 .

int getppid( ); 부모 프로세스의 ID 를 리턴한다 .

Page 16: 12 장 프로세스

프로세스 생성프로세스 생성

fork() 시스템 호출 부모 프로세스를 똑같이 복제하여 새로운 자식 프로세스를 생성 자기복제 ( 自己複製 )

fork() 는 한 번 호출되면 두 번 리턴한다 자식 프로세스에게는 0 을 리턴하고 부모 프로세스에게는 자식 프로세스 ID 를 리턴한다 부모 프로세스와 자식 프로세스는 병행적으로 각각 실행을 계속한다

16

#include <sys/types.h>

#include <unistd.h>

pid_t fork(void);

새로운 자식 프로세스를 생성한다 . 자식 프로세스에게는 0 을 리턴하고

부모 프로세스에게는 자식 프로세스 ID 를 리턴한다 .

Page 17: 12 장 프로세스

프로세스 생성프로세스 생성

17

Page 18: 12 장 프로세스

(( 예예 ) fork ) fork 시스템 호출시스템 호출 : onetwo.c: onetwo.c

18

printf(“One\n”);

pid = fork();

printf(Two\n”);

printf(“One\n”);

pid=fork();

printf(“Two\n”);

printf(“One\n”);

pid = fork();

printf(“Two\n”);

PC

PCPC

BEFOREAFTER

fork

자식 프로세스부모 프로세스

#include <stdio.h>main(){int pid; printf("One\n"); pid = fork(); printf("Two\n");}

$ gcc –o onetwo onetwo.c

Page 19: 12 장 프로세스

fork1.cfork1.c

#include <stdio.h>

/* 자식 프로세스를 생성한다 */

int main()

{

int pid;

printf("[%d] 프로세스 시작 \n", getpid());

pid = fork();

printf("[%d] 프로세스 : fork() 리턴값 pid=%d\n", getpid(), pid);

}

19

Page 20: 12 장 프로세스

부모 프로세스와 자식 프로세스 구분부모 프로세스와 자식 프로세스 구분 fork() 호출 후에 리턴값이 다르므로 이 리턴값을 이용하여 부모 프로세스와 자식 프로세스를 구별하고 서로 다른 일을 하도록 할 수 있다

pid = fork();if ( pid == 0 ) { 자식 프로세스의 실행 코드 } else{ 부모 프로세스의 실행 코드 }

20

Page 21: 12 장 프로세스

fork2.c: fork2.c: 자식 프로세스 생성자식 프로세스 생성#include <stdlib.h>

#include <stdio.h>

/* 부모 프로세스가 자식 프로세스를 생성하고 서로 다른 메시지를 프린트 */

int main()

{

int pid;

pid = fork();

if (pid ==0) { // 자식 프로세스 printf("[Child] : Hello, world pid=%d\n", getpid());

}

else { // 부모 프로세스 printf("[Parent] : Hello, world pid=%d\n", getpid());

}

}

21

Page 22: 12 장 프로세스

fork3.c: fork3.c: 두 개의 자식 프로세스 생성두 개의 자식 프로세스 생성#include <stdlib.h>#include <stdio.h>/* 부모 프로세스가 두 개의 자식 프로세스를 생성한다 */int main() { int pid1, pid2; pid1 = fork(); if (pid1 == 0) { printf("[Child 1] : Hello, world ! pid=%d\n", getpid()); exit(0); /* ① */ } pid2 = fork(); if (pid2 == 0) { printf("[Child 2] : Hello, world ! pid=%d\n", getpid()); exit(0); /* ② */ }}

22

①만 없애보세요 ! ②만 없애보세요 ! ①&② 모두 없애보세

요 !

Page 23: 12 장 프로세스

프로세스 기다리기프로세스 기다리기 : wait(): wait() 자식 프로세스 중의 하나가 끝날 때까지 기다린다

끝난 자식 프로세스의 종료 코드는 status 에 저장 끝난 자식 프로세스의 번호를 리턴 하나 이상의 자식이 이미 좀비라면 좀비 중 하나의 상태를 반환 ( 예 ) zombie.c

23

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status);

Page 24: 12 장 프로세스

zombie.czombie.c#include <stdio.h>#include <stdlib.h>main (){ int pid; pid = fork (); /* Duplicate */ if (pid != 0) /* Branch based on return value from fork () */ { while (1) /* Never terminate, and never execute a wait () */ sleep (1000); } else { exit (42); /* Exit with a silly number */ }}

24

$ gcc –o zombie zombie.c$ zombie &$ ps재부팅 후$ ps$ ps ux또는 $ ps –ef | grep 자기id$ killall zombie

Page 25: 12 장 프로세스

wait(&status)wait(&status)

wait(&status) /* int status */

status : 자식이 exit 으로 종료될 때의 상태정보 • 정상종료 경우 : 하위 8 비트는 0, 상위 8 비트는 exit status( 자식

프로세스가 exit 명령으로 전달한 값 )

• signal 로 종료된 경우 : 하위 8 비트는 signal 번호 , 상위 8 비트는

0

( 하위 8 비트 추출 )

status & 0x00FF;

( 상위 8 비트 추출 )

status >> 8

25

e x i t 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 s i g n a l

e x i t 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

0 0 0 0 0 0 0 0 e x i t

e x i t 0 0 0 0 0 0 0 0

&

>> 8

Page 26: 12 장 프로세스

forkwait.c: forkwait.c: 자식 프로세스 기다리기자식 프로세스 기다리기#include <stdio.h>#include <stdlib.h>/* 부모 프로세스가 자식 프로세스를 생성하고 끝나기를 기다린다 */int main(){ int pid, child, status; printf("[%d] 부모 프로세스 시작 \n", getpid( )); pid = fork(); if (pid == 0) { printf("[%d] 자식 프로세스 시작 \n", getpid( )); exit(1); } child = wait(&status); // 자식 프로세스가 끝나기를 기다린다 . printf("[%d] 자식 프로세스 %d 종료 \n", getpid(), child); printf("\t 종료 코드 %d\n", status >> 8);}

26

자식 프로세스 종료 코드 하위 8 비트 /상위 8 비트 구분 출력하기상위 8 비트 /하위 8 비트로 출력 순서 바꾸면 ?

Page 27: 12 장 프로세스

12.4 12.4 프로그램 실행프로그램 실행

27

Page 28: 12 장 프로세스

프로그램 실행프로그램 실행 fork() 후

자식 프로세스는 부모 프로세스와 똑같은 코드 실행

자식 프로세스에게 새로운 프로그램을 시키려면 어떻게 하여야 할까 ? 프로세스 내의 프로그램을 새 프로그램으로 대치 exec() 시스템 호출 사용

보통 fork() 후에 exec( )

28

Page 29: 12 장 프로세스

프로그램 실행프로그램 실행 : exec() : exec() 프로세스가 exec() 호출을 하면 ,

그 프로세스 내의 프로그램은 완전히 새로운 프로그램으로 대치 자기대치 ( 自己代置 )

새 프로그램의 main() 부터 실행이 시작한다

29

Page 30: 12 장 프로세스

프로그램 실행프로그램 실행 : exec() : exec() exec() 호출이 성공하면 리턴할 곳이 없어진다 성공한 exec() 호출은 절대 리턴하지 않는다

30

#include <unistd.h>

int execl(char* path, char* arg0, char* arg1, ... , char* argn, NULL)

int execv(char* path, char* argv[ ])

int execlp(char* file, char* arg0, char* arg1, ... , char* argn, NULL)

int execvp(char* file, char* argv[ ])

호출한 프로세스의 코드 , 데이터 , 힙 , 스택 등을 path 가 나타내는 새로운

프로그램으로 대치한 후 새 프로그램을 실행한다 . 성공한 exec( ) 호출은 리턴하지

않으며 실패하면 -1 을 리턴한다 .

Page 31: 12 장 프로세스

fork/execfork/exec 보통 fork() 호출 후에 exec() 호출

새로 실행할 프로그램에 대한 정보를 arguments 로 전달한다• .

exec() 호출이 성공하면 자식 프로세스는 새로운 프로그램을 실행하게 되고 부모는 계속해서 다음 코드를 실행하게 된다

if ((pid = fork()) == 0 ){

exec( arguments );

exit(1);

}

// 부모 계속 실행

31

Page 32: 12 장 프로세스

/* first line of ls */

forkfork 와 와 execexec 호출의 조합호출의 조합

PC

BA

A BEFORE FORK AFTER FORK

wait((int*)0);

wait((int*)0);

pid = fork();

A

PC

PC

AFTER FORK AFTER EXEC

execl("/bin/ls" …);

B (now runs ls)

PC

PC

32

Page 33: 12 장 프로세스

execute1.cexecute1.c#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

/* 자식 프로세스를 생성하여 echo 명령어를 실행한다 */

int main( )

{

printf(" 부모 프로세스 시작 \n");

if (fork( ) == 0) {

fprintf(stdout, " 자식 프로세스 시작 \n");

execl("/bin/echo", "echo", "hello", "world", NULL);

fprintf(stderr, " 자식 프로세스 실패 \n");

exit(1);

}

printf(" 부모 프로세스 끝 \n");

}

33

" ls -l " 실행하도록 수정하세요 !

Page 34: 12 장 프로세스

#include <stdio.h>#include <stdlib.h>#include <unistd.h>/* 자식 프로세스를 생성하여 echo 명령어를 실행한다 */int main( ){ char *av[3]; av[0]=“ls"; av[1]=“-l"; av[2]=NULL; printf(" 부모 프로세스 시작 \n"); if (fork( ) == 0) { fprintf(stdout, " 자식 프로세스 시작 \n"); execv("/bin/ls", av); fprintf(stderr, " 자식 프로세스 실패 \n"); exit(1); } printf(" 부모 프로세스 끝 \n");}

(( 응용응용 ) execute11.c) execute11.c

34

" ls -l -i " 실행하도록 수정하세요 !

avls\0

-l\0

0x8048644

0x8048647

0

1

2 0x0

0xbffff5c4 0x8048644

Page 35: 12 장 프로세스

execute2.cexecute2.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>/* 세 개의 자식 프로세스를 생성하여 각각 다른 명령어를 실행한다 */int main( ) { printf(" 부모 프로세스 시작 \n"); if (fork( ) == 0) { execl("/bin/echo", "echo", "hello", NULL); fprintf(stderr," 첫 번째 실패 "); exit(1); } if (fork( ) == 0) { execl("/bin/date", "date", NULL); fprintf(stderr," 두 번째 실패 "); exit(2); } if (fork( ) == 0) { execl("/bin/ls","ls", "-l", NULL); fprintf(stderr," 세 번째 실패 "); exit(3); } printf(" 부모 프로세스 끝 \n");}

35

Page 36: 12 장 프로세스

execute3.c: execute3.c: 자식 프로세스 명령어 실행자식 프로세스 명령어 실행#include <stdio.h>

/* 명령줄 인수로 받은 명령을 실행시킨다 */

int main(int argc, char *argv[])

{

int child, pid, status;

pid = fork( );

if (pid == 0) { // 자식 프로세스 execvp( argv[1], &argv[1] );

fprintf(stderr, "%s: 실행 불가 \n",argv[1]);

} else { // 부모 프로세스 child = wait(&status);

printf("[parent: %d] 자식 프로세스 %d 종료 \n", getpid(), pid);

printf("[child : %d] 종료 코드 하위 : %d 상위 : %d \n", child, status & 0x00FF, status >> 8);

}

}

36

$ gcc –g –o execute3 execute3.c$ execute3 ls –l$ execute3 myexit

$ vi myexit.c

#include <stdio.h>

#include <stdlib.h>

main ()

{

printf ("I'm going to exit with return code\n");

exit (33);

}

Q: Why &argv[1] ?

Page 37: 12 장 프로세스

$ gcc –g –o execute3 execute3.c$ gdb execute3(gdb) b 6Breakpoint 1 at 0x80484dd: file execute3.c, line 6.(gdb) b 13Breakpoint 2 at 0x8048559: file execute3.c, line 13.(gdb) r myexitStarting program: /home/mysung/ulprog/cprog/execute3

myexitBreakpoint 1, main (argc=2, argv=0xbffff684) at

execute3.c:66 pid = fork( );(gdb) p argc$1 = 2(gdb) p argv$2 = (char **) 0xbffff684(gdb) p &argc$3 = (int *) 0xbffff5e0(gdb) p &argv$4 = (char ***) 0xbffff5e4(gdb) p argv[0]$5 = 0xbffff7c5 "/home/mysung/ulprog/cprog/execute3"(gdb) p argv[1]$6 = 0xbffff7e8 "myexit"(gdb) p argv[2]$7 = 0x0(gdb) p &argv[0]$8 = (char **) 0xbffff684(gdb) p &argv[1]$9 = (char **) 0xbffff688(gdb) p &argv[2]$10 = (char **) 0xbffff68c

(gdb) cContinuing.Detaching after fork from child process 11288.I'm going to exit with return code[parent: 11246] 자식 프로세스 11288 종료Breakpoint 2, main (argc=2, argv=0xbffff684) at execute3.c:1313 printf("[child : %d] 종료 코드 하위 : %d 상위 : %d \n", child, status & 0x00FF, status >> 8 );(gdb) p status & 0xFF$11 = 0(gdb) p status >> 8$12 = 77(gdb) cContinuing.[child : 11288] 종료 코드 하위 : 0 상위 : 77Program exited with code 054.(gdb) quit

Why &argv[1] ?Why &argv[1] ?

37

0xbffff684argv execute3\0

myexit\0

0xbffff7c5

0xbffff7e8

0

1

2

argc 2 0xbffff684

0x0

0xbffff7c5

Q: execvp( argv[1], &argv[1] ); =execvp( ?? , ?? );

Page 38: 12 장 프로세스

(( 응용응용 ) execute33.c: ) execute33.c: 자식 프로세스 자식 프로세스 killkill

#include <stdio.h>#include <stdlib.h>#include <signal.h>/* 명령줄 인수로 받은 명령을 실행시킨다 */int main(int argc, char *argv[]){ int child, pid, status; pid = fork( ); if (pid == 0) { // 자식 프로세스 execvp(argv[1], &argv[1]); fprintf(stderr, "%s: 실행 불가 \n",argv[1]); } else { // 부모 프로세스 printf("SIGINT=%d, SIGKILL=%d, SIGTERM=%d\n", SIGINT, SIGKILL, SIGTERM); kill(pid, SIGINT); child = wait(&status); printf("[parent: %d] 자식 프로세스 %d 종료 \n", getpid(), pid); printf("[child : %d] 종료 코드 하위 : %d 상위 : %d \n", child, status & 0x00FF,

status>>8); }}

38

$ gcc -g –o execute33 execute33.c$ execute33 sleep 100

GDB 실행하여 확인해 보세요 !

Page 39: 12 장 프로세스

$ gdb execute33$ gdb execute33

39

$ gcc -g -o execute33 execute33.c $ gdb execute33(gdb) b 8Breakpoint 1 at 0x804850d: file execute33.c, line 8.(gdb) b 14Breakpoint 2 at 0x8048583: file execute33.c, line 14.(gdb) r sleep 100Starting program: /home/mysung/ulprog/cprog/execute33 sleep

100Breakpoint 1, main (argc=3, argv=0xbffff684) at execute33.c:88 pid = fork( );(gdb) p &argc$1 = (int *) 0xbffff5e0(gdb) p &argv$2 = (char ***) 0xbffff5e4(gdb) p argv[0]$3 = 0xbffff7c0 "/home/mysung/ulprog/cprog/execute33"(gdb) p argv[1]$4 = 0xbffff7e4 "sleep"(gdb) p argv[2]$5 = 0xbffff7ea "100"(gdb) p argv[3]$6 = 0x0(gdb) p pid$7 = 8065012(gdb) nDetaching after fork from child process 11893.9 if (pid == 0) { // 자식 프로세스(gdb) p pid$8 = 11893(gdb) n13 printf("SIGINT=%d, SIGKILL=%d, SIGTERM=%d\n",

SIGINT, SIGKILL, SIGTERM);(gdb) cContinuing.SIGINT=2, SIGKILL=9, SIGTERM=15Breakpoint 2, main (argc=3, argv=0xbffff684) at execute33.c:1414 kill(pid, SIGKILL);

(gdb) p pid$7 = 11893(gdb) p SIGKILLNo symbol "SIGKILL" in current context.(gdb) n15 child = wait(&status);(gdb) p child$8 = 134514203(gdb) p status$9 = 134513744(gdb) n16 printf("[parent: %d] 자식 프로세스 %d 종료 \n",

getpid(), pid);(gdb) p getpid()$10 = 11888(gdb) p pid$11 = 11893(gdb) n[parent: 11888] 자식 프로세스 11893 종료17 printf("[child : %d] 종료 코드 상위 : %d 하위 : %d \

n", child, status>>8, status & 0x00FF);(gdb) p child$12 = 11893(gdb) p status$13 = 9(gdb) p status >> 8$14 = 0(gdb) p status & 0x00ff$15 = 9(gdb) quit

0xbffff684argv execute33\0

sleep\0

100\0

0xbffff7c0

0xbffff7e4

0xbffff7ea

0

1

2

3

argc 3 0xbffff684 0xbffff7c0

0x0

Page 40: 12 장 프로세스

(( 시험시험 예시예시 ) run.c) run.c#include <stdio.h>

#include <stdlib.h>

#include <signal.h>

/* 명령줄 인수로 받은 명령을 실행시킨다 */

int main(int argc, char *argv[])

{

int child, pid, status;

pid = fork( );

if (pid == 0) { // 자식 프로세스

execvp(argv[1], &argv[1]);

fprintf(stderr, "%s: 실행 불가 \n",argv[1]);

} else { // 부모 프로세스

/* kill(pid, SIGKILL); */

child = wait(&status);

printf("[parent: %d] 자식 프로세스 %d 종료 \n", getpid(), pid);

printf("[child : %d] 종료 코드 하위 : %d 상위 : %d \n", child, status & 0x00FF, status>>8);

}

}40

②argv: run\0

sleep\0

100\0

⑤0

argc: ①

$ gcc –g –o run.c run$ run

Page 41: 12 장 프로세스

(( 시험 예시시험 예시 ) $ gdb run) $ gdb run

41

$ gcc -g -o run run.c $ gdb run(gdb) b 8Breakpoint 1 at 0x804850d: file run.c, line 8.(gdb) r sleep 100Starting program:

/home/mysung/59bu/mytest/x2013ul/run sleep 100Breakpoint 1, main (argc=3, argv=0xbffff674) at

run.c:88 pid = fork( );(gdb) p &argc$1 = (int *) 0xbffff5d0(gdb) p &argv$2 = (char ***) 0xbffff5d4(gdb) p argv[0]$3 = 0xbffff7b7 "/home/mysung/59bu/mytest/x2013ul/run"(gdb) p argv[1]$4 = 0xbffff7dc "sleep"(gdb) p argv[2]$5 = 0xbffff7e2 "100"(gdb) p argv[3]$6 = 0x0(gdb) p &argv[0]$7 = (char **) 0xbffff674(gdb) p &argv[1]$8 = (char **) 0xbffff678(gdb) p &argv[2]$9 = (char **) 0xbffff67c(gdb) p &argv[3]$10 = (char **) 0xbffff680(gdb) nDetaching after fork from child process 28803.9 if (pid == 0) { // 자식 프로세스

(gdb) p pid$11 = 28803(gdb) n13 kill(pid, SIGKILL);(gdb) p pid$12 = 28803(gdb) p SIGKILLNo symbol "SIGKILL" in current context.(gdb) n14 child = wait(&status);(gdb) n15 printf("[parent: %d] 자식 프로세스 %d 종료 \n",

getpid(), pid);(gdb) p getpid()$13 = 28660(gdb) p pid$14 = 28803(gdb) n[parent: 28660] 자식 프로세스 28803 종료16 printf("[child : %d] 종료 코드 상위 : %d 하위 : %d \n",

child, status>>8, status & 0x00FF);(gdb) p child$15 = 28803(gdb) p status>>8$16 = 0(gdb) p status & 0x00FF$17 = 9(gdb) p status$18 = 9(gdb) quit

0xbffff674argv run\0

sleep\0

100\0

0xbffff7b7

0xbffff7dc

0xbffff7e2

0

1

2

3

argc 3 0xbffff674 0xbffff7b7

0x0

Page 42: 12 장 프로세스

12.5 12.5 입출력 재지정입출력 재지정

42

Page 43: 12 장 프로세스

입출력 재지정입출력 재지정 명령어의 표준 출력이 파일에 저장

$ 명령어 > 파일 출력 재지정 기능 구현

dup() 혹은 dup2() 시스템 호출

파일 디스크립터 fd 를 표준출력 (1) 에 dup2() fd = open(argv[1], O_CREAT|O_TRUNC|O_WRONLY, 0600);dup2(fd, 1);

43

#include <unistd.h>

int dup(int oldfd);

int dup2(int oldfd, int newfd);

성공하면 복사된 새로운 파일 디스크립터를 리턴 , 실패하면 -1 을 리턴

fd1fd2fd3

파일

Page 44: 12 장 프로세스

(( 참고참고 ) inode () inode (iindexndex nodenode) ) 구조구조

44

Linux CentOS 2.6.32-71.el6.i686 #1 SMP /usr/include/linux/ext2_fs.h

• struct ext2_inode /* ext2_fs.h 208 행 */• /* ext2_fs.h 77 행 reserved inode bumbers */

#define EXT2_BAD_INO 1 /* Bad blocks inode */#define EXT2_ROOT_INO 2 /* Root inode */#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode *//* First non-reserved inode for old ext2 filesystems */#define EXT2_GOOD_OLD_FIRST_INO 11

• /usr/src/kernels/2.6.43.8-1.fc15.i686/include/linux/fs.h• struct file /* fs.h 971 행 */• struct address_space /* fs.h 641 행 */• struct inode /* fs.h 756 행 */

Solaris 10 /usr/include/sys/file.h 및 vnode.h

• typedef struct file /* file.h 43 행 */• typedef struct vnode /* vnode.h 243 행

Page 45: 12 장 프로세스

redirect1.c: redirect1.c: 표준출력 재지정표준출력 재지정#include <stdio.h>

#include <fcntl.h>

/* 표준 출력을 파일에 재지정하는 프로그램 */

int main(int argc, char* argv[])

{

int fd, status;

fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0600);

dup2(fd, 1); /* 파일을 표준출력에 복제 */

close(fd);

printf("Hello stdout !\n");

fprintf(stderr,"Hello stderr !\n");

}

45

$ gcc –o redirect 1 redirect1.c$ redirect1 xx

Page 46: 12 장 프로세스

redirect2.c: redirect2.c: 자식 프로세스 표준출력 자식 프로세스 표준출력 재지정재지정#include <stdio.h>#include <fcntl.h>/* 자식 프로세스의 표준 출력을 파일에 재지정한다 */int main(int argc, char* argv[]){ int child, pid, fd, status; pid = fork( ); if (pid == 0) { fd = open(argv[1],O_CREAT | O_TRUNC| O_WRONLY, 0600); dup2(fd, 1); // 파일을 표준출력에 복제 close(fd); execvp(argv[2], &argv[2]); fprintf(stderr, "%s: 실행 불가 \n",argv[1]); } else { child = wait(&status); printf("[%d] 자식 프로세스 %d 종료 \n", getpid(), child); } }

46

$ gcc –o redirect2 redirect2.c$ redirect2 xx ls –l $ ls –l > xx (redirect out) 원리

Page 47: 12 장 프로세스

기타 시스템 호출기타 시스템 호출

47

#include <unistd.h>

int chdir (char* pathname);

현재 작업 디렉토리를 pathname 으로 변경한다 . 성공하면 0 실패하면 -1 를 리턴한다

#include <sys/types.h>

#include <unistd.h>

int getuid(); 프로세스의 사용자 ID 를 리턴한다

int getgid(); 프로세스의 그룹 ID 를 리턴한다

#include <sys/types.h>

#include <unistd.h>

int setuid(uid_t uid); 프로세스의 사용자 ID 를 uid 로 변경한다int setgid(gid_t gid); 프로세스의 그룹 ID 를 gid 로 변경한다

Page 48: 12 장 프로세스

12.6 12.6 시스템 부팅시스템 부팅

48

Page 49: 12 장 프로세스

시스템 부팅시스템 부팅 시스템 부팅은 fork/exec 시스템 호출을 통해 이루어진다

49

$ ps alx | more$ ps –ef | more

(root 권한으로 )$ cat /proc/1/maps

같은 pid!

Page 50: 12 장 프로세스

시스템 부팅시스템 부팅 swapper( 스케줄러 프로세스 )

커널 내부에서 만들어진 프로세스로 프로세스 스케줄링을 한다 init( 초기화 프로세스 )

/etc/inittab 파일에 기술된 대로 시스템을 초기화 getty 프로세스

로그인 프롬프트를 내고 키보드 입력을 감지한다 login 프로세스

사용자의 로그인 아이디 및 패스워드를 검사 shell 프로세스

시작 파일을 실행한 후에 쉘 프롬프트를 내고 사용자로부터 명령어를 기다린다

50

Page 51: 12 장 프로세스

12.7 12.7 시그널시그널

51

Page 52: 12 장 프로세스

시그널 종류시그널 종류 시그널은 예기치 않은 사건이 발생할 때 이를 알리는 소프트웨어

인터럽트이다 총 31 개의 유저영역 시그널이 /usr/include/asm/signal.h 에 정의 각 시그널 이름은 SIG 로 시작되며 교재 표 12.1 과 같다

$ kill –l

$ stty -a 시그널 발생 예

SIGFPE 부동소수점 오류 SIGPWR 정전 SIGALRM 알람시계 울림 SIGCHLD 자식 프로세스 종료 SIGINT 키보드로부터 종료 요청 (Ctrl-C) SIGCONT 키보드로부터 정지 요청 (Ctrl-Z)

52

Page 53: 12 장 프로세스

alarm.calarm.c

#include <stdio.h>

/* 알람 시그널을 보여주는 프로그램 */

int main( )

{

alarm(5);

printf(" 무한 루프 \n");

while (1) {

sleep(1);

printf("1 초 경과 \n");

}

printf(" 실행되지 않음 \n");

}

53

Page 54: 12 장 프로세스

시그널 처리시그널 처리 시그널에 대한 처리함수 지정

signal() 시스템 호출 “ 이 시그널이 발생하면 이렇게 처리하라”

signal() 시스템 호출

func 은 SIG_IGN, SIG_DFL 혹은 사용자 정의 함수 이름

54

#include <signal.h>

signal(int signo, void (*func)( )))

signo 에 대한 처리 함수를 func 으로 지정한다 . 기존의 처리함수를 리턴한다 .

Page 55: 12 장 프로세스

alarmhandler.calarmhandler.c#include <stdio.h>#include <stdlib.h>#include <signal.h>void alarmHandler();/* 알람 시그널을 처리한다 */int main( ){ signal(SIGALRM, alarmHandler); alarm(5); /* 알람 시간 설정 */ printf(" 무한 루프 \n"); while (1) { sleep(1); printf("1 초 경과 \n"); } printf(" 실행되지 않음 \n");}void alarmHandler(){ printf(" 일어나세요 \n"); exit(0);}

55

signal(SIGALRM, SIG_IGN); signal(SIGALRM, SIG_DFL);

Page 56: 12 장 프로세스

inthandler.cinthandler.c#include <stdio.h>#include <stdlib.h>#include <signal.h>void intHandler();/* 인터럽트 시그널을 처리한다 */int main( ){ signal(SIGINT, intHandler); //->SIGQUIT test while (1) pause(); printf(" 실행되지 않음 \n");}void intHandler(){ printf(" 인터럽트 시그널 %d 처리 \n", SIGINT); //->SIGQUIT test exit(0);}

56

( 응용 1) SIGQUIT test ( 응용 2) SIGINT 가로채서 무시하도록 수정

#include <signal.h>

pause()

시 그 널 을 받 을 때 까 지 해 당 프로세스를 잠들게 만든다

Page 57: 12 장 프로세스

제한 시간 명령어 실행제한 시간 명령어 실행 tlimit.c 프로그램

명령줄 인수로 받은 명령어를 제한 시간 내에 실행 execute3.c 프로그램을 알람 시그널을 이용하여 확장

프로그램 설명 자식 프로세스가 명령어를 실행하는 동안 정해진 시간이 초과되면

SIGALRM 시그널이 발생 SIGALRM 시그널에 대한 처리함수 alarmHandler() 에서 자식

프로세스를 강제 종료 kill(pid,SIGINT) 호출을 통해 자식 프로세스에 SIGINT 시그널을

보내어 강제 종료 만약 SIGALRM 시그널이 발생하기 전에 자식 프로세스가

종료하면 이 프로그램은 정상적으로 끝남

57

Page 58: 12 장 프로세스

tlimit.ctlimit.c#include <stdio.h>#include <signal.h>int pid;void alarmHandler();/* 명령줄 인수로 받은 명령어 실행에 제한 시간을 둔다 */int main(int argc, char *argv[]){ int child, status, limit; signal(SIGALRM, alarmHandler); sscanf(argv[1], "%d", &limit); alarm(limit); pid = fork( );if (pid == 0) { execvp(argv[2], &argv[2]); fprintf(stderr, "%s: 실행 불가 \n", argv[1]); } else { child = wait(&status); printf("[%d] 자식 프로세스 %d 종료 \n", getpid(), pid); }}void alarmHandler(){ printf("[ 알람 ] 자식 프로세스 %d 시간 초과 \n", pid); kill(pid,SIGINT);} 58

$ gcc –o tlimit tlimit.c$ tlimit 5 sleep 100

Page 59: 12 장 프로세스

시그널 보내기시그널 보내기 : kill(): kill() kill() 시스템 호출

특정 프로세스 pid 에 원하는 임의의 시그널 signo 를 보낸다 보내는 프로세스의 소유자가 프로세스 pid 의 소유자와 같거나

혹은 보내는 프로세스의 소유자가 슈퍼유저이어야 한다

59

#include <sys/types.h>

#include <signal.h>

int kill(int pid, int signo);

프로세스 pid 에 시그널 signo 를 보낸다 . 성공하면 0 실패하면 -1 를

리턴한다 .

Page 60: 12 장 프로세스

control.ccontrol.c#include <signal.h>#include <stdio.h>/* 시그널을 이용하여 자식

프로세스들을 제어한다 */int main( ){ int pid1, pid2;

pid1 = fork( ); if (pid1 == 0) { while (1) { sleep(1); printf(" 프로세스 [1] 실행 \

n"); } } pid2 = fork( );

if (pid2 == 0) { while (1) { sleep(1); printf(" 프로세스 [2] 실행 \

n"); } } sleep(2); kill(pid1, SIGSTOP); sleep(4); kill(pid1, SIGCONT); sleep(2); kill(pid2, SIGSTOP); sleep(4); kill(pid2, SIGCONT); sleep(2); kill(pid1, SIGKILL); kill(pid2, SIGKILL);} 60

Page 61: 12 장 프로세스

핵심 개념핵심 개념 프로세스는 실행중인 프로그램이다

fork() 시스템 호출은 부모 프로세스를 똑같이 복제하여 새로운 자식 프로세스를 생성한다

exec() 시스템 호출은 프로세스 내의 프로그램을 새로운 프로그램으로 대치하여 새로운 프로그램을 실행시킨다

시스템 부팅은 fork/exec 시스템 호출을 통해 이루어진다

시그널은 예기치 않은 사건이 발생할 때 이를 알리는 소프트웨어 인터럽트이다

61