일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- LazyHGrid
- 세로모드끄기
- 리액트최적화
- react상태관리라이브러리
- BFS
- react hook
- C++
- 페이지전환
- 리렌더링최적화
- GridItem
- 반응형 css
- SwiftUI Font
- featured-sliced-design
- 가로모드끄기
- 컴퓨터네트워크
- @observedobject 프로퍼티 래퍼
- LazyVGrid
- @published 프로퍼티 래퍼
- 비동기함수
- zustand
- CSS
- @environmentobject 프로퍼티 래퍼
- zustand란
- react-router-dom
- react fsd
- navigationBar 숨기기
- 상단 빈공간 제거
- 동기 함수 내에서 비동기 함수 호출
- 페이지이동함수
- 블로그업로드확인
- Today
- Total
leebaek
[UNIX프로그래밍] 7강 - 프로세스 생성과 실행 본문
- fork 시스템 호출 ( Process의 생성 )
- exit 시스템 호출
- exec 시스템 호출
- exec와 fork를 함께 사용
- 프로세스의 동기화
- zombie와 너무 이른 퇴장
■ fork 시스템 호출 ( Process 생성 )
□ fork() : 수행하던 process(부모)의 복사본 process(자식) 생성
- fork() 바로 다음 문장부터 부모 process와 자식 process 동시에 실행
- 자식 process는 부모 process의 복제
- 모든 변수 값이 그대로 복제
- fork() 후에 변경된 값은 복제되지 않음 ( 독립적인 메모리 공간을 가짐 )
- file descriptor도 복제 ( 부모 process에서 오픈한 file 자식 process에게도 open ( 동시에 접근 가능 )
- 부모와 자식이 file을 공통으로 사용 가능 ( file desciptor가 복제된 경우, 한쪽에서 파일의 위치를 변경하면 다른 쪽 프로세스에도 영향을 미칠 수 있음 )
return 값
- 성공
- 0 : 자식 프로세스
- 0보다 큰 값 : 부모 프로세스( process id )
- 실패
- -1
- 실패 원인 : [ 시스템 전체 process의 수 제한 ] [ 한 process가 생성할 수 있는 process 수 제한 ]
> 헤더파일
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
int pid;
pid = fork();
if (pid == 0) {
// 자식 프로세스
} else if (pid > 0){
// 부모 프로세스
}
■ exit 시스템 호출 ( Process 종료 )
□ exit() : process 정지 -> open된 file 닫기 -> clean-up-action( 메모리와 기타 자원 정리 )
- 종료 코드 확인: 터미널에서 echo $? 명령을 사용하여 exit()에 전달된 status 값 확인 가능
ex. exit(3);로 종료 한 경우 -> echo $? 입력시, 3 출력
> 헤더 파일
#include <stdlib.h>
void exit(int status); // status: 0 : 정상 종료 | 0이 아닌 값 : 오류가 발생했음
exit(0); // 정상 종료
□ atexit() : 프로세스가 종료될 때 실행될 함수 등록
- 클린업 함수 등록: 매개변수가 없고 반환값이 없는 함수의 포인터를 받음
- 클린업 순서: 등록된 함수들은 등록된 순서의 역순으로 실행됨
> 헤더파일
#include <stdilb.h>
int atexit(void(*func)(void));
void cleanup1() {
printf("Clean-up action 1\n");
}
void cleanup2() {
printf("Clean-up action 2\n");
}
atexit(cleanup1);
atexit(cleanup2);
exit(0); // 정상 종료, exit 시 cleanup2 -> cleanup1 순서로 실행
■ exec 계열 함수 시스템 호출 ( 새 프로그램 수행 )
- 새로운 프로그램을 현재 실행 중인 프로세스의 주소 공간에 적재하는데 사용
- 기존의 프로세스는 없어지고, 새 프로그램이 기존 프로세스의 프로세스 id를 유지하며 실행됨
> 헤더파일
#include <unistd.h>
□ execl() : 절대 경로를 사용하여 실행
int execl(const char*path, const char *arg0, ..., const char *argn, (char*) 0);
execl("/bin/ls", "ls", "-l", (char *) 0);
□ execlp() : PATH 환경 변수에서 경로를 검색하여 실행
int execlp(const char *file, const char *arg0, ..., const char *argn, (char*) 0);
execlp("ls", "ls", "-l", (char *) 0);
□ execv() : 인수를 배열로 전달하여 실행
int execv(const char *path, char *const argv[]);
char *args[] = {"ls", "-l", NULL};
execv("/bin/ls", args);
□ execvp() : PATH 환경 변수에서 경로를 검색하고 인수를 배열로 전달
int execvp(const char *file, char *const argv[]);
char *args[] = {"ls", "-l", NULL};
execvp("ls", args);
공통점 )
- 새로운 프로그램 적재 : 호출된 프로세스의 메모리 공간이 새로운 프로그램으로 덮어씌워짐
- 호출한 프로세스는 더 이상 존재 X, 새로운 프로그램이 처음부터 실행
- 프로세스 id 유지: 기존 프로세스 id를 이어 받음
- 성공 시 반환값 없음: 실패하면 -1 반환
- fork()와의 차이점 : fork()는 새로운 프로세스를 생성하여 두 프로세스가 병렬로 실행되는 반면, exec 함수는 새로운 프로그램을 기존 프로세스 위에 덮어씌우기 때문에 병렬수행이 아님
차이점 )
- 경로 지정 방식:
- path: execl과 execv는 실행할 프로그램의 절대 경로 또는 상대 경로를 명시
- file: execlp와 execvp는 프로그램의 파일 이름만 명시 가능
- $PATH 환경 변수에 지정된 경로들에서 프로그램 검색
- ex. execvp("ls", ...)은 ls 명령어가 위치한 경로를 $PATH에서 찾아 실행
- 인수 전달 방식:
- arg0, arg1, ..., argn: execl과 execlp는 프로그램에 전달될 인수를 각각의 인수로 나열하여 전달
- arg0: 일반적으로 실행할 프로그램의 이름(경로 없이)을 지정
- 인수 목록의 마지막: NULL 포인터를 명시하여 끝을 알림
- argv[]: execv와 execvp는 인수를 배열로 받아 처리
- 인수들을 배열 형태로 전달
- 배열의 마지막 요소로 NULL 포인터가 포함되어야 함
- arg0, arg1, ..., argn: execl과 execlp는 프로그램에 전달될 인수를 각각의 인수로 나열하여 전달
■ exec와 fork를 함께 사용
#include <unistd.h>
int main() {
pid_t pid;
switch ( pid = fork() ) {
case -1:
perror("fork failed");
exit(1);
break;
case 0:
execl("bin/ls", "ls", "-1", (char*) 0);
perror("exec failed");
exit(1);
break;
defalut:
wait((int*) 0);
printf("ls completed\n");
exit(0);
}
}
■ wait 시스템 호출 ( 프로세스의 동기화 )
□ wait() : 하나 이상의 자식 process 수행 시 아무나 하나가 종료되면, 그 자식의 종료 상태를 얻음
- return 값:
- 성공: 종료된 자식의 id
- 실패: -1 ( 살아있는 child process가 없는 경우 )
- stauts: 자식 process의 종료 상태를 반환하는 포인터
- 매크로 함수 사용하여 분석 가능
- WIFEXITED(status) : 자식 프로세스가 정상 종료했는지 여부를 확인 - statue 값의 하위 8비트가 0이면 정상 종료 ( 종료 여부 )
- WEXITSTATUS(status) : 자식 프로세스가 exit() 호출로 종료되었을 때, 종료 시 전달된 값을 반환 - status 값의 상위 8비트에 저장됨 ( 종료 코드 )
- 하위 8비트는 자식 프로세스가 특정 시그널에 의해 종료되었는지, 또는 비정상적인 이유로 종료되었는지 등을 나타냄
- 상위 8비트는 자식 프로세스가 exit()로 반환한 종료 코드를 나타냅니다.
> 헤더파일
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status); // 자식 프로세스의 종료 상태를 알기 위함
int main() {
pid_t pid;
int status, exit_status;
// 새로운 프로세스 생성 (fork)
if ((pid = fork()) < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) { // 자식 프로세스
sleep(4); // 4초간 대기
exit(5); // 종료 시 상태 5 반환
} else { // 부모 프로세스
if (wait(&status) == -1) { // 자식 프로세스가 종료될 때까지 기다림
perror("wait failed");
exit(2);
}
// 자식 프로세스가 정상 종료되었는지 확인
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status); // 자식이 반환한 종료 코드
printf("Child exited with status: %d\n", exit_status);
}
}
return 0;
}
□waitpid(): 부모 process가 대기할 특정 자식 process의 id를 지정 / -1일 경우 아무 자식 process를 기다림
- return 값:
- 성공: 종료된 자식의 id
- 실패: -1 ( 특정 자식 process가 없는 경우 )
- pid: 부모 프로세스가 대기할 특정 자식 프로세스 id 지정
- stauts: 자식 process의 종료 상태를 저장할 포인터
- options: 추가적인 동작을 제어할 수 있는 플래그
- WNOHANG : 자식 프로세스가 종료되지 않았더라도 즉시 반환 ( with no hang ) / 종료하지 않았으면 0을 return
- 비동기적으로 자식의 종료를 처리할 수 있음
> 헤더파일
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
int main() {
pid_t pid;
int status;
if ((pid = fork()) < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) { // 자식 프로세스
sleep(4); // 4초간 대기
exit(5); // 종료 시 상태 5 반환
} else { // 부모 프로세스
while (1) {
// 자식 프로세스가 종료되었는지 확인
pid_t result = waitpid(pid, &status, WNOHANG);
if (result == 0) {
printf("Child process still running\n");
sleep(1); // 1초 후 다시 확인
} else if (result == -1) {
perror("waitpid failed");
exit(2);
} else {
if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
printf("Child exited with status: %d\n", exit_status);
}
break;
}
}
}
return 0;
}
■ Zombie와 너무 이른 퇴장
□ Zombie
: 부모 프로세스가 wait를 수행하지 않고 있는 상태에서 자식 프로세스가 퇴장 -> 자식은 zombie가 됨
□ 너무 이른 퇴장
:하나 이상의 자식 프로세스가 수행되고 있는 상태에서 부모 프로세스가 퇴장 -> 자식은 init(pid=1)의 자식으로 남게 됨
'수업 정리 > UNIX 프로그래밍' 카테고리의 다른 글
[UNIX 프로그래밍] 9강 - 메모리 매핑 (0) | 2024.11.17 |
---|---|
[UNIX프로그래밍] 8강 - 시그널 (0) | 2024.11.17 |
[UNIX프로그래밍] 6강 - 프로세스 정보 (7) | 2024.10.16 |
[UNIX프로그래밍] 5강 - 시스템 정보 (1) | 2024.10.15 |
[UNIX프로그래밍] 4강 - 파일 입출력 (0) | 2024.10.15 |