12장 프로세스.

Slides:



Advertisements
Similar presentations
Linux System Programming
Advertisements

Linux/UNIX Programming APUE (The Environment of a UNIX Process)
2장. 프로그램의 기본 구성. 2장. 프로그램의 기본 구성 2-1"Hello, World!" 들여다 보기 /* Hello.c */ #include int main(void) { printf("Hello, World! \n"); return 0;
인공지능실험실 석사 2학기 이희재 TCP/IP Socket Programming… 제 11장 프로세스간 통신 인공지능실험실 석사 2학기 이희재
9장. C 언어의 핵심! 함수. 9장. C 언어의 핵심! 함수 9-1 함수의 정의와 선언 main 함수 다시 보기 : 함수의 기본 형태 { } 그림 9-1.
Linux System Programming
8장 프로세스.
인공지능실험실 석사 2학기 김승겸 TCP/IP Socket Programming… 제 10장 멀티태스킹 기반의 서버구현 인공지능실험실 석사 2학기 김승겸
Signal & Inter-Process Communication
Department of Computer Engineering
Department of Computer Science and Engineering
6 프로세스 생성과 실행.
6 프로세스 생성과 실행.
Department of Computer Science and Engineering
조 병 규 Software Quality Lab. 한국교통대학교
제15장 파일 입출력 문자열을 출력하는 여러가지 방법 (15-2쪽) 문자열만 처리하는 입출력 함수
쉽게 풀어쓴 C언어 Express 제17장 동적메모리와 연결리스트 C Express Slide 1 (of 13)
Department of Computer Engineering
Homework #6 (1/3) 다음을 수행한 후, 화면(혹은 파일)을 출력하여 제출한다.
양방향 파이프의 활용 양방향 통신 파이프는 기본적으로 단방향이므로 양방향 통신을 위해서는 파이프를 2개 생성한다.
12장 파이프.
fork로 생성한 자식 프로세스에서 exec 함수군을 호출
07. 디바이스 드라이버의 초기화와 종료 김진홍
Chapter 06 프로세스와 예약작업 관리 Solaris 1. 프로세스 관리
12장 프로세스.
8장 함수 함수의 필요성 라이브러리 함수와 사용자 정의 함수 함수의 정의, 원형, 호출 배열을 함수 인자로 전달 재귀호출.
Signal & Inter-Process Communication
Term Project Team Member
Department of Computer Engineering
프로세스 생성[1] 프로그램 실행 : system(3) #include <stdlib.h>
컴퓨터 프로그래밍 기초 #02 : printf(), scanf()
8 메모리 매핑.
파일 기술자 파일 기술자 현재 열려있는 파일을 구분하는 정수값 저수준 파일 입출력에서 열린 파일을 참조하는데 사용
4장 파일.
임베디드 실습 # LED, 7’Segment 제어
6장 파일 및 레코드 잠금.
메시지 큐[5] – test1.c 메시지 제어: msgctl(2) #include <sys/msg.h>
TCP/IP Socket Programming…
Department of Computer Engineering
5 프로세스 정보.
사용자 함수 사용하기 함수 함수 정의 프로그램에서 특정한 기능을 수행하도록 만든 하나의 단위 작업
어서와 C언어는 처음이지 제14장.
12. 시스템 프로그래밍 (System Programming)
Signal & Inter-Process Communication
13장 프로세스 사이의 통신.
Homework #6 (1/3) 다음을 수행한 후, 화면(혹은 파일)을 출력하여 제출한다.
24장. 파일 입출력.
쉽게 풀어쓴 C언어 Express 제14장 포인터 활용 C Express Slide 1 (of 22)
Homework #6 (1/3) 다음을 수행한 후, 화면(혹은 파일)을 출력하여 제출한다.
11장 시그널.
Chapter6 : JVM과 메모리 6.1 JVM의 구조와 메모리 모델 6.2 프로그램 실행과 메모리 6.3 객체생성과 메모리
School of Electronics and Information. Kyung Hee University.
Department of Computer Engineering
컴퓨터 프로그래밍 기초 - 8th : 함수와 변수 / 배열 -
Signal & Inter-Process Communication
Homework 7… 마지막 수업시간까지 (실습) 매개변수로 입력 받아 처리할 수 있도록 수정해 보세요
Department of Computer Engineering
( Windows Service Application Debugging )
디버깅 관련 옵션 실습해보기 발표 : 2008년 5월 19일 2분반 정 훈 승
시스템 인터페이스 Lab#5 쉡 실습.
Homework #12 (1/2) 프로그램을 작성하고, 프로그램과 실행 결과를 프린트하여 제출한다.
3. 모듈 (5장. 모듈).
함수, 모듈.
구조체(struct)와 공용체(union)
실습과제 1번 생성된 파일 basic.txt를 프로젝트 폴더에서 메모장으로 열고 내용을 확인
Department of Computer Engineering
06. 디바이스의 등록과 해제 김진홍
Signal & Inter-Process Communication
제 29 강 스트링(string) 다루기 s a i s . s T i h t g r i n.
Signal & Inter-Process Communication
Presentation transcript:

