본문 바로가기
컴퓨터 기초/운영체제 실습

[운영체제 실습] 10.프로세스 생성과 실행

by 인생여희 2020. 6. 28.

#.프로세스 생성 - system 함수 사용하기

 

예제

#include <stdlib.h>
#include <stdio.h>
/*
system 함수는 프로그램 안에서 새로운 프로그램을 실행하는 가장 간단한 방법이다.
그러나 system 함수는 명령을 실행하기 위해 쉘까지 동작시키므로 비효율적이다.
system 함수는 기존 명령이나 실행 파일명을 인자로 받아 쉘에 전달한다.
쉘은 내부적으로 새 프로세스를 생성해 인자로 받은 명령을 실행하고,
해당 명령의 실행이 끝날 때까지 기다렸다가 종료 상태를 리턴한다.
*/
int main(void) {
int a;
//파이프로 연결된 아래 명령을 실행하다록 system 함수를 호출한다.
//인자로 전달된 명령은 현재 실행 중인 프로세스들에서 han을 포함한 내용을 찾아 han.txt에 저장한다.
a = system("ps -ef | grep han > han.txt");
printf("Return Value : %d\n", a);
return 0;
}
view raw ex6_1.c hosted with ❤ by GitHub

결과

system 함수로 실행한 명령인 ps -ef grep han > han.txt 가 실행되고 있다.

 

 

#.fork 함수 특징

1.fork 함수를 호출하면,

2. 새로운 프로세스를 생성한다.

3. fork 함수로 생성한 자식 프로세스의 메모리 공간은 부모 프로세스의 메모리 공간을 그대로 복사해 만든다.

4.fork 함수는 부모 프로세스에는 자식 프로세스 pid를 리턴하고, 자식 프로세스에는 0을 리턴한다.

 

#.자식프로세스가 상속 받는 대표적인 속성

1.실제 사용자 id

2.유효 사용자 id

3.실제그룹 id

4.유효 그룹 id

5.환경변수

6.열린 파일 기술자

7.시그널 처리 설정

8.현재 작업 디렉토리

 

 

#.프로세스 생성 - fork 함수 사용하기

 

예제

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/*
유닉스에서 프로세스를 생성해 프로그램을 실행하는 대표적인 방법은
fork 함수를 사용하는 것이다.
fork 함수는 수행을 성공하면 부모프로세스에는 자식 프로세스의 id,
자식 프로세스에는 0을 리턴한다. 실패하면 -1 리턴
*/
int main(void) {
pid_t pid;
//fork 함수 호출
switch (pid = fork()) {
case -1 : /* 리턴값이 -1이면 에러 */
perror("fork");
exit(1);
break;
case 0 : /* 리턴값이 0이면 자식 프로세스에 리턴 - 이 라인은 자식 프로세스에서 실행됨*/
//자신의 pid와 부모프로세스의 id를 검색해서 리턴
printf("Child Process - My PID:%d, My Parent's PID:%d\n",
(int)getpid(), (int)getppid());
break;
default : /* 리턴값이 -1이나 0이 아니면 부모프로세스에 리턴한 것임. */
printf("Parent process - My PID:%d, My Parent's PID:%d, "
"My Child's PID:%d\n", (int)getpid(), (int)getppid(), (int)pid);
break;
}
//자식 프로세스와 부모 프로세스가 각각 실행하므로 두번 출력된다.
printf("End of fork\n");
return 0;
}
view raw ex6_2.c hosted with ❤ by GitHub

결과

부모프로세스는 fork함수의 리턴값으로 자식 프로세스의 id를 받는데, 이를 출력하면 자식 프로세스가 출력한 pid와 같음을 알 수 있다.

 

 

#.프로세스 종료

유닉스는 프로세스가 종료되면 해당 프로세스가 어떻게 종료되었는지를 나타내는 종료 상태를 저장한다. 부모프로세스는 저장된 종료 상태 정보를 사용해 자식 프로세스가 어떻게 종료되었는지 알 수 있다. 자식 프로세스는 부모 프로세스에 자신이 어떻게 종료되었는지 알리는 종료 상태값을 리턴할 수 있다. 

 

