IT/C

리눅스 환경에서 fork() 함수를 이용한 자식 프로세스 생성하기

김효랑이 2018. 10. 19. 11:33
728x90
반응형

 호야의 블로그 

[C언어] 리눅스 환경에서 fork()함수를 이용한 자식 및 자손 프로세스 생성하기

fork() 함수는 현재 프로세스에 대해 자식 프로세스를 생성하는 함수입니다. 특히 유닉스 환경 서버 측에서 fork() 함수를 필수적으로 사용합니다. 
서버는 하나의 서비스 내에서 여러 개의 클라이언트를 받아내야 합니다. 그래서 서버의 프로세스가 클라이언트의 요청을 받으면 자신이 응답하는 것이 아니라, 자식을 fork() 하여 전달합니다. 간단히 말하면 서버 프로세스가 접속을 받을 때마다 요청을 처리할 프로세스를 만들어 응답하는 것입니다.
이 외에도 멀티태스킹 운영체제에선 동작중인 프로그램 외에도 다른 프로그램을 동작시켜야 할 때가 있습니다. 이를 위해선 새로운 프로세스를 생성해야 하고, 그 방법으로 프로세스 자신을 복제하여 프로그램을 호출합니다.
이를 간단히 구현해 보았는데 저는 자식뿐만 아니라 자식의 자식, 즉 자손 프로세스까지 생성해보겠습니다.


헤더 선언

stdio.h를 추가로 unistd.h를 선언합니다. 

#include <stdio.h>

#include <unistd.h>

unistd.h는 유닉스 계열에서 동작하는 C 컴파일러에 있는 헤더 파일입니다. 윈도우 환경에는 없는 헤더 파일이므로 저와 같은 리눅스 환경을 구성하지 않으시면 제대로 된 결과를 불 수 없습니다. 

이후 메인 함수를 선언하고, pid_t 형식으로 pid라는 변수를 선언합니다. pid가 아니어도 무방합니다.

pid_t pid; //프로세스 ID로 변수 pid를 선언 pid = fork(); //fork함수 실행(실행 실패 시 -1, 부모에게 새로운 pid, 자식 프로세스에는 0이 반환)

변수 PID에 fork() 함수를 실행하고, 반환 값을 PID에 저장합니다. fork 함수가 실패하면 PID에 -1을 반환하며, 부모 프로세스에는 새로운 자식 프로세스 PID가 반환되고, 자식 프로세스에는 0이 반환됩니다.

이를 활용하여 다양한 조건문을 통해 각 프로세스에 다른 연산 혹은 동작을 줄 수 있습니다.

PID의 값이 0보다 큰 경우(부모 프로세스) 동작하는 조건문으로 부모프로세스가 getpid()를 통해 자신의 프로세스 아이디 즉 PID를 출력합니다. 참고로 getppid() 함수로 부모의 PID도 출력이 가능하니 한 번 테스트해보시며 이해하시길 추천합니다.