12장 프로세스

12.1 프로그램 시작 및 종료

프로그램 실행 시작 exec 시스템 호출 C 시작 루틴(start-up routine) 프로그램을 실행시킨다 C 시작 루틴(start-up routine) main 함수를 호출하면서 명령줄 인수, 환경 변수를 전달 exit( main( argc, argv) ); 실행이 끝나면 반환값을 받아 exit 한다

프로그램 실행 시작

명령줄 인수/환경 변수 int main(int argc, char *argv[]); argc : 명령줄 인수의 수

printall.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); }

프로그램 종료 정상 종료(normal termination) 비정상 종료(abnormal termination) main() 실행을 마치고 리턴하면 C 시작 루틴은 이 리턴값을 가 지고 exit()을 호출 프로그램 내에서 직접 exit()을 호출 프로그램 내에서 직접 _exit()을 호출 비정상 종료(abnormal termination) abort() 프로세스에 SIGABRT 시그널을 보내어 프로세스를 비정상적으로 종료 시그널에 의한 종료

프로그램 종료 exit() 모든 열려진 스트림을 닫고(fclose), 출력 버퍼의 내용을 디스크 에 쓰는(fflush) 등의 뒷정리 후 프로세스를 정상적으로 종료 종료 코드(exit code)를 부모 프로세스에게 전달한다 _exit() #include <stdlib.h> void exit(int status); 뒷정리를 한 후 프로세스를 정상적으로 종료시킨다. #include <stdlib.h> void _exit(int status); 뒷정리를 하지 않고 프로세스를 즉시 종료시킨다.

12.2 프로세스 구조

프로세스 프로세스는 실행중인 프로그램이다 프로그램 실행을 위해서는 프로세스 이미지(구조)는 메모리 내의 프로세스 레이아웃 프로그램의 코드, 데이터, 스택, 힙, U-영역 등이 필요하다 프로세스 이미지(구조)는 메모리 내의 프로세스 레이아웃 프로그램 자체가 프로세스는 아니다 !

프로세스 구조 프로세스 구조 코드(code): executable object code 데이터(data) : global variable & static variable 힙(heap): dymanic memery allocation 스택 (stack): runtime stack U-영역(user-area): open files 교재 373 참조

12.3 프로세스 생성

프로세스 ID 각 프로세스는 프로세스를 구별하는 번호인 프로세스 ID를 갖고 있다 int getpid( ); 프로세스의 ID를 리턴한다. int getppid( ); 부모 프로세스의 ID를 리턴한다.

프로세스 생성 fork() 시스템 호출 fork()는 한 번 호출되면 두 번 리턴한다 부모 프로세스를 똑같이 복제하여 새로운 자식 프로세스를 생성 자기복제(自己複製) fork()는 한 번 호출되면 두 번 리턴한다 자식 프로세스에게는 0을 리턴하고 부모 프로세스에게는 자식 프로세스 ID를 리턴한다 부모 프로세스와 자식 프로세스는 병행적으로 각각 실행을 계속한다 #include <sys/types.h> #include <unistd.h> pid_t fork(void); 새로운 자식 프로세스를 생성한다. 자식 프로세스에게는 0을 리턴하고 부모 프로세스에게는 자식 프로세스 ID를 리턴한다.

프로세스 생성

(예) fork 시스템 호출: onetwo.c #include <stdio.h> main() { int pid; printf("One\n"); pid = fork(); printf("Two\n"); } printf(“One\n”); pid = fork(); printf(Two\n”); PC BEFORE $ gcc –o onetwo onetwo.c fork AFTER printf(“One\n”); pid=fork(); printf(“Two\n”); printf(“One\n”); pid = fork(); printf(“Two\n”); PC PC 부모 프로세스 자식 프로세스

fork1.c #include <stdio.h> /* 자식 프로세스를 생성한다 */ int main() { int pid; printf("[%d] 프로세스 시작 \n", getpid()); pid = fork(); printf("[%d] 프로세스 : fork() 리턴값 pid=%d\n", getpid(), pid); }

