Process의 생성
- fork 시스템 호출 :
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
//아무런 인자도 주어지지 않음
//fork를 호출한 프로세스를 parent 프로세스라고 부름
//새로 생성되는 프로세스를 child 프로세스라고 부름
// 이 두 개는 정확히 같음
//parent 프로세스가 자기 자신을 그대로 복사해서 child 프로세스를 만들어내기 때문
//프로그램을 main 함수의 첫 번째 줄부터 시작하지 않음
- 수행되던 process(parent)의 복사본 process(child) 생성
- fork() 바로 다음 문장부터 동시에 실행
- 두 process의 차이점 :
- pid와 ppid가 다르다
- fork()의 return 값이 다르다.
- parent process의 return 값은 child process의 process id
- child process의 return 값은 0이다
- fork 실패 시 -1 return
- 실패 원인 :
- 시스템 전체 process의 수 제한
- 한 process가 생성할 수 있는 process 수 제한
- 실패 원인 :
int main(void) {
pid_t pid;
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
pid = fork();
if (pid == 0) {
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
return 0;
}
wait(0); //child프로세스가 종료할때까지 대기
return 0;
}
int main(void) {
pid_t pid;
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
pid = fork();
if (pid == 0) {
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
sleep(5); //5초동안 쉼
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
return 0;
}
sleep(1); //1초동안 쉼 -> wait이 있어야 함
return 0;
}
//안 좋은 코드
int main(int argc, char **argv) {
int i, N;
pid_t pid;
N = atoi(argv[1]);
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
for (i = 0; i < N; i++) {
pid = fork();
if (pid == 0) {
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
}
}
for (i = 0; i < N; i++) { //N개의 child 만들었으므로 N번 wait
if (pid > 0) {
wait(0);
}
}
return 0;
}
//parent와 child가 작업하는 코드를 분리해야함
//child 함수 마지막에는 exit 필수
void do_child(void) {
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
exit(0); //필수
}
int main(int argc, char **argv) {
int i, N;
pid_t pid;
N = atoi(argv[1]);
printf("pid=%ld ... ppid=%ld\n", getpid(), getppid());
for (i = 0; i < N; i++) {
pid = fork();
if (pid == 0)
do_child();
}
for (i = 0; i < N; i++) {
wait(0);
}
return 0;
}
fork : 파일과 자료
- child process는 parent process의 복제
- 모든 변수 값이 그대로 복제된다.
- fork()후에 변경된 값은 복제되지 않는다 (완전히 분리된 별개의 두 프로세스가 됨)
- file descriptor도 복제된다
- parent process가 open한 file은 child process에게도 open
- parent와 child가 file을 공통으로 사용 가능
int main(void) {
int fd;
pid_t pid;
char buf[10];
fd = open("data", O_RDONLY);
read(fd, buf, 10);
printf("befork: %ld\n", lseek(fd, (off_t)0, SEEK_CUR)); //10출력
switch (pid == fork()) {
case -1: perror("fork failed\n");
exit(1);
break;
case 0: printf("child before read: %ld\n", lseek(fd, (off_t)0, SEEK_CUR));
read(fd, buf, 10);
printf("child after read: %ld\n", lseek(fd, (off_t)0, SEEK_CUR));
break;
default: wait((int*)0);
printf("parent after wait: %ld\n", lseek(fd, (off_t)0, SEEK_CUR));
}
return 0;
}
/*
before fork : 10
child before read : 10
child after read : 20
parent after wait : 20
*/
exit 시스템 호출
#include <stdlib.h>
void exit(int status);
//return : 함수를 호출한 위치로 되돌아감
//exit : 무조건 그냥 종료.
//status : child가 parent한테 어떻게 종료했는지 전달하는 숫자 (0~255)
//exit하는 위치별로 status 숫자를 다르게 써줘야함
- 사용법:
- exit: process 정지→ open된 file 닫기→ clean-up-action
- status의 값은 프로세스 종료 후, $ echo $?
- 명령에 의해 알아낼 수 있다.
- clean-up-action 지정 :
#include <stdlib.h>
int atexit(void (*func) (void));
- 지정된 순서의 역순으로 실행
void ABC(void) {
printf("ABC ...\n");
}
void DEF(void) {
printf("DEF ...\n");
}
void GHI(void) {
printf("GHI ...\n");
}
int main(void) {
atexit(ABC);
atexit(DEF);
atexit(GHI);
exit(15);
}
//ghi def abc 순서대로 실행됨
exec을 이용하여 새 프로그램을 수행
(프로그램이 다른 프로그램으로 변신하는 시스템 call)
- 사용법 :
#include <unistd.h>
int execl(const char *path, const char *arg0, ..., const char *argn, (char *) 0);
int execlp(const char *file, const char *arg0, ..., const char *argn, (char *) 0);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
//네 가지 다 새로운 프로그램을 실행시키는 것은 동일함
//인자를 사용하는 방법과 파일 이름을 사용하는 방법에 차이가 있음
//l과 lp는 인자값들을 전부 다 적어줌 (첫번째 인자는 실행시키는 프로그램의 이름, 마지막은 널)
//v와 vp는 포인터 배열을 인자값으로 넣어줌, 포인터 배열의 이름만 줌
//l과 v는 (p가 없으면) 파일의 정확한 경로를 적음 (파일의 이름만 적을 수 없음)
- 공통점 :
- 호출 프로세스의 주기억장치 공간에 새로운 프로그램을 적재
→ 호출 프로세스는 no longer exists.
→ 새 process는 처음부터 실행
→ 새 process는 호출 프로세스의 id로 실행 - 실패 시 -1 return; 성공 시 return이 없다
- fork와의 차이점 : 기존 프로세스와 병렬 수행이 아니다.
- 호출 프로세스의 주기억장치 공간에 새로운 프로그램을 적재
- 차이점 :
- path : 파일의 경로 이름 포함; vs. file : 파일 이름
- 인수 :
- arg0 : 프로그램 이름(경로 이름 빼고); and arg1,...,argn : 프로그램에 입력으로 사용될 인수들, 마지막엔 null pointer
- argv[] : 배열로 받기
- file 이름을 쓰는 경우는? 환경 변수에 의해 설정된 path안의 file; ($echo $PATH)
int main(void) {
execl("./test1", "test1", "3", "5", "7", (char*)0);
printf("execution(execl) fails...\n");
exit(1);
}
int main(void) {
char* const av[] = { "test1", "3", "5", "7", (char*)0 };
execv("./test1", av);
printf("execution(execv) fails...\n");
exit(1);
}
int main(void) {
char* const av[] = { "test1", "3", "5", "7", (char*)0 };
execv("./test1", av);
exit(1);
}
int main(void) {
char* const av[] = { "test1", "3", "5", "7", (char*)0 };
execv("test1", av);
exit(1);
}
int main(void) {
char* const av[] = { "test1", "3", "5", "7", (char*)0 };
execvp("./test1", av);
exit(1);
}
int main(void) {
char* const av[] = { "test1", "3", "5", "7", (char*)0 };
execvp("test1", av);
exit(1);
}
'전공 > 유닉스' 카테고리의 다른 글
5장. 프로세스 정보 (0) | 2023.11.01 |
---|---|
4장. 시스템 정보 (0) | 2023.11.01 |
3장. 파일과 디렉토리 (1) | 2023.11.01 |
2장. 파일 입출력 (2) (1) | 2023.10.23 |
2장. 파일 입출력 (1) (1) | 2023.10.05 |
댓글