_exit 함수는 일반적으로 프로그램에서 직접 사용하지 않고, exit 함수에서 내부적으로 호출 한다. _exit 함수는 시스템 호출로, 프로세스를 종료할 때 다음과 같은 과정을 통해 시스템 관련 자원을 정리 한다.

1.모든 파일 기술자를 닫는다.

2.부모 프로세스에 종료 상태를 알린다.

3.자식 프로세스들에 sighup 시그널을 보낸다.

4.부모 프로세스에 sigchild 시그널을 보낸다.

5.프로세스 간 통신에 사용한 자원을 반납한다.

 

#.exit 와 atexit 함수 사용하기

 

예제

#include <stdlib.h>
#include <stdio.h>
/*
exit 함수는 프로세스를 종료 시키고, 부모 프로세스에 종료 상태값을 전달한다.
이때, atexit 함수로 예약한 함수들을 지정된 순서와 역순으로 모두 실행한다.
만일 atexit 함수로 예약한 함수가 수행 도중에 문제가 발생해 리턴하지 못할 경우
exit 함수의 나머지 과정도 실행되지 않는다.
exit 함수는 프로세스가 사용중이던 모든 표준 입출력 스트림에 데이터가 남아 있으면 이를 모두 기록하고,
열려 있는 스트림을 모두 닫는다.
그 다음 tmpfile 함수로 생성한 임시 파일을 모두 삭제하고, _exit 함수를 호출 한다.
_exit 함수는 시스템 호출로, 프로세스가 사용하던 모든 자원을 반납한다.
*/
void cleanup1(void) {
printf("Cleanup 1 is called.\n");
}
void cleanup2(void) {
printf("Cleanup 2 is called.\n");
}
int main(void) {
//프로세스가 종료할 때 수행할 기능을 예약한다.
//인자로는 함수의 포인터를 받는다.
//atexit의 인자로 지정하는 함수는 인자와 리턴값이 없는 함수다.
atexit(cleanup1);
atexit(cleanup2);
exit(0);
}
view raw ex6_3.c hosted with ❤ by GitHub

결과

위에서 프로세스 종료시 수행할 작업을 지정하는 함수를 작성했는데, 실제로는 프로세스를 종료할때 반드시 수행해야 할 작업을 실행해야 한다. 예를들어, 로그를 남기거나, 관리자에게 메시지를 보내거나 데이터를 저장하는 등의 작업을 할 수 있다.

 

 

#.exec 함수군 활용

exec 함수군을 사용해 명령이나 실행파일을 실행할 수 있다. exec 함수군은 인자로 받은 다른 프로그램을 자신을 호출한 프로세스의 메모리에 덮어쓴다. 따라서 프로세스가 수행 중이던 기존 프로그램은 중지되어 없어지고 새로 덮어쓴 프로그램이 실행된다. exec 함수군은 path 나 file에 지정한 명령이나 실행 파일을 실행한다.

 

#.execlp 함수 사용하기

 

예제

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/*
exec 함수군을 이용해서 새로운 프로세스를 실행하는 방법.
헤더 unistd.h
형태 int execlp( const char *file, const char *arg, ...);
인수 char *file (실행할 파일 이름)
const char *arg (인수 목록)
반환 int 실패일 때만 -1
*/
int main(void) {
printf("--> Before exec function\n");
//execlp 함수를 사용해 현재 프로그램을 다른 명령 (ls -a)으로 바꿔서 실행
//인수 중복 이유
//이는 프로그램을 실행하면 첫번째 인수가 실행한 프로그램의 전체 이름이기 때문입니다.
if (execlp("ls", "ls", "-a", (char *)NULL) == -1) {
perror("execlp");
exit(1);
}
//위에서 execlp 함수를 만나 프로세스의 이미지가 ls 명령의 이미지로 바뀌었으므로 13행은 실행되지 않는다.
printf("--> After exec function\n");
return 0;
}
view raw ex6_4.c hosted with ❤ by GitHub