부모 프로세스와 자식 프로세스 구분 fork() 호출 후에 리턴값이 다르므로 이 리턴값을 이용하여 부모 프로세스와 자식 프로세스를 구별하고 서로 다른 일을 하도록 할 수 있다 pid = fork(); if ( pid == 0 ) { 자식 프로세스의 실행 코드 } else { 부모 프로세스의 실행 코드 }

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());

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());

프로세스 기다리기: wait() 자식 프로세스 중의 하나가 끝날 때까지 기다린다 끝난 자식 프로세스의 종료 코드는 status에 저장 끝난 자식 프로세스의 번호를 리턴 하나 이상의 자식이 이미 좀비라면 좀비 중 하나의 상태를 반환 (예) zombie.c #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status);

zombie.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 */ $ gcc –o zombie zombie.c $ zombie & $ ps

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

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); 자식 프로세스 종료 코드 상위8비트/하위8비트 구분 출력하기

12.4 프로그램 실행

프로그램 실행 fork() 후 자식 프로세스에게 새로운 프로그램을 시키려면 어떻게 하 여야 할까? 자식 프로세스는 부모 프로세스와 똑같은 코드 실행 자식 프로세스에게 새로운 프로그램을 시키려면 어떻게 하 여야 할까? 프로세스 내의 프로그램을 새 프로그램으로 대치 exec() 시스템 호출 사용 보통 fork() 후에 exec( )

프로그램 실행: exec() 프로세스가 exec() 호출을 하면, 새 프로그램의 main()부터 실행이 시작한다 그 프로세스 내의 프로그램은 완전히 새로운 프로그램으로 대치 자기대치(自己代置) 새 프로그램의 main()부터 실행이 시작한다

프로그램 실행: exec() exec() 호출이 성공하면 리턴할 곳이 없어진다 성공한 exec() 호출은 절대 리턴하지 않는다 #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을 리턴한다.

fork/exec 보통 fork() 호출 후에 exec() 호출 exec() 호출이 성공하면 새로 실행할 프로그램에 대한 정보를 arguments로 전달한다 . exec() 호출이 성공하면 자식 프로세스는 새로운 프로그램을 실행하게 되고 부모는 계속해서 다음 코드를 실행하게 된다 if ((pid = fork()) == 0 ){ exec( arguments ); exit(1); } // 부모 계속 실행

fork와 exec호출의 조합 PC A BEFORE FORK AFTER FORK PC PC A B AFTER FORK pid = fork(); PC A BEFORE FORK AFTER FORK wait((int*)0); execl(“/bin/ls” …); PC PC A B AFTER FORK AFTER EXEC wait((int*)0); /* first line of ls */ PC PC A B (now runs ls)

execute1.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", NULL); fprintf(stderr, "자식 프로세스 실패\n"); exit(1); } printf("부모 프로세스 끝\n");

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

execute2.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); } execl("/bin/date", "date", NULL); fprintf(stderr,"두 번째 실패"); exit(2); execl("/bin/ls","ls", "-l", NULL); fprintf(stderr,"세 번째 실패"); exit(3); printf("부모 프로세스 끝\n");

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>>8, status & 0x00FF); } #include <stdio.h> #include <stdlib.h> main () { printf ("I'm going to exit with return code\n"); exit (33); } $ gcc –o execute3 execute3.c $ execute3 ls –l $ execute3 myexit Q: Why &argv[1] ?

Why &argv[1] ? 3 argc: 0xbffff6b4 0xbffff81d argv: execute3\0 ls\0 $ gcc –g –o execute3 execute3.c $ gdb execute3 (gdb) b 6 Breakpoint 1 at 0x80484dd: file execute3.c, line 6. (gdb) b 13 Breakpoint 2 at 0x8048559: file execute3.c, line 13. (gdb) r ls -l Starting program: /home/mysung/ulprog/execute3 ls -l Breakpoint 1, main (argc=3, argv=0xbffff6b4) at execute3.c:6 6 pid = fork( ); (gdb) p argc $1 = 3 (gdb) p argv $2 = (char **) 0xbffff6b4 (gdb) p argv[0] $3 = 0xbffff800 "/home/mysung/ulprog/execute3" (gdb) p argv[1] $4 = 0xbffff81d "ls" (gdb) p argv[2] $5 = 0xbffff820 "-l" (gdb) p argv[3] $6 = 0x0 (gdb) p &argv[0] $7 = (char **) 0xbffff6b4 (gdb) p &argv[1] $8 = (char **) 0xbffff6b8 (gdb) p &argv[2] $9 = (char **) 0xbffff6bc (gdb) p &argv[3] $10 = (char **) 0xbffff6c0 argv: 0xbffff6b4 1 2 3 0xbffff800 execute3\0 0xbffff81d ls\0 0xbffff820 -l\0 (gdb) c Continuing. Detaching after fork from child process 11041. … ls –l … [parent: 11040] 자식 프로세스 11041 종료 Breakpoint 2, main (argc=3, argv=0xbffff6b4) at execute3.c:13 13 printf("[child : %d] 종료 코드 상위: %d 하위: %d \n", child, status>>8, status & 0x00FF); (gdb) p status $11 = 0 (gdb) p status >> 8 $12 = 0 (gdb) p status & 0xFF $13 = 0 [child : 11041] 종료 코드 상위: 0 하위: 0 [Inferior 1 (process 11040) exited with code 053]

