You are looking for information, articles, knowledge about the topic nail salons open on sunday near me 리눅스 프로세스 생성 과정 on Google, you do not find the information you need! Here are the best content compiled and compiled by the https://toplist.maxfit.vn team, along with other related topics such as: 리눅스 프로세스 생성 과정 리눅스 프로세스 생성 함수, Fork 함수 개념, Linux wait 명령어, 리눅스 부모 프로세스
리눅스에서 구동되는 프로세스는 크게 유저 레벨에서 생성된 프로세스와 커널 레벨에서 생성된 프로세스가 있습니다. 유저 레벨에서 생성된 프로세스는 유저 공간에서 프로세스를 생성하는 라이브러리(glibc) 도움을 받아 커널에게 프로세스 생성 요청을 합니다.
Linux Kernel/Driver Hacks :: [리눅스커널][프로세스] 프로세스는 어떻게 생성하나?
- Article author: austindhkim.tistory.com
- Reviews from users: 30260 Ratings
- Top rated: 3.7
- Lowest rated: 1
- Summary of article content: Articles about Linux Kernel/Driver Hacks :: [리눅스커널][프로세스] 프로세스는 어떻게 생성하나? Updating …
- Most searched keywords: Whether you are looking for Linux Kernel/Driver Hacks :: [리눅스커널][프로세스] 프로세스는 어떻게 생성하나? Updating 프로세스에 대한 이해를 하려면 프로세스가 어떻게 생성되는 지 알면 좋습니다. 프로세스 생성 과정에서 프로세스를 관리하는 자료구조 관계를 알 수 있기 때문입니다. 리눅스에서 구동되는 프로세스는 크게 유저..
- Table of Contents:
리눅스 프로세스 생성하기
- Article author: velog.io
- Reviews from users: 601 Ratings
- Top rated: 4.4
- Lowest rated: 1
- Summary of article content: Articles about 리눅스 프로세스 생성하기 리눅스에서 프로세스를 생성하는 과정을 알아보기 전에, 프로세스의 기본 … TEXT : 프로세스의 정의나 명령 DATA : 프로그램이 사용하는 정적 변수 … …
- Most searched keywords: Whether you are looking for 리눅스 프로세스 생성하기 리눅스에서 프로세스를 생성하는 과정을 알아보기 전에, 프로세스의 기본 … TEXT : 프로세스의 정의나 명령 DATA : 프로그램이 사용하는 정적 변수 … 리눅스에서 프로세스를 생성하는 과정을 알아보기 전에, 프로세스의 기본 구조를 다시 생각해보자.
프로세스의 기본 구조는 다음과 같다.
TEXT : 프로세스의 정의나 명령
DATA : 프로그램이 사용하는 정적 변수
BSS : 초기화하지 않은 전역 변수나 정적 변수
HE - Table of Contents:
REAKWON :: [리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드
- Article author: reakwon.tistory.com
- Reviews from users: 11673 Ratings
- Top rated: 4.2
- Lowest rated: 1
- Summary of article content: Articles about REAKWON :: [리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드 [리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드. REAKWON 2018. 12. 10. 21:03. 프로세스(process). 프로세스는 간단히 말하자면 실행 중인 프로그램을 … …
- Most searched keywords: Whether you are looking for REAKWON :: [리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드 [리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드. REAKWON 2018. 12. 10. 21:03. 프로세스(process). 프로세스는 간단히 말하자면 실행 중인 프로그램을 … 프로세스(process) 프로세스는 간단히 말하자면 실행 중인 프로그램을 의미합니다. 아마 여러분들은 컴퓨터를 하면서 아주 빈번하게 듣는 용어이기도 합니다. 실행 중인 프로그램이라?? 컴퓨터에서 말하는 실행 중..
- Table of Contents:
티스토리툴바
〔LINUX/UNIX〕프로세스 생성과 실행: pid, fork, exec, pipe
- Article author: love-every-moment.tistory.com
- Reviews from users: 5399 Ratings
- Top rated: 5.0
- Lowest rated: 1
- Summary of article content: Articles about 〔LINUX/UNIX〕프로세스 생성과 실행: pid, fork, exec, pipe 어느 프로세스가 먼저 실행되는지는 알 수 없으며 유닉스 운영체제의 스케쥴링에 따라 처리 순서가 달라진다. fork() 함수 처리 과정: #1. fork() 함수 … …
- Most searched keywords: Whether you are looking for 〔LINUX/UNIX〕프로세스 생성과 실행: pid, fork, exec, pipe 어느 프로세스가 먼저 실행되는지는 알 수 없으며 유닉스 운영체제의 스케쥴링에 따라 처리 순서가 달라진다. fork() 함수 처리 과정: #1. fork() 함수 … 1. 프로세스(Process) (1) 프로세스란? 현재 실행 중인 프로그램 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당 받아 실행중인 것 운영체제의 제어 아래 실행(Running), 대기(Waiting), 중..
- Table of Contents:
Love Every Moment
〔LINUXUNIX〕프로세스 생성과 실행 pid fork exec pipe 본문
티스토리툴바
6. 프로세스 생성과 실행 · UNIXBasic
- Article author: jihooyim1.gitbooks.io
- Reviews from users: 20471 Ratings
- Top rated: 3.4
- Lowest rated: 1
- Summary of article content: Articles about 6. 프로세스 생성과 실행 · UNIXBasic 유닉스 운영체제의 스케쥴링(scheduling)에 따라 처리 순서가 달라진다. fork함수 실행시 처리 과정. fork 함수 호출; 새로운 프로세스(자식 프로세스) 생성; fork함수로 … …
- Most searched keywords: Whether you are looking for 6. 프로세스 생성과 실행 · UNIXBasic 유닉스 운영체제의 스케쥴링(scheduling)에 따라 처리 순서가 달라진다. fork함수 실행시 처리 과정. fork 함수 호출; 새로운 프로세스(자식 프로세스) 생성; fork함수로 …
- Table of Contents:
6-1개요
6-2프로세스 생성
6-3프로세스 종료
6-4 exec 함수군 활용
6-5 프로세스 동기화
[Linux Kernel] 커널 스레드(프로세스) 생성 과정 소스 분석
- Article author: darkengineer.tistory.com
- Reviews from users: 4510 Ratings
- Top rated: 3.6
- Lowest rated: 1
- Summary of article content: Articles about [Linux Kernel] 커널 스레드(프로세스) 생성 과정 소스 분석 보통 유저 프로세스에서 부모 프로세스가 소멸 되면, init 프로세스가 부모 역할을 수행하게 된다. ps -ejH. 프로세스 생성 과정. 크게 두가지로 분류 할 … …
- Most searched keywords: Whether you are looking for [Linux Kernel] 커널 스레드(프로세스) 생성 과정 소스 분석 보통 유저 프로세스에서 부모 프로세스가 소멸 되면, init 프로세스가 부모 역할을 수행하게 된다. ps -ejH. 프로세스 생성 과정. 크게 두가지로 분류 할 … 다크엔지니어 님의 블로그입니다.
- Table of Contents:
커널 스레드 생성 과정 소스 분석
[OS] 프로세스의 생성과 종료
- Article author: pongsoyun.tistory.com
- Reviews from users: 4547 Ratings
- Top rated: 3.8
- Lowest rated: 1
- Summary of article content: Articles about [OS] 프로세스의 생성과 종료 컴퓨터에서 실제 프로세스 생성과정을 보자. BOOTING : OS가 RAM 에 resent; OS가 가장 먼저 첫 process를 만든다. (ex. Linux: init ); 그 init … …
- Most searched keywords: Whether you are looking for [OS] 프로세스의 생성과 종료 컴퓨터에서 실제 프로세스 생성과정을 보자. BOOTING : OS가 RAM 에 resent; OS가 가장 먼저 첫 process를 만든다. (ex. Linux: init ); 그 init … 프로세스 생성과 종료 프로세스 생성 프로세스는 프로세스에 의해 만들어진다. 컴퓨터에서 실제 프로세스 생성과정을 보자. BOOTING : OS가 RAM 에 resident OS가 가장 먼저 첫 process를 만든다. (ex. Linux: in..기록의 다채로움
- Table of Contents:
[운영체제] 프로세스 생성과 소멸
- Article author: latter2005.tistory.com
- Reviews from users: 11846 Ratings
- Top rated: 4.8
- Lowest rated: 1
- Summary of article content: Articles about [운영체제] 프로세스 생성과 소멸 init 프로세스 : 리눅스, 유닉스 계열의 OS에서 최초로 실행되는 데몬 프로세스이며 PID는 항상 1입니다. 프로세스 트리 구조. 생성된 자식 프로세스 … …
- Most searched keywords: Whether you are looking for [운영체제] 프로세스 생성과 소멸 init 프로세스 : 리눅스, 유닉스 계열의 OS에서 최초로 실행되는 데몬 프로세스이며 PID는 항상 1입니다. 프로세스 트리 구조. 생성된 자식 프로세스 … Process Creation 프로세스 생성은 부모 프로세스가 연산을 통해 자식 프로세스를 만들어냅니다. 생성된 자식 프로세스 또한 새로운 자식 프로세스를 만들 수 있으며 이를 구별하기 위해 모든 프로세스는 각자 고..Cs 공부 정리 블로그
- Table of Contents:
티스토리툴바
[ Linux Kernel ] 06. Child Process 생성하기
- Article author: coder-in-war.tistory.com
- Reviews from users: 37421 Ratings
- Top rated: 3.2
- Lowest rated: 1
- Summary of article content: Articles about [ Linux Kernel ] 06. Child Process 생성하기 지정된 공간에 Child Process의 값들을 넣기 전에 먼저 Parent Process 의 image를 똑같이 복사를 해준다. 이 이유는 후에 등장한다. 프로세스 처리과정을 … …
- Most searched keywords: Whether you are looking for [ Linux Kernel ] 06. Child Process 생성하기 지정된 공간에 Child Process의 값들을 넣기 전에 먼저 Parent Process 의 image를 똑같이 복사를 해준다. 이 이유는 후에 등장한다. 프로세스 처리과정을 … 8. Child Process 생성하기 컴퓨터를 부팅하면 제일 먼저 커널 프로세스가 로드된다. 그리고 이 커널은 터미널이 켜질때 마다 그에 해당하는 Shell, 즉 Child Process를 만든다. Shell은 사용자의 입력을 기다리고..꾸준함이 무기입니다.
- Table of Contents:
81 Fork
티스토리툴바
See more articles in the same category here: 180+ tips for you.
REAKWON :: [리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드
프로세스(process)
프로세스는 간단히 말하자면 실행 중인 프로그램을 의미합니다. 아마 여러분들은 컴퓨터를 하면서 아주 빈번하게 듣는 용어이기도 합니다.
실행 중인 프로그램이라??
컴퓨터에서 말하는 실행 중인 프로그램이라는 건 저장공간에 있는 실행 파일이 메모리를 할당받아 명령어를 수행한다는 것을 의미합니다.
우리가 예를 들어 한글이라는 프로그램을 설치했다고 가정합시다. 아직은 더블클릭으로 프로그램을 실행하지 않았으므로 프로세스가 아닙니다. 설치 후 보고서를 작성하기 위해 프로그램을 실행한다면 그 순한 한글이라는 프로세스가 생성되는 것입니다.
프로세스에서 프로세스를 생성할 수도 있습니다. 이 경우 생성한 프로세스는 부모 프로세스, 생성당한 프로세스를 자식프로세스라고 합니다.
코드 영역(혹은 텍스트 영역이라고도 함) : 프로세스의 명령어가 존재합니다. CPU는 이 영역에서 명령을 실행합니다. 위 그림에서는 부모 자식 프로세스간 별개로 텍스트 영역이 존재하는 것처럼 보이지만 실제로 코드영역은 프로 자식 프로세스간 공유가 됩니다.
데이터 영역 : 전역 변수와 정적 변수가 존재합니다. 프로그램이 실행하는 동시에 할당되고 종료시 소멸됩니다.
힙 영역 : 동적할당에 필요한 메모리입니다. 프로그램 종료시 해제되지 않고 직접 해제해 주어야 합니다. C언어에서는 free를 사용해서 해제하게 됩니다.
스택 영역 : 함수 호출 시 매개변수, 지역변수가 이 곳에 자리잡게 됩니다. 함수가 종료하게 되면 해제됩니다.
자식 프로세스를 생성하는 시스템 콜이 fork라고 합니다. fork는 자기 자신을 복사합니다만 결국 다른 별도의 메모리를 갖게 됩니다. 그래서 자식 프로세스에서 변수의 값을 바꾸어도 부모 프로세스에 영향을 주지 않게 되죠.
자식 프로세스 생성(fork)
프로세스를 하나 생성하면서 특징을 같이 이야기 해봅시다.
#include #include #include #include int g_var = 1; char buf[] = “a write to stdout
“; int main(){ int l_var; pid_t pid; l_var = 10; if(write(STDOUT_FILENO,buf,sizeof(buf)-1) != sizeof(buf)-1){ printf(“write error
“); exit(0); } printf(“fork start!
“); //fflush(stdout); if((pid=fork())<0){ printf("fork error "); exit(0); }else if(pid == 0){ //child g_var++; l_var++; }else{ //parent sleep(3); } printf("pid = %ld, g_var = %d, l_var = %d ",(long)getpid(),g_var,l_var); exit(0); } 이제 컴파일 후 실행시켜보도록 하지요. # gcc fork.c # ./a.out a write to stdout fork start! pid = 83929, g_var = 2, l_var = 11 pid = 83928, g_var = 1, l_var = 10 위 코드를 설명하자면 fork를 통해서 자식 프로세스를 생성하게 됩니다. fork가 정상적으로 호출이되면 자식 프로세스는 0이 반환되고 부모 프로세스에게는 자식의 프로세스 id가 반환됩니다. 자식 프로세스는 변수들의 값을 변경시키고 있습니다. 그 결과는 위와 같습니다. 위의 실행 결과를 파일로 저장해볼까요? # ./a.out > fork.out # cat fork.out a write to stdout fork start! pid = 83960, glob = 2, var = 11 fork start! pid = 83959, glob = 1, var = 10
자세히보면 fork_start!라는 문자열이 두번 출력되고 있네요. 이같은 결과가 우리에게 시사하는 바는 자식 프로세스는 부모 프로세스의 버퍼까지 복사한다는 사실입니다. printf는 내부적으로 write 시스템 콜을 사용합니다. printf는 write를 최소한으로 그리고 효율적으로 사용하기 위해 내부적으로 버퍼를 사용하고 있습니다. 터미널 콘솔에 출력할때는 l줄 단위 버퍼링 방식을 사용합니다. 이 말은 줄이 바뀌면(엔터) 화면에 출력한다는 것입니다. 이와 다르게 파일에 기록할때는 full 버퍼링 방식을 사용합니다. 이 방식은 버퍼가 전부 채워져야 파일에 기록하는 버퍼링 방식입니다.
위의 테스트 결과에서 내용을 파일(fork.out)에 저장하는 것이므로 full buffering 방식을 사용하며 이때 버퍼가 다 차지 않았고 이 후에 자식 프로세스를 생성했습니다. 자식 프로세스는 부포 프로세스의 버퍼까지 전부 복제하였고 프로그램이 종료가 될때 버퍼를 비우며 종료가 됩니다.
위의 fflush(stdout)을 주석해제하고 다시 실행해보시기 바랍니다. fork전에 버퍼를 다 방출하기 때문에 fork_start!라는 문자열이 한번만 출력됩니다.
고아 프로세스
프로세스를 생성하기만 하면 다가 아닙니다. 왜 그런지 실행과 결과를 보면서 이야기 하도록 하지요.
#include
#include #include int main(){ int pid; if((pid=fork())==0){ sleep(1); printf(“\t [child process %d] created from %d “, getpid(),getppid()); exit(0); } printf(“[parent process %d] create %d process, ppid:%d
“, getpid(),pid,getppid()); }
getpid : 현재 프로세스의 pid를 반환합니다.
getppid : 부모의 프로세스 id를 반환하지요.
이 코드의 결과를 예상해보세요.
자식 프로세스에서는 부모의 프로세스 ID를 출력하니까 부모의 getpid와 같을 겁니다. 그럴까요??
[parent process 21162] create 21163 process, ppid:20819 [child process 21163] created from 1????
우리의 예상과는 다른데요??
분명 부모프로세스의 pid는 21162인데 자식 프로세스의 부모프로세스 pid가 쌩뚱맞은 1입니다.
왜 그럴까요??
우선 이 코드는 문제가 좀 있는 코드입니다. 부모프로세스가 먼저 종료되었기 때문입니다. 자식이 먼저 종료된다는 사실을 확보하기 위해 1초를 기다리는 이유가 바로 이 사실을 확인하기 위해서 입니다. 자식 프로세스는 부모 프로세스를 잃어 다른 부모를 찾게 됩니다. 바로 init 프로세스라고 하는데요. init프로세스의 pid가 바로 1입니다.
그러니까 모든 프로세스의 선조가 init프로세스라는 이야기죠.
이런 프로세스를 부모를 잃었다고 하여 고아프로세스가 됩니다.
그렇기 때문에 모든 부모님이 그렇듯 자식을 기다려주어야합니다. 그렇기 때문에 wait이라는 시스템 콜이 있지요.
코드를 수정해서 다시 확인합시다.
#include
#include #include #include #include int main(){ int pid; int status; int terminatedPid; if((pid=fork())==0){ printf(“\t [child process %d] created from %d “, getpid(),getppid()); sleep(1); exit(0); } printf(“[parent process %d] create %d process, ppid:%d
“, getpid(),pid,getppid()); terminatedPid=wait(&status); printf(“[parent process ] process wait process %d, status %d
“, terminatedPid,status); }
wait : wait의 첫번째 인자는 상태를 나타냅니다. 종료 상태를 받아올 수 있는 거죠. 반환값은 종료된 자식의 pid를 반환합니다.
[parent process 21803] create 21804 process, ppid:20819 [child process 21804] created from 21803 [parent process ] process wait process 21804, status 0이제 우리가 원하는 결과가 나왔군요. 자식프로세스를 기다려야합니다.
좀비프로세스
하지만 반대로 부모프로세스는 종료하지 않았지만 자식 프로세스가 먼저 종료하게 되면 어떨까요? 단, wait 호출을 하지 않고 말입니다.
#include
#include #include int main(){ if(fork()==0){ printf(“\t child process created and exit “); exit(0); } printf(“parent process sleep
“); sleep(10); sleep(10); printf(“process exit
“); }
이 후 컴파일하고 실행합니다.
# gcc process.c # ./a.out & [1] 22111 parent process sleep child process created and exit process exit
# ps -ef | grep 22111 … root 22112 22111 0 10:26 pts/2 00:00:00 [a.out]
… 부모 프로세스는 총 20초간 수행합니다. pid가 22111인 부모프로세스의 자식 프로세스 22112가 defunct인것이 보이세요? 바로 좀비 프로세스입니다.
부모프로세스는 자식 프로세스를 기다리고 있지 않아 종료상태를 얻지 못하고 있습니다. 그게 좀비 프로세스가 된 이유인데요. 이 종료 상태는 의외로 중요한데 예를 들어 쉘스크립트를 통해 명령을 실행할때 종료상태에 따라 분기를 하기 위해서 사용되기도 합니다.
부모 프로세스가 종료상태를 얻어와야 커널에서 자식 프로세스의 정보를 갖고 있는 구조체를 해제할 수 있습니다. 그 구조체가 task_struct라는 구조체입니다.
부모 프로세스는 커널이 관리하는 task_struct 구조체에서 멤버 pid와 exit_code를 통해서 종료상태를 얻어올 수 있습니다. 비록 좀비프로세스는 직접적으로 CPU, 메모리를 소모하지는 않지만 task_struct를 유지해야하기 때문에 메모리 낭비가 있게 되지요.
task_struct에서 pid와 exit_code, 무엇이 생각나세요?
wait의 반환값과 인자값입니다. 그 때문에 wait을 통해 해결할 수 있습니다.
코드를 다음과 같이 수정해보세요.
#include
#include #include #include #include int main(){ if(fork()==0){ printf(“\tchild process created and exit “); exit(0); } printf(“parent process sleep
“); wait(NULL); sleep(10); sleep(10); printf(“process exit
“); }
부모 프로세스에서 바로 wait 콜을 호출합니다. 그러니까 종료상태를 알게 되죠. 결과도 그럴까요?
# gcc process.c # ./a.out & [1] 23839 parent process sleep child process created and exit
# ps -ef | grep 23839 root 23839 20819 0 11:31 pts/2 00:00:00 ./a.out root 23842 20819 0 11:31 pts/2 00:00:00 grep –color=auto 23839 process exit
오.. 좀비프로세스가 없어졌어요.
또는 시그널을 이용한 방법도 있습니다.
#include
#include #include #include #include #include void childSignal(int sig){ printf(“signal:%d “,sig); wait(NULL); } int main(){ signal(SIGCHLD,childSignal); if(fork()==0){ printf(“\tchild process created and exit
“); exit(0); } printf(“parent process sleep
“); sleep(10); sleep(10); printf(“process exit
“); }
(시그널을 알아야 코드를 이해하는 데 도움이 됩니다.)
그리고 컴파일하고 실행해보도록 합시다. 그리고 재빠르게 ps 명령어를 쳐서 확인해보세요.
# gcc process.c # ./a.out & [1] 23451 parent process sleep child process created and exit signal:17
# ps -ef | grep 23451 root 23451 20819 0 11:09 pts/2 00:00:00 ./a.out root 23461 20819 0 11:10 pts/2 00:00:00 grep –color=auto 23451 process exit
이제 좀비프로세스가 없어졌군요!
왜 그럴까요??
우선 부모프로세스는 시그널을 등록합니다. 핸들러는 childSignal입니다.
자식 프로세스가 종료가 되면 시그널 번호 SIGCHLD로 부모프로세스에 시그널을 보냅니다.
그렇다면 그 순간 시그널 핸들러가 동작하죠. 그렇다면 부모프로세스는 childSignal함수안에 wait을 호출합니다.
그러므로 종료상태를 받을 수 있게 되고 자식 프로세스는 좀비프로세스가 되지 않습니다.
아차, sleep함수는 일정 시간 동안 시그널을 기다립니다. 그 시간안에 시그널을 받는 다면 sleep은 동작을 멈추게 됩니다.
왜 2번 sleep을 호출하는지 알겠죠? 바로 ps를 입력할 시간을 확보하기 위해서 입니다.
자식 프로세스의 종료 상태 알아오기
이번에는 wait에 인자를 이용하여 자식 프로세스의 종지 상태를 알아보도록 합시다. 종지상태를 조사하는 매크로는 표에 나와있습니다.
매크로 설명 WIFEXITED(status) 자식프로세스가 정상적으로 종료되었으면 true를 반환합니다. 이때 자식이 exit, _exit, _Exit으로 넘겨준 인수의 하위 8비트를 얻을 수도 있습니다. 그럴때 WEXITSTATUS(status)를 사용하세요. WIFSIGNALED(status) 만일 자식프로세스가 신호를 받아 비정상적으로 종료되었다면 참을 반환합니다. 이때 신호번호를 알려면 WTERMSIG(status)를 이용하여 알 수 있습니다. WCOREDUMP(status) 매크로를 지원하는 시스템은 코어덤프를 생성했는지도 알 수 있습니다. WIFSTOPPED(status) 자식 프로세스가 정지된 상태라면 참을 반환합니다. 이때 정지를 유발한 신호는 WSTOPSIG(status)를 통해 알아낼 수 있습니다. WIFCONTINUED(status) 만일 자식이 작업 제어 정지 이후 재개되었으면 참을 반환합니다.
다음은 위의 매크로를 사용한 예제입니다.
#include
#include #include #include void pr_exit(int status){ if(WIFEXITED(status)) printf(“normal termination, exit status = %d “,WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf(“abnormal termination, signal number =%d%s
“,WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status) ? ” (core file generated)”: “”); #else “”); #endif else if(WIFSTOPPED(status)) printf(“child stopped, signal number = %d
“, WSTOPSIG(status)); } int main(void){ pid_t pid; int status; if((pid=fork())<0){ printf("fork error "); exit(0); }else if(pid==0) exit(7); if(wait(&status) != pid){ printf("wait error "); exit(0); } pr_exit(status); if((pid=fork())<0){ printf("fork error "); exit(0); }else if(pid==0) //abort signal abort(); if(wait(&status) != pid){ printf("wait error "); exit(0); } pr_exit(status); if((pid=fork()) < 0){ printf("fork erorr "); exit(0); }else if(pid==0) // 0으로 나누기 신호 status /=0; if(wait(&status)!=pid){ printf("wait error "); exit(0); } pr_exit(status); exit(0); } 컴파일 후 실행해보면 자식 프로세스가 어떻게 종료가 되었는지 알 수 있습니다. # ./a.out normal termination, exit status = 7 abnormal termination, signal number =6 (core file generated) abnormal termination, signal number =8 (core file generated) 자식 프로세스가 exit(7)로 종료되었음을 부모 프로세스는 WEXITSTATUS매크로로 알 수 가 있네요. 이제 fork와 wait을 사용하는 방법을 아시겠나요? 하지만 wait에는 문제점이 존재하는데요. 그 문제점을 보완하는 함수 waitpid가 존재합니다. 아래의 포스팅을 참고하세요. https://reakwon.tistory.com/99
UNIX〕프로세스 생성과 실행: pid, fork, exec, pipe
반응형
1. 프로세스(Process)
(1) 프로세스란?
현재 실행 중인 프로그램
즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당 받아 실행중인 것
운영체제의 제어 아래 실행(Running), 대기(Waiting), 중단(Stopped), 좀비(Zombie) 중 하나의 상태에 있게 된다.
각각의 프로세스가 가지는 고유의 번호를 PID(Process Idendification Number) 라고 한다.
예를 들어 데비안 리눅스가 부팅될 때에는 최상위 프로세스인 systemd(PID: 1)이 생성되며 모든 프로세스들은 이 1번 프로세스의 자식 프로세스가 된다.
부모 프로세스의 PID 를 줄여서 PPID 라고 한다.
ps 명령어를 통해 현재 실행중인 프로세스 목록을 확인할 수 있다.
(2) 쓰레드(Thread)
프로세스 내에서 실제로 작업을 수행하는 주체
모든 프로세스에는 하나 이상의 쓰레드가 존재한다.
두 개 이상의 쓰레드를 가지는 프로세스를 멀티 쓰레드 프로세스라고 한다.
특정한 시점에 프로그램의 특정한 부분을 수행하는 각각이 모두 쓰레드이다.
따라서 여러 개의 쓰레드를 이용하여 프로그램의 여러 부분을 동시에 수행시킬 수 있다.
싱글 쓰레드 프로세스와 멀티 쓰레드 프로세스 비교
각각의 프로세스들은 각각의 독립된 영역을 가지는 반면, 쓰레드는 하나의 프로세스 내에서 여러개 존재하기 때문에 같은 프로세스에 있는 메모리 공간을 서로 공유한다. 위의 사진에서 세 개의 쓰레드는 하나의 주소 공간에 존재하며 같은 데이터와 파일을 서로 공유하면서 코드의 여러 부분을 동시에 수행한다.
이 멀티 쓰레드가 각각 독립적으로 수행되기 위해서 가지는 두 가지 정보가 바로 CPU의 프로그램 카운터와 스택이다.
프로그램 카운터(Program Counter): 프로그램의 어느 부분을 실행하고 있는지에 대한 정보를 저장
스택(Stack): 함수를 호출하는 순서(Function Call)에 대한 정보를 저장
2. 프로세스 생성과 실행
사용자가 명령행에서 직접 프로세스를 실행하여 생성하는 경우 이외에도 프로그램 내에서 다른 프로그램을 실행하여 생성하는 경우도 있다. 위와 같이 system(), fork(), 또는 vfork() 함수를 통해 프로세스를 실행하거나 생성할 수 있다.
프로그램 실행 int system(const char *string); 프로세스 생성 pid_t fork(void); 또는 pid_t vfork(void);
(1) system()
system() 함수를 이용하여 간단하게 프로그램 안에서 새로운 프로그램을 실행시킬 수 있다.
기존의 명령이나 실행 파일명을 인자로 받아 쉘에 전달한다.
쉘은 내부적으로 새로운 프로세스를 생성하여 인자로 받은 명령을 실행한다.
해당 명령이 끝날 때까지 기다렸다가 종료 상태를 리턴한다.
가장 간단하지만 명령을 실행하기 위해 쉘까지 동작시키기 때문에 비효율적인 방법이다.
system(“leaks a.out”);
→ main 문 안에 적으면 프로그램이 종료되기 전에 메모리 누수를 확인해준다.
(2) fork()
fork() 함수로 생성된 새로운 프로세스를 자식 프로세스(Child Process)라고 한다.
fork() 함수를 호출한 프로세스는 부모 프로세스(Parent Process)가 된다.
fork() 함수가 리턴하면 부모 프로세스와 자식 프로세스가 동시에 동작하게 된다.
어느 프로세스가 먼저 실행되는지는 알 수 없으며 유닉스 운영체제의 스케쥴링에 따라 처리 순서가 달라진다.
fork() 함수 처리 과정:
#1. fork() 함수 호출
#2. 새로운 프로세스(=자식 프로세스) 생성
#3. 부모 프로세스의 데이터 영역을 그대로 복사하여 자식 프로세스에 메모리를 할당해 준다.
#4. 부모 프로세스에는 자식 프로세스의 PID를, 자식 프로세스에는 0 을 반환한다.
(3) exec()
현재 프로그램의 텍스트, 데이터, 스택 영역에 exec() 계열의 함수의 인자로 전달된 프로그램의 텍스트, 데이터, 스택 영역을 덮어씌우는 함수이다. 즉, 새로운 프로세스가 현재 프로세스 위치에 덮어씌워지므로 현재 프로세스는 종료된다.
#include
#include int main() { printf(“exec()함수 호출전 “); execl(“/bin/ls” , “ls” , “-l” , (char *) 0); printf(“exec()함수 호출 후
“); return 0; }
위의 예시에서 출력되는 것은 “exec() 함수 호출전” 뿐이다. execl()이 ls 프로세스를 실행함과 동시에 현재 프로세스가 종료되었기 때문이다. 만약 현재 프로세스를 종료시키고 싶지 않다면 아래처럼 fork() 함수를 이용한 뒤에 exec() 을 사용하면 된다.
#include
#include int main() { int pid; if((pid=fork()) == 0) { printf(“Child Process ID:%d “, getpid()); execl(“/bin/ls”, “ls”, “-l”, (char *)0); } else { printf(“Parent Process ID: %d
“, getpid()); } return 0; }
자식 프로세스에서 exec 함수를 호출하면 부모 프로세스로부터 복사한 프로그램과는 다른 명령이나 프로그램을 실행 가능하다. 부모 프로세스와 자식 프로세스가 각각 다른 작업을 수행해야 하는 경우 fork()와 exec() 함수를 함께 사용해야한다.
예를 들어 쉘에서 ‘ls -a’ 라는 명령어를 입력 받으면 쉘은 fork() 함수로 자식 프로세스를 만들고 사용자가 입력한 명령 ‘ls -a’를 exec() 함수군을 사용하여 자식 프로세스에서 실행한다.
■ execve()
#include
int execve(const char *filename, char *const argv[], char *const envp[]);
filename: 실행 가능한 binary file, shell script, 또는 명령어
argv: main 에서의 argv 와 똑같지만, main 과 달리 argc 가 없으므로 마지막에는 NULL 이 있다.
envp: key=value 형식으로 구성되어 있는 환경 변수 문자열 배열리스트로 마지막에는 NULL 이 있다. 예를 들어 envp[0] 이 “HOME=/home/user” 라면 key 는 “HOME”, value 는 “/home/user” 가 된다. 만약 이미 설정된 환경 변수를 사용하려면 environ 환경 변수를 사용할 수 있다. environ 은 C 프로그램에서 이미 선언되어 있기 때문에 extern 문을 통해 environ 변수를 참조하여 환경 변수 목록을 확인할 수 있다.
#include
extern char **environ; int main() { int i = 0; printf(“=== environment list === “); for(i=0; environ[i];i++) { printf(“<%2d>: %s
“, i, environ[i]); } return 0; }
ls 명령어 실행하는 프로그램 만들기
#include
#include #include #include #include extern char **environ; int main(int argc, char *argv[]) { char **new_argv; char command[] = “ls”; int idx; new_argv = (char **)malloc(sizeof(char *) * (argc + 1)); new_argv[0] = command; // 명령어를 ls로 변경 // command line으로 넘어온 parameter를 그대로 사용 for (idx = 1; idx < argc; idx++) new_argv[idx] = argv[idx]; // argc를 execve 파라미터에 전달할 수 없기 때문에 NULL이 파라미터의 끝을 의미한다. new_argv[argc] = NULL; if (execve("/bin/ls", new_argv, environ) == -1) { fprintf(stderr, "프로그램 실행 error: %s ", strerror(errno)); return 1; } // ls 명령어 binary로 실행로직이 교체되었으므로 이후의 로직은 절대 실행되지 않는다. printf("이것은 이제 ls 명령어러 이 라인은 출력되지 않는다. "); return 0; } (4) wait() fork() 함수를 통해 자식 프로세스를 생성하고 나면 부모 프로세스와 자식 프로세스 중에 어느 것이 먼저 실행되는지 알 수 없으며 먼저 실행을 마친 프로세스는 종료한다. 하지만 이들 사이의 종료 절차가 제대로 진행되지 않는 경우가 있다. 이 때 좀비 프로세스(Zombie Process)라는 불안정 상태의 프로세스가 발생한다. 이를 방지하기 위해 필요한 것이 프로세스 동기화이다. 부모 프로세스와 자식 프로세스를 동기화하려면 부모 프로세스는 자식 프로세스가 종료할 때까지 기다려야한다. 여기서 자식 프로세스의 실행이 완전히 끝나기를 기다렸다가 종료 상태를 확인하는 함수가 wait() 함수이다. 자식 프로세스의 종료 상태는 stat_loc 에 지정한 주소에 저장된다. 만약 부모 프로세스가 wait() 함수를 호출하기 전에 자식 프로세스가 종료하면 wait() 함수는 즉시 리턴한다. 리턴값은 자식 프로세스의 ID 이다. 만약 리턴값이 -1 이라면 살아있는 자식 프로세스가 하나도 없다는 것을 의미한다. ※ wait() 함수의 경우 아무 자식 프로세스나 종료하면 리턴하지만 waitpid() 함수는 특정 PID 의 자식 프로세스가 종료하기를 기다린다는 점에서 다르다. #include #include
pid_t wait(int *stat_loc);
※ stat_loc: 상태 정보를 저장하는 주소
pid_t waitpid(pid_t pid, int *status, int options);
※ status: 프로세스의 상태정보를 저장하는 주소
waitpid() 함수에서 status 는 프로세스의 상태를 나타내기 위해 사용된다. status 가 NULL 이 아닌 경우, status 가 가리키는 위치에 프로세스의 상태정보를 저장한다. 여기서 상태정보를 알아내기 위해 사용되는 매크로들 중에서 가장 대표적인 것이 WIFEXITED(status) 이다. 자식 프로세스가 정상적으로 종료되었다면 TRUE(non-zero) 이다.
3. pipe()
(1) 형식
#include
int pipe(int fd[2]);
(2) 작동 원리
출처: https://reakwon.tistory.com/80
pipe 함수가 성공적으로 호출되는 경우 0, 실패했을 경우에는 -1 을 반환
파이프는 사진과 같이 커널 영역에 생성되며 프로세스는 파일 디스크립터만을 가지고 있다.
파일 디스크립터 fd[0] 은 읽기용 파이프이고 fd[1] 은 쓰기용 파이프이다.
현재 프로세스에서 fork() 하게되면 자식 프로세스가 생성된다.
부모 프로세스의 파일 디스크립터는 그대로 자식 프로세스에 복제된다.
#include
#include #include #include #define MAX_BUF 1024 #define READ 0 #define WRITE 1 int main(){ int fd[2]; pid_t pid; char buf[MAX_BUF]; if(pipe(fd) < 0) { printf("pipe error "); exit(1); } if((pid=fork())<0) { printf("fork error "); exit(1); } printf(" "); if(pid>0) //parent process { close(fd[READ]); strcpy(buf,”message from parent “); write(fd[WRITE],buf,strlen(buf)); } else //child process { close(fd[WRITE]); read(fd[READ],buf,MAX_BUF); printf(“child got message : %s
“,buf); } exit(0); }
우선 부모 프로세스에서 파이프를 생성하여 파이프에 데이터를 쓰기(fd[1]: stdout)만 할것이므로 읽기 파이프(fd[0]: stdin)는 닫고, fd[1] 에 데이터를 쓴다.
자식 프로세스에서 쓰기 파이프는 쓰지 않으므로 fd[1] 은 닫고 읽기 파이프를 통해 데이터를 읽는다.
출력 결과는 child got message : message from parent 이 된다.
반응형
6. 프로세스 생성과 실행 · UNIXBasic
6. 프로세스 생성과 실행
6-1.개요
유닉스에서 프로세스는 사용자가 명령행에서 직접 프로세스를 실행해 생성하는 경우도 있지만, 프로그램 안에서 다른 프로그램을 실행해 생성하는 경우도 있다. 이렇게 프로그램에서 다른 프로그램을 실행해 새로운 프로세스를 실행할 때는 system, fork, vfork함수를 사용한다.
기능 함수원형 프로그램 실행 int system(const char *string); 프로세스 생성 pid_t fork(void); pid_t vfork(void);
유닉스는 프로세스가 종료되면 해당 프로세스가 어떻게 종료되었는지를 나타내는 종료 상태(exit status)를 저장한다. 자식 프로세스는 부모 프로세스에 자신이 어떻게 종료되었는지 알리는 종료 상태값을 리턴할 수 있다. 일반적으로 종료 상태값이 0이면 정상적으로 종료했음을 의미한다. 종료 상태값이 0이 아니면 오류가 발생했음을 의미한다. 프로그램을 종료할 때는 exit, atexit, _exit함수를 사용한다.
기능 함수원형 프로세스 종료 void exit(int status); 종료 시 수행할 작업 지정 int atexit(void (*func)void));
함수명이 exec로 시작하는 exec함수군이 있다. exec함수군은 인자로 받은 다른 프로그램을 자신을 호출한 프로세스의 메모리에 덮어쓴다.
따라서 프로세스가 수행중이던 기존 프로그램은 중지되어 없어지고 새로 덮어쓴 프로그램이 실행된다. exec함수군은 fork함수와 연결해 fork로 생성한 자식 프로세스가 새로운 프로그램을 실행하도록 할 때도 사용한다.
<프로세스 수행>
int execl(const char *path, const char *argo, …, const char *argn, (char *)0; int execv(const char *path, char *const argv[]); int execle(const char *path, const char *arg0, …, const char *argn, (char *)0, char *const envp[]); int execve(const char *path, char *const argv[], char *const envp[]); int execlp9const char *file, const char *arg0, …, const char *argn, (char *)0; int execvp(const char *file, char *const argv[]);
fork 함수로 자식 프로세스를 생성하면 부모 프로세스와 자식 프로세스는 순서에 관계 없이 실행되고, 먼저 실행을 마친 프로세스는 종료한다. 이때 좀비 프로세스zombie procss같은 불안정 상태의 프로세스가 발생하는데 이를 방지하려면 프로세스 동기화 함수를 수행해서 부모 프로세스와 자식 프로세스를 동기화 시켜야한다.
<프로세스 동기화 함수>
기능 함수원형 임의의 자식 프로세스의 상태값 구하기 pid_t wait(int *stat_loc); 특정 프로세스의 상태값 구하기 pid_t waitpid(pid_t pid, int *stat_loc, int options);
6-2.프로세스 생성
프로세스는 실행 중인 프로그램을 의미한다.
프로세스는 사용자가 명령행에서 직접 실행해서 생성하는 경우도 있지만, 프로그램 안에서 다른 프로그램을 실행해 생성하는 경우도 있다.
예를 들어 기능 별로 별도의 프로그램으로 구성되어 있는 업무용 소프트웨어에서 보고서 작성을 위해 출력 프로그램을 실행할 수 있다.
인터넷을 통해 서비스를 제공하도록 다른 서비스 프로그램을 동작시키려면 새로운 프로세스를 생성해야한다.
이렇게 새로운 프로세스를 생성할 때 system, fork, vfork 함수를 사용한다.
6-2-1. 간단한 방법
system함수는 프로그램 안에서 새로운 프로그램을 실행하는 가장 간단한 방법이다.
그러나 이 함수는 명령을 실행하기 위해 쉘까지 동작시키므로 비효율적이다.
프로그램 실행: system(3)
int system ( const char * string ) ; * string : 실행할 명령이나 실행파일명
이 함수는 기존 명령이나 실행 파일명을 인자로 받아 쉘에 전달한다.
쉘은 내부적으로 새 프로세스를 생성해 인자로 받은 명령을 실행하고, 해당명령의 실행이 끝날 때까지 기다렸다가 종료 상태를 리턴한다
#include
#include int main(void){ int a; //파이프로 연결된 ps -ef | grep han > han.txt 명령을 실행하도록 //system 함수를 호출한다. //인자로 전달된 명령은 현재 실행 중인 프로세스들에서 ‘han’을 포함한 //내용을 찾아 han.txt파일에 저장한다. a = system(“ps -ef | grep han > han.txt”); print(“return value: %d “, a); return 0; } * cat명령으로 han.txt파일을 살펴보면 system함수로 실행한 명령인 “sh -c ps -ef | grep han > han.txt 가 실행되고 있다. (system 함수로 명령을 실행하면 본 쉘로 실행되고 -c옵션이 지정된다.)
6-2-2. 프로세스 생성
유닉스에서 프로세스를 생성해 프로그램을 실행하는 방법은 fork 함수를 사용하는 것이다.
fork함수는 새로운 프로세스를 생성한다. 이 함수가 생성한 새 프로세스는 child process라고 한다.
fork함수를 호출한 프로세스는 parent process가 된다.
fork함수가 리턴하면 부모 프로세스와 자식 프로세스가 동시에 동작하는데, 어느 프로세스가 먼저 실행될지는 알수 없다.
유닉스 운영체제의 스케쥴링(scheduling)에 따라 처리 순서가 달라진다.
fork함수 실행시 처리 과정 fork 함수 호출
새로운 프로세스(자식 프로세스) 생성
fork함수로 생성한 자식 프로세스의 메모리 공간은 부모 프로세스의 메모리 공간을 그대로 복사해 만든다.
이 함수는 부모 프로세스에는 자식 프로세스의 PID를 리턴하고, 자식 프로세스에는 0을 리턴한다.
자식 프로세스가 상속 받는 대표적인 속성 목록 실제 사용자ID(RUID), 유효사용자ID(EUID), 실제그룹ID(RGID), 유효그룹ID(EGID)
환경 변수
열린 파일 기술자
시그널 처리 설정
setuid, setgid 설정
현재 작업 디렉토리
umask 설정값
사용 가능한 자원 제한
자식 프로세스와 부모 프로세스와의 다른 점 (fork함수 결과 생성된 자식프로세스)
자식프로세스는 유일한 프로세스ID를 갖는다. 자식 프로세스는 부모프로세스를 PPID로 설정한다. 자식 프로세스는 부모 프로세스가 연 파일 기술자의 사본을 가지고 있으므로 부모 프로세스와 자식 프로세스가 같은 파일의 오프셋을 공유하는 상태가 되므로 읽거나 쓸때 주의해야한다. 자식 프로세스는 부모 프로세스가 설정한 프로세스 잠금, 파일 잠금, 기타 메모리 잠금 등은 상속하지 않는다. 자식 프로세스의 tms 구조체 값은 0으로 초기화 된다. 프로세스 실행 시간을 측정하는 기준 값이 새로 설정된다.
int main ( void ) { pid_t pid; switch (pid = fork()){ case -1 : perror( “fork” ); exit ( 1 ); break ; case 0 : printf ( “child process – my PID : %d, may parent’s PID: %d ” , ( int )getpid(), ( int )getppid()); break ; default : printf ( “parent process – my PID: %d, My parent’s PID: %d, my child’s PID:%d
” , ( int )getpid(), ( int )getppid(), ( int )pid); break ; } printf ( “end of fork
” ); return 0 ; }
프로세스 생성 : vfork(2)
pid_t vfork( void ); * return 값 : 성공시 부모 프로세스에는 자식 프로세스의 ID, 자식 프로세스에는 0. 실패시 -1. pid_t vfork( void );
vfork함수도 fork처럼 새로운 프로세스를 생성한다.
그러나 부모 프로세스의 메모리 공간을 모두 복사하지는 않는다.
vfork함수는 표준이 아니므로 시스템에서 지원하는지 확인하고 사용해야한다.
6-3.프로세스 종료
유닉스는 프로세스가 종료되면 해당 프로세스가 어떻게 종료되었는지를 나타내는 종료 상태(exit status)를 저장한다. 부모 프로세스는 저장된 종료 상태 정보를 사용해 자식 프로세스가 어떻게 종료되었는지 알 수 있다.
자식 프로세스는 부모 프로세스에 자신이 어떻게 종료되었는지 알리는 종료 상태값 을 리턴할 수 있다.
종료 상태값이 0이면 프로세스가 정상 종료했음을 알수 있다. 오류가 발생하면 1을 리턴하며, 프로그래머에 따라 다양한 상태값을 이용해 종료상태를 알려주기도 한다.
6-3-1.프로세스 종료 함수
exit, atexit, _exit함수는 프로세스 종료와 관련이 있다. exit함수는 프로세스를 종료할 때 사용하는 기본 함수다. atexit함수를 사용하면 프로세스를 종료할 때 수행할 작업을 지정할 수 있다. _exit 함수는 일반적으로 프로그램에서 직접 호출하지 않는다.
프로그램 종료 : exit(2)
void exit ( int status) ; * status : 상태 종료값
exit 함수는 프로세스를 종료시키고 부모 프로세스에 종료 상태값을 전달한다. 이때 atexit 함수로 예약한 함수들을 지정된 순서와 역순으로 모두 실행한다.
만약 atexit함수로 예약한 함수가 수행도중 문제 발생하여 리턴하지 못할 경우는 exit함수의 나머지 과정도 수행되지 않는다.
exit함수는 프로세스가 사용중이던 모든 표준 입출력 스트림에 데이터가 남아있으면 이를 모두 기록하고 열려 있는 스트림을 모두 닫는다.
그 다음 tmpfile함수로 생성한 임시 파일을 모두 삭제하고, _exit함수를 호출한다.
_exit함수는 시스템 호출로, 프로세스가 사용하던 모든 자원을 반납한다. 이는 exit 함수가 C표준 함수이기 때문에 시스템에 독립적인 기능만 수행하고 시스템과 관련된 기능은 시스템 호출에서 처리하도록 해야하기 때문이다.
프로그램에서 종료시 수행할 작업 예약 : axexit(3)
#include
int atexit(void (*func) (void)); * func : 종료시 수행할 작업을 지정한 함수명 axexit 함수는 프로세스가 종료할 때 수행할 기능을 예약한다. atexit함수는 인자로 함수의 포인터를 받는다.
atexit의 인자로 지정하는 함수는 인자와 리턴값이 없는 함수다.
이 함수로 예약할 수 있는 함수의 개수는 여유 메모리에 따라 달라지며,
sysconf 함수의 _SC_ATEXIT_MAX 항목으로 검색할 수 있다.
< exit 와 aexit함수 사용예제> void cleanup1 ( void ) { printf ( “cleanup 1 is called
” ); } void cleanup2 ( void ) { printf ( “cleanup 2 is called
” ); } int main ( void ) { atexit(cleanup1); atexit(cleanup2); exit ( 0 ); } * 결과 실행결과는 함수가 예약 순서와 반대로 호출되었음을 알 수 있다. cleanup 2 is called; cleanup 1 is called;
프로그램 종료 : _exit(2)
void _exit( int status); * status : 종료 상태값
_exit 함수는 프로그램에서 직접 사용하지 않고, exit함수에서 내부적으로 호출한다. 이 함수는 시스템 호출로, 프로세스 종료시 다음과 같은 과정을 통해 시스템 관련 자원을 정리한다.
모든 파일 기술자를 닫는다. 부모 프로세스에 종료 상태를 알린다. 자식 프로세스들에 SIGHUP 시그널을 보낸다. 부모 프로세스에 SIGCHLD 시그널을 보낸다. 프로세스 간 통신에 사용한 자원을 반납한다.
6-4. exec 함수군 활용
함수명이 exec로 시작하는 함수들이 있는데 이를 exec함수군이라고 부른다. exec함수군을 이용해 명령이나 실행 파일을 만들 수 있다.
이들은 인자로 받은 다른 프로그램을 자신을 호출한 프로세스의 메모리에 덮어쓴다.
따라서 프로세스가 수행중이던 기존 프로그램은 없어지고, 새로 덮어쓴 프로그램이 실행된다.
이들을 호출한 프로세스 자체가 바뀌므로 exec 함수군 호출 성공시 리턴값은 없다.
exec함수군은 fork함수와 연결해 fork로 생성한 자식 프로세스가 새로운 프로그램을 실행하도록 할 때 유용하다.
6-4-1. exec 함수군의 함수 형태
exec(2) 함수군은 다음과 같이 6가지 함수 형태가 있다. 각 형태에 따라 지정하는 인자가 약간씩 차이가 있으므로 주의해야한다.
exec함수군은 path나 file에 지정한 명령이나 실행 파일을 실행한다. arg, envp로 시작하는 인자를 path나 file에 지정한 파일의 main함수에 전달한다.
#include
int execl(const char *path, const char *arg0, …, const char *argn, (char * ) 0); * path : 지정한 경로명 * arg0 ~ argn을 인자로 전달한다. (arg0에는 실행파일명을 관례로 지정함) * execl 의 마지막 인자에는 인자의 끝을 의미하는 NULL포인터((char *)0)을 지정해야한다. int execv(const char *path, char *const argv[]); * argv : 포인터 배열(이 배열의 마지막에는 널 문자열을 저장해야한다.) int execl(const char *path, const char *arg0, …, const char *argn, (char *)0, char *const envp[]); * arg0~argn : 포인터로 지정하며 마지막 값은 NULL 포인터로 지정해야한다. * evnp : 새로운 환경 변수 설정. 포인터 배열이며 이 배열의 마지막에는 널 문자열을 저장해야한다. int execve(const char *path, char *const argv[], char *const envp[]); * argv, envp 는 포인터 배열이며 마지막에는 NULL문자열을 저장할 것. int execlp(const char *file, const char *arg0, …, const char *argn, (char *)0); * 파일은 이 함수를 호출한 프로세스의 검색 경로(환경 변수 PATH에 정의된 경로)에서 찾는다. int execvp(const char *file, char *const argv[]); * file에 지정한 파일을 실행하며 argv를 인자로 전달한다. argv는 포인터 배열이다. 이 배열의 마지막에는 NULL문자열을 저장해야 한다. 6-4-2. exec 함수군의 활용
execlp함수를 사용해 현재 프로그램을 다른 명령(ls -a)로 바꿔서 실행하는 예
int main ( void ) { printf ( “–>Before exec function
” ); if (execlp( “ls” , “ls” , “-a” , ( char *) NULL ) == -1 ) { perror( “execlp” ); exit ( 1 ); } printf ( “–> After exec function
” ); return 0 ; }
위 예제의 결과에서 –> After메세지는 출력되지 않고 ls명령만 출력됨을 알 수 있다.
execlp 함수를 만나 프로세스의 이미지가 ls명령의 이미지로 바뀌었으므로 이후의 명령은 수행되지 않는다.
execv 함수를 사용해 현재 프로그램을 다른 명령(ls -a)으로 바꿔 실행하는 예제
int main ( void ) { char *argv[ 3 ]; printf ( “Before exec function
” ); argv[ 0 ] = “ls” ; argv[ 1 ] = “-a” ; argv[ 2 ] = NULL ; if (execv( “/usr/bin/ls” , argv) == -1 ){ perror( “execv” ); exit ( 1 ); } printf ( “After exec function
” ); return 0 ; } * 수행 결과 After~ 메세지는 출력하지 않고 ls 명령을 수행함을 알 수 있다.
argv와 envp를 모두 지정해 execve 함수 호출
#include
#include #include int main(void){ char *argv[3]; char *argv[2]; printf(“Before exec function “); argv[0] = “arg.out”; argv[1] = “100”; argv[2] = NULL; envp[0] = “MYENV=aaa”; envp[1] = NULL; //arg.out은 다음 프로그램 소스를 컴파일해 만든 것이다. //이 프로그램은 인자로 받은 argv와 envp 값을 출력한다. if(execve(“./arg.out”, argv, envp) == -1) { perror(“execve:); exit(1); } printf(“After exec function
“); return 0; } * arg.out 의 컴파일전 프로그램 소스 #include
int main(int argc, char **argv, char **envp) { int n; char **env; printf(“argc = %d “, argc); for(n =0; n < argc; n++) printf[%d] = %s ", n, argv[n]); env = envp; while(*env){ printf(%s ", *env); env++; } return 0; } 6-4-3. exec 함수군과 fork 함수 자식 프로세스에서 exec함수군을 호출하면 자식프로세스는 부모 프로세스로부터 복사한 프로그램과는 다른 명령이나 프로그램을 실행할 수 있다. 예를 들어 쉘에서 어떤 명령이나 파일을 실행하면, 쉘은 fork함수로 자식 프로세스를 만들고 사용자가 입력한 명령이나 파일을 exec함수군을 사용해 자식 프로세스에서 실행한다. 이렇게 부모 프로세스와 자식 프로세스가 각기 다른 작업을 수행해야할 때 fork와 exec 함수를 같이 사용해야한다. fork함수로 자식 프로세스를 만들고, exec함수를 사용해 자식 프로세스는 ls -a 명령을 수행하도록 만드는 예제 int main ( void ) { pid_t pid; switch (pid == fork(){ case -1 : perror( "fork" ); exit ( 1 ); break ; case 0 : printf ( " --> child process
” ); if (execlp( “ls” , “ls” , “-a” , ( char *) NULL ) == -1 ){ perror( “execlp” ); exit ( 1 ); } exit ( 0 ); break ; default : printf ( “–>parent process – MY pid : %d/n” , ( int )getpid()); break ; } return 0 ; } * 실행결과를 보면 자식 프로세스는 –>Child Process~라는 메세지를 출력한 후, ls명령을 수행하고, –>parent process~ 메세지를 출력했음을 알 수 있다. 다시 실행해보면 ls명령이 부모 프로세스 다음에 실행될 수도 있다.
6-5. 프로세스 동기화
fork함수로 자식 프로세스를 생성하면 부모 프로세스와 자식 프로세스는 순서에 상관없이 실행되고, 먼저 실행을 마친 프로세스는 종료한다.
그러나 부모 프로세스와 자식 프로세스 사이의 종료 절차가 제대로 진행되지 않을 때가 있다.
이때 zombie process 와 같은 불안정 상태의 프로세스가 발생하는데 이를 방지하려면 부모 프로세스와 자식 프로세스를 동기화해야한다.
wait함수 사용 예제
int main ( void ) { int status; pid_t pid; switch (pid = fork()){ case -1 : perror( “fork” ); exit ( 1 ); break ; case 0 : printf ( “–> Child process
” ); exi( 2 ); break ; default : while (wait(&status) != pid) continue ; printf ( “–>Parent process
” ); printf ( “Status : %d, %x
” , status, status); printf ( “Child process Exit Status : %d
” , status >> 8 ); break ; } return 0 ; } * 자식프로세스가 종료할 때까지 기다리기 때문에 (wait 함수, while 구문) 항상 자식 프로세스의 결과가 먼저 출력되고 –> Parent process… 의 결과가 출력된다. * 실행결과를 보면 status가 512 ( 10 진수), 0x0200 ( 16 진수)으로 출력됨을 알 수 있다. 자식 프로세스가 전달한 값은 부모 프로세스에 왼쪽으로 한 바이트 이동해 전달된다. 이를 제대로 출력하려면 오른쪽으로 8 비트 이동시켜야한다. 그렇게 이동시켜서 출력한 결과는 종료 상태값이 2 가 된다.
6-5-1. 좀비 프로세스
정상적인 프로세스 종료 과정이라면, 자식 프로세스가 종료를 위해 부모 프로세스에 종료상태 정보를 보내고, 부모 프로세스는 이 정보를 받으면 프로세스 테이블에서 자식 프로세스를 삭제한다.
그러나 자식 프로세스가 자원을 모두 반납했어도 부모 프로세스가 종료 상태 정보를 얻어가지 않거나 자식 프로세스보다 먼저 종료하는 경우가 발생한다.
실행을 종료한 후 자원을 반납한 자식 프로세스의 종료 상태 정보를 부모 프로세스가 얻어가지 않는 경우에 좀비 프로세스가 발생한다.
좀비 프로세스는 프로세스 테이블에만 존재한다.
좀비 프로세스는 일반적인 방법으로 제거할 수 없으며, 부모 프로세스가 wait 관련 함수를 호출해야 사라진다.
만일 자식 프로세스보다 부모 프로세스가 먼저 종료되면 자식 프로세스들은 고아 프로세스가 된다. 이들 고아 프로세스는 init(PID 1) 프로세스의 자식 프로세스로 등록된다.
6-5-2. 프로세스 동기화
부모 프로세스와 자식 프로세스를 동기화하려면 부모 프로세스는 자식 프로스가 종료할 때까지 기다려야한다.
자식 프로세스의 실행이 완전히 끝나기를 기다렸다가 종료 상태를 확인하는 함수는 wait, waitpid이다.
프로세스 동기화 : wait(3)
pid_t wait( int *stat_loc); * stat_loc : 상태 정보를 저장할 주소
wait 함수는 자식 프로세스가 종료할 때까지 부모 프로세스를 기다리게 한다 자식 프로세스의 종료 상태는 stat_loc에 지정한 주소에 저장된다.
stat_loc에 NULL을 지정할 수도 있다.
만약 부모 프로세스가 wait 함수를 호출하기 전에 자식 프로세스가 종료하면 wait함수는 즉시 리턴한다.
wait함수의 리턴값은 자식프로세스의 ID이다.
wait 함수의 리턴값이 -1이면 살아있는 자식 프로세스가 하나도 없다는 의미이다.
특정 자식 프로세스와 동기화 : waitpid(3)
pid_t waitpid( pid_t pid, int *stat_loc, int options); * pid : 종료를 기다리는 PID * -1 보다 작은 경우 : pid의 절댓값과 같은 프로세스 그룹 ID에 속한 자식 프로세스 중 임의의 프로세스의 상태값을 요청한다. * -1 : wait 함수처럼 임의의 자식 프로세스의 상태값 * 0 : 함수를 호출한 프로세스와 같은 프로세스 그룹에 속한 임의의 프로세스의 상태값을 요청한다. * 0 보다 큰 경우 : 지정한 pid에 해당하는 프로세스의 상태값을 요청한다. * stat_loc : 종료 상태값을 저장할 주소 * options : waitpid 함수의 리턴 조건.
에 정의된 값을 사용한다. OR연산으로 연결해 지정할 수 있다. * WCONTINUED : 수행중인 자식 프로세스의 상태값이 리턴 * WNOHANG: pid로 지정한 자식 프로세스의 상태값을 즉시 리턴받을 수 없어도 이를 호출한 프로세스의 실행을 블록하지 않고, 다른 작업을 수행하게 한다. * WNOWAIT : 상태값을 리턴한 프로세스가 대기 상태로 머물수 있도록 한다. * WUNTRACED : 실행을 중단한 자식 프로세스의 상태 값을 리턴한다. 실행이 중단되었으므로 더이상 상태값을 리턴하지 않는다. wait함수는 자식프로세스가 여러개일 경우 아무 자식프로세스나 종료하면 리턴한다 waitpid함수는 특정 PID의 자식 프로세스가 종료하기를 기다린다. 이 함수는 pid 로 지정한 자식 프로세스가 종료하기를 기다리며, 자식 프로세스의 종료 상태 값을 stat_loc에 저장하고 , option의 조건에 따라 리턴한다.
waitpid 함수 사용 예제
So you have finished reading the 리눅스 프로세스 생성 과정 topic article, if you find this article useful, please share it. Thank you very much. See more: 리눅스 프로세스 생성 함수, Fork 함수 개념, Linux wait 명령어, 리눅스 부모 프로세스