결과

 

 

#.execv 함수 사용하기

 

예제

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
//포인터 배열을 선언한다.
char *argv[3];
printf("Before exec function\n");
//포인터 배열의 각 요소에 값을 저장한다.
//0번째는 관례에 따라 실행파일명을 저장하고, 1번째에 옵션을 저장한다.
//더 이상 옵션이나 인자가 없으므로 argc[2]에는 null 을 지정한다.
argv[0] = "ls";
argv[1] = "-a";
argv[2] = NULL;
//execv 함수로 명령의 경로와 포인터 배열 argv를 인자로 지정한다.
//ls의 위치는 whereis 명령어로 확인가능!
if (execv("/bin/ls", argv) == -1) {
perror("execv");
exit(1);
}
printf("After exec function\n");
return 0;
}
view raw ex6_5.c hosted with ❤ by GitHub

결과

 

 

#execve 함수 사용하기

 

예제

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/*
argv 와 envp 인자를 모두 지정해 execve 함수를 호출한다.
이 값들이 제대로 전달되었는지 확인하기 위해 ex6_6.out 파일을 별도록 작성한다.
ex6_6.out은 인자로 받은 argv와 envp 값을 출력한다.
*/
int main(void) {
char *argv[3];
char *envp[2];
printf("Before exec function\n");
argv[0] = "ex6_6.out";
argv[1] = "100";
argv[2] = NULL;
envp[0] = "MYENV=hanbit";
envp[1] = NULL;
if (execve("./ex6_6.out", argv, envp) == -1) {
perror("execve");
exit(1);
}
printf("After exec function\n");
return 0;
}
view raw ex6_6.c hosted with ❤ by GitHub

 

#include <stdio.h>
int main(int argc, char **argv, char **envp) {
int n;
char **env;
printf("\n--> In ex6_6.out Main\n");
printf("argc = %d\n", argc);
for (n = 0; n < argc; n++)
printf("argv[%d] = %s\n", n, argv[n]);
env = envp;
while (*env) {
printf("%s\n", *env);
env++;
}
return 0;
}
view raw ex6_6.out hosted with ❤ by GitHub

결과

 

 

 

#.exec 함수군과 fork 함수 

 fork 함수로 생성한 자식 프로세스에서도 부모 프로세스와 같은 코드를 수행했다.

 case 문으로 부모 프로세스와 자식 프로세스가 수행할 코드를 분리하기는 했지만,

 기본적으로 같은 프로그램이다.

 

 그러나 자식 프로세스에서 exec 함수군을 호출 하면 자식 프로세스는 부모 프로세스로부터

 복사한 프로그램과는 다른 명령이나 프로그램을 실행할 수 있다.

 예를들어 쉘에서 어떤 명령이나 파일을 실행하면, 쉘은 fork 함수로 자식 프로세스를 만들고,

 사용자가 입력한 명령이나 파일을 exec 함수군을 사용해 자식 프로세스에서 실행한다.

 이렇게 부모 프로세스와 자식 프로세스가 각기 다른 작업을 수행해야 할 때 fork 와 exec 함수를 같이 사용해야 한다.

 

 

#.exec 함수군과 fork 함수 사용하기

 

예제

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
pid_t pid;
switch (pid = fork()) {
case -1 : /* fork failed */
perror("fork");
exit(1);
break;
case 0 : /* child process */
printf("--> Child Process\n");
//자식 프로세스가 수행할 부분으로 , execlp 함수를 호출한다.
//이렇게 하면 자식 프로세스가 가지고 있던, 부모프로세스의 이미지를 ls 명령의 이미지가 덮어쓴다.
//즉, 부모 프로세스와 자식 프로세스는 각기 다른 프로그램을 실행한다.
if (execlp("ls", "ls", "-a", (char *)NULL) == -1) {
perror("execlp");
exit(1);
}
exit(0);
break;
default : /* parent process */
printf("--> Parent process - My PID:%d\n", (int)getpid());
break;
}
return 0;
}
view raw ex6_7.c hosted with ❤ by GitHub