if(pid>0){ //부모 프로세스 들어감 printf("parent process %d\n",getpid()); //getpid로 현재 프로세스 pID, getppid로 부모 PID 출력 }

아래 코드는 PID가 0일 경우 즉, 자식 프로세스의 동작 조건문으로 getpid()로 자신의 PID를 출력합니다. 

하지만 저의 목적은 자손 프로세스의 생성이므로 여기서 끝나지 않고, 다시 한 번 PID에 fork() 함수를 실행합니다. 이제 자식의 자식 프로세스가 생성되어, 현재의 조건문을 동작하게 됩니다.

즉 현재 상황은 제일 처음 생성된 프로세스 부모 A, 이후 생성된 자식 B, 그 이후 자식이 생성한 자식 C(A의 자손)가 존재합니다.

else if(pid==0){ //자식 프로세스 들어감 printf("child process %d\n",getpid()); pid =fork(); //fork함수 한 번 더 실행(자식이 손자 프로세스를 생성) if(pid>0){ //자식 프로세스 들어감 printf("child process %d\n",getpid()); } else printf("grand child %d\n",getpid()); //pid가 0인 경우(손자가 들어감) }

else if(PID==0) 문 안을 보시면, fork() 이후에 다시 조건문이 생겼습니다. 이는 B와 C의 동작을 위한 코드이며 B가 이제 부모가 되었으므로 PID&gt;0 조건에 부합합니다. else 조건은(else if PID ==0 이어도 같은 동작을 합니다.) C 프로세스가 들어가 자신의 PID를 출력하는 동작을 하고 프로그램은 종료합니다.


아래 코드는 fork() 함수의 실행 실패 시, 즉 PID가 -1인 경우 에러처리 하는 코드와 현재 동작하는 프로세스를 볼 수 있는 시스템 코드로 되어있습니다. grep부분은 아래에 구현 화면에서 자세히 설명해드리겠습니다.

else printf("error\n"); //pid -1인 경우 에러 처리 system("ps | grep (C파일 있는 디렉토리 이름)");


프로그램 코드는 이렇게 끝났습니다. 아래에 전체 코드를 올려드리겠습니다.

#include <stdio.h> #include <unistd.h> int main() { pid_t pid; //프로세스 ID로 변수 pid를 선언 pid = fork(); //fork함수 실행(실행 실패 시 -1, 부모에게 새로운 pid, 자식 프로세스에는 0이 반환) if(pid>0){ //부모 프로세스 들어감 printf("parent process %d\n",getpid()); //getpid로 현재 프로세스 pID, getppid로 부모 PID 출력 } else if(pid==0){ //자식 프로세스 들어감 printf("child process %d\n",getpid()); pid =fork(); //fork함수 한 번 더 실행(자식이 손자 프로세스를 생성) if(pid>0){ //자식 프로세스 들어감 printf("child process %d\n",getpid()); } else printf("grand child %d\n",getpid()); //pid가 0인 경우(손자가 들어감) } else printf("error\n"); //pid -1인 경우 에러 처리 system("ps | grep (C파일 있는 디렉토리 이름)"); return 0; }


구현 및 동작 화면

ps명령어는 현재 동작 중인 프로세스를 보여주는 시스템 명령어로 system() 함수로 코드 내에서 동작하게 하였습니다. 또한, grep 코드로 실행 후 `~~`에 해당하는 문구가 들어간 프로세스만 출력합니다. ps명령어 실행 시에는 주요한 PID, PPID, PGID, COMMAND를 보시면 됩니다. PID와 PPID는 각각 현재, 부모 프로세스이며, PGID는 조상 프로세스입니다. 마지막으로 COMMAND는 현재 동작 중인 프로그램 주소를 나타냅니다.


※ 예) 만약, 실행 프로그램 이름이 'fork_test.exe'일 경우

system("ps | grep fork_test");

'fork_test 프로그램에서 동작 중인 프로세스를 출력해라'라는 뜻이 됩니다.


프로그램 동작 화면을 보시면 먼저 부모 프로세스 21032가 동작하고, 자식 프로세스 12864가 동작합니다. 이후 else로 빠진 후에 시스템 명령어 "ps | grep ~~"을 실행합니다. 이후 자식 프로세스가 fork()를 통해 자손을 만들고 다음 시스템 명령어 ps를 실행합니다. 최종적으로 아래와 비슷한 화면이 출력되는데 PID는 무작위로 생성되어 프로그램을 실행시킬 때마다 다르게 되는 점 참고 바랍니다.


<Cygwin에서 프로그램 동작화면>


참고자료 여기를 클릭(위키백과)


후기 및 정리



fork() 함수 실습으로 프로세스를 생성하는 테스트를 해보았습니다. 저는 자손까지 생성하는 코드를 구현했지만, 여러분들은 한 프로세스로 여러 자식 프로세스를 만드는 코드도 연습해보시면 좋을 것 같습니다. 읽어주셔서 감사합니다.

리눅스 개발 환경이므로 Cygwin에서 개발 및 테스트 한 점 참고 부탁하겠습니다. 다음에 리눅스 환경을 구축하는 방법과 Cygwin 설치에 대한 글도 올리겠습니다.



조금의 도움이 되셨다면 로그인 없이도 가능한

왼쪽 아래 ♥공감 버튼을 꾹 눌러주세요! 



728x90
반응형