(응용) execute33.c: 자식 프로세스 kill #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>>8, status & 0x00FF); } $ gcc –o execute3k execute3k.c $ execute3k sleep 100

run.c ① ② ③ ④ ⑤ argc: argv: run\0 ls\0 -l\0 #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>>8, status & 0x00FF); } argc: ① argv: ② 1 2 3 ③ run\0 ④ ls\0 ⑤ -l\0

$ gdb run 3 argc: 0xbffff6a4 0xbffff81d argv: run\0 ls\0 -l\0 1 2 3 $ gcc run.c -o run -g $ gdb run (gdb) b 8 Breakpoint 1 at 0x804850d: file run.c, line 8. (gdb) b 16 Breakpoint 2 at 0x804859d: file run.c, line 16. (gdb) r ls -l Starting program: /home/mysung/run ls -l Breakpoint 1, main (argc=3, argv=0xbffff6a4) at run.c:8 8 pid = fork( ); (gdb) p argc $1 = 3 (gdb) p argv $2 = (char **) 0xbffff6a4 (gdb) p argv[0] $3 = 0xbffff7f9 "/home/mysung/run" (gdb) p argv[1] $4 = 0xbffff80a "ls" (gdb) p argv[2] $5 = 0xbffff80d "-l" (gdb) p argv[3] $6 = 0x0 (gdb) p &argv[0] $7 = (char **) 0xbffff6a4 (gdb) p &argv[1] $8 = (char **) 0xbffff6a8 (gdb) p &argv[2] $9 = (char **) 0xbffff6ac (gdb) p &argv[3] $10 = (char **) 0xbffff6b0 argc: 3 0xbffff6a4 0xbffff81d argv: 0xbffff6a4 1 2 3 0xbffff7f9 run\0 0xbffff80a ls\0 0xbffff80d -l\0 (gdb) c Continuing. Detaching after fork from child process 10672. [parent: 10522] 자식 프로세스 10672 종료 Breakpoint 2, main (argc=3, argv=0xbffff6a4) at run.c:16 16 printf("[child : %d] 종료 코드 상위: %d 하위: %d \n", child, status>>8, status & 0x00FF); (gdb) p status $11 = 9 (gdb) p status >> 8 $12 = 0 (gdb) p status & 0xFF $13 = 9 [child : 10672] 종료 코드 상위: 0 하위: 9 [Inferior 1 (process 10522) exited with code 053]

12.5 입출력 재지정

입출력 재지정 fd1 파일 fd2 fd3 명령어의 표준 출력이 파일에 저장 출력 재지정 기능 구현 $ 명령어 > 파일 출력 재지정 기능 구현 dup() 혹은 dup2() 시스템 호출 파일 디스크립터 fd를 표준출력(1)에 dup2() fd = open(argv[1], O_CREAT|O_TRUNC|O_WRONLY, 0600); dup2(fd, 1); fd1 fd2 fd3 파일 #include <unistd.h> int dup(int oldfd); int dup2(int oldfd, int newfd); 성공하면 복사된 새로운 파일 디스크립터를 리턴, 실패하면 -1을 리턴