결과

실행결과를 보면 자식 프로세스는 ls 명령을 수행했음을 알 수 있다.

 

 

#.프로세스 동기화

정상적인 프로세스 종료 과정이라면, 자식 프로세스가 종료를 위해 부모 프로세스에 종료 상태 정보를 보내고,

부모 프로세스는 이 정보를 받으면, 프로세스 테이블에서 자식 프로세스를 삭제해야 한다.

그러나 자식 프로세스가 모든 자원을 반납했어도 부모 프로세스가 종료 상태 정보를 얻어가지 않거나 자식 프로세스보다 먼저 종료 하는 경우 발생한다.

 

 실행을 종료한 후 자원을 반납한 자식 프로세스의 종료 상태 정보를 부모 프로세스가 얻어가지 않는 경우에는

 좀비 프로세스가 발생한다. 좀비 프로세스는 프로세스 테이블에만 존재한다.

 좀비 프로세스는 일반적인 방법으로 제거할 수 없으며, 부모 프로세스가 wait 관련 함수를 호출해야 사라진다.

 

 

#.wait 함수 사용하기

 

예제

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/*
자식 프로세스의 실행이 완전히 끝나기를 기다렸다가 종료 상태를 확인하는 함수는 wait, waitpid 다.
wait 함수는 자식 프로세스가 종료할 때 까지 부모프로세스를 기다리게 한다.
자식프로세스의 종료 상태는 stat_loc 에 지정한 주소에 저장된다.
만약 부모 프로세스가 wait 함수를 호출하기 전에 자식 프로세스가 종료하면 wait 함수는 즉시 리턴한다.
wait 함수의 리턴값은 자식 프로세스의 id 다.
wait 함수의 리턴 값이 -1 이면 살아있는 자식 프로세스가 하나도 없다는 의미다.
*/
int main(void) {
int status;
pid_t pid;
//fork 함수를 호출해 새로운 프로세스를 생성한다.
switch (pid = fork()) {
case -1 : /* fork failed */
perror("fork");
exit(1);
break;
case 0 : /* child process */
//자식 프로세스는 출력을 하나 하고 바로 exit 함수를 호출해서 종료한다.
printf("--> Child Process\n");
//종료 상태 값으로 2 지정
exit(2);
break;
default : /* parent process */
//wait 함수를 사용해 자식 프로세스가 종료할때 까지 부모프로세스를 기다리게 하기
while (wait(&status) != pid)
continue;
printf("--> Parent process\n");
printf("Status: %d, %x\n", status, status);
printf("Child process Exit Status:%d\n", status >> 8);
break;
}
return 0;
}
view raw ex6_8.c hosted with ❤ by GitHub

결과

결과는 512(10진수), 0x200(16진수) 로 출력됨을 알 수 있다. 자식 프로세스가 전달한 값은 부모 프로세스에 왼쪽으로 한 바이트 이동해 전달된다. 이를 제대로 출력하려면 오른쪽으로 8비트 이동시켜야 한다. 

 

 

#.waitpid 함수 사용하기

 

예제

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/*
wait 함수는 자식 프로세스가 여러개일 경우 아무 자식 프로세스나 종료하면 리턴한다.
이와 달리 waitpid 함수는 특정 pid의 자식 프로세스가 종료하기를 기다린다.
waitpid 함수는 pid로 지정한 자식 프로세스가 종료하기를 기다리며, 자식 프로세스의 종료상태 값을
stat_loc에 저장하고, options의 조건에 따라 리턴한다.
*/
int main(void) {
int status;
pid_t pid;
if ((pid = fork()) < 0) { /* fork failed */
perror("fork");
exit(1);
}
if (pid == 0) { /* child process */
printf("--> Child process\n");
//자식 프로세스에서 곧바로 종료하지 않고, 시간을 지연하도록 한다.
sleep(3);
exit(3);
}
printf("--> Parent process\n");
//부모 프로세스는 자식 프로세스가 종료하기를 기다린다.
//이때 WNOHANG 옵션을 지정했으므로, waitpid 함수는 블록되지 않고, 아래 행을 반복적으로 실행한다.
while (waitpid(pid, &status, WNOHANG) == 0) {
printf("Parent still wait...\n");
sleep(1);
}
//자식프로세스의 종료 상태값 3 출력
printf("Child Exit Status : %d\n", status>>8);
return 0;
}
/*
참고
wiatpid 함수의 첫 번째 인자
pid가 -1 일 경우 (pid == -1) : 임의의 자식 프로세스를 기다림
pid가 0 보다 클 경우 (pid > 0) : 프로세스 ID가 pid인 자식 프로세스를 기다림
pid가 -1 보다 작을 경우 (pid < -1) : 프로세스 그룹 ID가 pid의 절댓값과 같은 자식 프로세스를 기다림
pid가 0일 경우 (pid == 0) : waitpid를 호출한 프로세스의 프로세스 그룹 PID와 같은 프로세스 그룹 ID를 가진 프로세스를 기다림
* waitpid 함수의 오류(-1)는 지정한 pid의 프로세스 또는 프로세스 그룹이 없는 경우에 발생하며 그리고 pid가 자식 프로세스가 아닐 때 발생.
세 번째 인자로 사용가능한 상수
WCONTINUED : 중단 되었다가 재개된 자식 프로세스의 상태를 받음
WNOHANG : 기다리는 PID가 종료되지 않아서 즉시 종료 상태를 회수 할 수 없는 상황에서 호출자는 차단되지 않고 반환값으로 0을 받음
WUNTRACED : 중단된 자식 프로세스의 상태를 받음
*/
view raw ex6_9.c hosted with ❤ by GitHub

결과

 

 

 

예제파일

ch06.zip
0.01MB

 

 

 

 

 

 

======================= 실습 2 ======================= 

 

#.프로세스 실행

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]){
printf("start! \n");
//execl 함수는 프로그램을 실행시켜서 새로운 프로세스를 실행하면, 현재의 프로세스 이미지를 덮어써 버린다.
execl("/bin/ls" , "ls" , "-al", NULL);
printf("end! \n"); //실행되지 않는다.
exit(0);
}
// execl 함수는 프로세스를 덮어써 버리기 때문에, 쉘과 같은 프로그램의 제작이 불가능하다. 어떻게 쉘 같은 프로그램을 만들 수 있을까 ?
//힌트 : fork는 원본프로그램의 복사판을 만드는 함수 사용
/*
fork를 해서 원본프로세스의 복사본을 만들고, 여기에서 execl을 이용해서 새로운 프로세스를 실행시키는 것이다. 이러한 식으로 프로그램을 생성시키는 가장 대표적인 프로그램이 바로 shell 프로그램이다. shell 에서 ls 프로그램을 실행시키면, 다시 shell로 되돌아오는 것을 확인할 수 있을 것이다. 이는 shell 이 ls 명령을 받으면 fork함수를 이용해서 자식 프로세스를 만들고, 이 자식 프로세스에서 execl을 이용해서 ls를 실행시키기 때문에 가능해진다.
이렇게 fork & execl 을 이용하면, 진정한 멀티 프로세스 환경이 가능해 지게 된다. 유닉스 운영체제는 fork & execl 을 통해서 생성된 수많은 프로세스를 시분할방식으로 동시에 수행함으로써, 멀티 프로세스 환경을 제공한다.
*/
view raw execTest.c hosted with ❤ by GitHub

#.결과

 printf("end! \n");  이부분이 출력되지 않는다.

 