(참고) inode (index node) 구조 Fedora 15 /usr/include/linux/ext2_fs.h struct ext2_inode /* ext2_fs.h 209행 */ /* 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행

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"); } $ gcc –o redirect 1 redirect1.c $ redirect1 xx

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); } $ gcc –o redirect 2 redirect2.c $ redirect2 xx ls –l  “>” redirect out 원리

기타 시스템 호출 #include <unistd.h> int chdir (char* pathname); #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로 변경한다.

12.6 시스템 부팅

시스템 부팅 시스템 부팅은 fork/exec 시스템 호출을 통해 이루어진다 $ ps –ef | more

시스템 부팅 swapper(스케줄러 프로세스) init(초기화 프로세스) getty 프로세스 login 프로세스 커널 내부에서 만들어진 프로세스로 프로세스 스케줄링을 한다 init(초기화 프로세스) /etc/inittab 파일에 기술된 대로 시스템을 초기화 getty 프로세스 로그인 프롬프트를 내고 키보드 입력을 감지한다 login 프로세스 사용자의 로그인 아이디 및 패스워드를 검사 shell 프로세스 시작 파일을 실행한 후에 쉘 프롬프트를 내고 사용자로부터 명 령어를 기다린다

12.7 시그널

시그널 종류 시그널은 예기치 않은 사건이 발생할 때 이를 알리는 소프트웨어 인터럽트이다 시그널은 예기치 않은 사건이 발생할 때 이를 알리는 소프트웨어 인터럽트이다 총 31개의 유저영역 시그널이 /usr/include/asm/signal.h에 정의 각 시그널 이름은 SIG로 시작되며 교재 표 12.1과 같다 $ kill –l $ stty -a 시그널 발생 예 SIGFPE 부동소수점 오류 SIGPWR 정전 SIGALRM 알람시계 울림 SIGCHLD 자식 프로세스 종료 SIGINT 키보드로부터 종료 요청 (Ctrl-C) SIGCONT 키보드로부터 정지 요청 (Ctrl-Z)

alarm.c #include <stdio.h> /* 알람 시그널을 보여주는 프로그램 */ int main( ) { alarm(6); printf("무한 루프 \n"); while (1) { sleep(1); printf("1초 경과 \n"); } printf("실행되지 않음 \n");

시그널 처리 시그널에 대한 처리함수 지정 signal() 시스템 호출 “이 시그널이 발생하면 이렇게 처리하라” func은 SIG_IGN, SIG_DFL 혹은 사용자 정의 함수 이름 #include <signal.h> signal(int signo, void (*func)( ))) signo에 대한 처리 함수를 func으로 지정한다. 기존의 처리함수를 리턴한다.

alarmhandler.c  signal(SIGALRM, SIG_IGN);  signal(SIGALRM, SIG_DFL); #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);  signal(SIGALRM, SIG_IGN);  signal(SIGALRM, SIG_DFL);

inthandler.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); #include <signal.h> pause() 시그널을 받을 때까지 해당 프로세스를 잠들게 만든다  (응용) SIGQUIT

제한 시간 명령어 실행 tlimit.c 프로그램 프로그램 설명 명령줄 인수로 받은 명령어를 제한 시간 내에 실행 execute3.c 프로그램을 알람 시그널을 이용하여 확장 프로그램 설명 자식 프로세스가 명령어를 실행하는 동안 정해진 시간이 초과되 면 SIGALRM 시그널이 발생 SIGALRM 시그널에 대한 처리함수 alarmHandler()에서 자식 프로 세스를 강제 종료 kill(pid,SIGINT) 호출을 통해 자식 프로세스에 SIGINT 시그널 을 보내어 강제 종료 만약 SIGALRM 시그널이 발생하기 전에 자식 프로세스가 종료하 면 이 프로그램은 정상적으로 끝남

tlimit.c $ gcc –o tlimit tlimit.c $ tlimit 5 sleep 100 #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); } $ gcc –o tlimit tlimit.c $ tlimit 5 sleep 100

시그널 보내기: kill() kill() 시스템 호출 특정 프로세스 pid에 원하는 임의의 시그널 signo를 보낸다 #include <sys/types.h> #include <signal.h> int kill(int pid, int signo); 프로세스 pid에 시그널 signo를 보낸다. 성공하면 0 실패하면 -1를 리턴한다.

control.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); kill(pid2, SIGSTOP); kill(pid2, SIGCONT); kill(pid1, SIGKILL); kill(pid2, SIGKILL);

핵심 개념 프로세스는 실행중인 프로그램이다 fork() 시스템 호출은 부모 프로세스를 똑같이 복제하여 새로운 자식 프로세스를 생성한다 exec() 시스템 호출은 프로세스 내의 프로그램을 새로운 프로그램 으로 대치하여 새로운 프로그램을 실행시킨다 시스템 부팅은 fork/exec 시스템 호출을 통해 이루어진다 시그널은 예기치 않은 사건이 발생할 때 이를 알리는 소프트웨어 인터럽트이다