#.fork 

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]){
int pid;
int i;
i = 1000;
printf("start! \n");
pid = fork();
//오류
if (pid == -1) {
perror("fork error");
exit(1);
//자식 프로세스
}else if(pid == 0){
printf("자식 : 나의 pid 는 %d 입니다. \n" , getpid());
while (1) {
printf("(자)--> %d\n" , i);
i++;
sleep(1);
}//while - end
}else{
printf("나를 복사한(자식) PID는 %d 입니다. \n" , pid);
printf("나의 pid 는 %d 입니다. \n" , getpid());
while (1) {
printf("(부)==> %d\n" , i);
i += 4;
sleep(1);
}//while - end
}
printf("end! \n"); //실행되지 않는다.
exit(0);
}
/*
부모프로세스의 정보들, 열려있는 파일, signal정보, 메모리에 있는 많은 정보들이 자식프로세스에게 복사된다.
fork함수가 성공적으로 수행되어서 자식 프로세스가 생성되면,
부모프로세스에게는 새로 생성된 자식프로세스의 PID가 리턴이 되고, 자식프로세스에게는 0이 리턴된다.
부모프로세스와 자식프로세스가 동시에 주어진 코드를 실행시키는 것을 확인할 수 있을 것이다.
ps 를 이용하면 이들 프로세스와의 관계를 명확하게 확인할 수 있다.
$ ps -ef | grep forktest
*/
view raw forkTest.c hosted with ❤ by GitHub

#.결과

 

 

#.fork와 exec를 이용해서 새로운 프로세스 생성

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#define chop(str) str[strlen(str)-1] = 0x00;
int main (int argc, char *argv[]){
char buf[256];
printf("my shell\n");
int pid;
while (1) {
//사용자 입력 기다림
printf("#");
fgets(buf,255, stdin);
chop(buf);
//입력이 quit면 종료
if (strncmp(buf,"quit",4) == 0) {
printf("quit 입력 - 종료 \n");
exit(0);
}
printf("buf : %s\n" , buf);
printf("access(buf, X_OK) : %d \n" , access(buf, X_OK));
//입력한 명령이 실행가능하면 fork 후, execl 실행
if (access(buf, X_OK) == 0) {
//fork 실행 - 현재 프로세스 복사
pid = fork();
//fork - 에러
if (pid < 0) {
fprintf(stderr, "fork error");
exit(1);
}
//자식 프로세스
if (pid == 0 ) {
sleep(5);
printf("나는 자식 프로세스 \n");
//execl 실행 - 자식프로세스에서 특정 명령 수행
if (execl(buf,buf,NULL) == -1) {
fprintf(stderr, "command exec error\n\n");
exit(1);
}
}
//부모프로세스
if (pid > 0) {
//부모프로세스는 자식이 종료되길 기다린다.
sleep(2);
int status;
printf("부모 프로세스 대기\n");
waitpid(pid, &status, WUNTRACED);
printf("부모 프로세스 종료\n"); //자식 프로세스가 종료되면 종료.
}
}else{
//실행가능한 프로그램이 아니면 에러 출력
fprintf(stderr, "command not found \n\n");
exit(1);
}
}
}
/*
init가 모든 프로세스의 조상이 되는 프로세스다.
모든 프로세스는 init로 부터 fork & exec 되어서 생성이 된다.
pstree명령을 이용하면, 프로세스의 관계를 확인할 수 있다.
*/
view raw myshell.c hosted with ❤ by GitHub

#.결과

부모프로세스는 자식프로세스가 종료되길 기다렸다가 종료된다.

 

#.고아 프로세스

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]){
int pid;
int i;
i = 1000;
printf("start! \n");
pid = fork();
//오류
if (pid == -1) {
perror("fork error");
exit(1);
//자식 프로세스
}else if(pid == 0){
printf("자식 : 나의 pid 는 %d 입니다. \n" , getpid());
while (1) {
printf("(자)--> %d\n" , i);
i++;
sleep(1);
}//while - end
}else{
printf("나를 복사한(자식) PID는 %d 입니다. \n" , pid);
printf("나의 pid 는 %d 입니다. \n" , getpid());
sleep(1);
printf("T.T 나죽네\n");
exit(0);
}
printf("end! \n"); //실행되지 않는다.
exit(0);
}
/*
부모프로세스가 종료되면 자식 프로세스도 따라서 종료되어버리거나 부모로부터 버려진 고아가 되는 등의 영향을 받는다.
또한 부모프로세스는 자식프로세스를 종료시킬 수 있으며, 아예 분가시켜버릴 수도 있다.
고아가된 프로세스를 init 프로세스가 관리한다.
*/
/*
결과
쉘과는 따로 자식프로세스가 계속 실행되는걸 알 수 있을 것이다.
이제 Ctrl+C 를 눌러보자. Ctrl+C를 누르면 일반적으로 프로세스는 종료가 된다는 것을 경험적으로 알고 있을 것이다.
- 정확히 말하자면 SIGINT라는 시그널이 전달되고, 이에 대한 반응으로 프로세스가 죽는다. 시그널은 나중에 다룰 것이다 -. 그러나 Ctrl+C를 아무리 눌러도 자식프로세스가 죽지 않는걸 알 수 있을 것이다. 왜냐하면, bash 의 자식의 자식 프로세스, 즉 같은 그룹에 속하지 않은 전혀 다른 그룹의 프로세스가 되었기 때문이다. ps 결과로 확인해 보도록 하자.
#ps -efjc | grep forkTest2
*/
view raw forkTest2.c hosted with ❤ by GitHub

#.결과

 

#.데몬 프로세스

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]){
int pid;
int i;
i = 1000;
printf("start! \n");
pid = fork();
//오류
if (pid == -1) {
perror("fork error");
exit(1);
//자식 프로세스
}else if(pid == 0){
printf("자식 : 나의 pid 는 %d 입니다. \n" , getpid());
//표준입력,표준출력,표준에러를 닫는다.
close(0);
close(1);
close(2);
//setsid()를 이용해서, 사용자환경에서 독립된 자신의 환경을 만든다.
//기존의 환경이 리셋되면서 터미널이 사라진다.
//또한 새로운 터미널을 지정하지 않았기 때문에, 이 프로세스는 결과적으로 터미널을 가지지 않게 된다.
setsid();
while (1) {
printf("(자)--> %d\n" , i);
i++;
sleep(1);
}//while - end
}else{
printf("나를 복사한(자식) PID는 %d 입니다. \n" , pid);
printf("나의 pid 는 %d 입니다. \n" , getpid());
sleep(1);
printf("나죽네..ㅜㅜ");
//부모프로세스를 종료한다
exit(0);
}
exit(0);
}
/*
현재 화면과 프로세스에서 떨어져 나가서 독립적으로 실행되는 프로세스를 데몬 프로세스라고 한다. 가장 대표적인 프로그램이 웹서비스를 위한 웹서버 프로그램일 것이다. 이런 프로그램들은 거의 운영체제가 시작됨과 동시에 시작되어서 운영체제가 끝날때까지 뒤에서 우리가 눈치채지 못하는 상태에서실행이 된다.
데몬프로세스가 되려면 다음과 같은 조건을 갖추어야 한다.
1.일단 고아 프로세스가 되어야 한다.
데몬 프로세스는 완전히 독립된 프로세스다. 그러므로 고아 프로세스가 되어야 한다. 예컨데, 가족으로 부터 독립해서 사회로 나가야 된다는 얘기가 되겠다.
2.표준입력, 표준출력, 표준에러을 닫는다.
3.터미널을 가지지 않는다
*/
view raw demon.c hosted with ❤ by GitHub

#.결과

 

ppid 가 1 이되었고, 터미널이 ?? 이되었다.

 

 

 

 

 

 

 

참고 : 유닉스시스템 프로그래밍(한빛미디어) , Joinc