일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- ecole42
- printf
- 42 pipex
- get next line
- 42 libft
- 에꼴42
- 와인선별방법
- 지베르니 계절 추천
- 42
- push swap
- 포르투갈 여행
- ft_printf
- 지베르니 가을
- str함수
- 파리 피크닉
- push swap 설명
- 굿노트 스티커
- 와인 고르기
- 42 so_long
- gnl
- pipex 42
- so_long
- get_next_line
- 알고리즘 기초
- 서울42
- pipex
- 이지젯
- 지베르니 여름
- 지베르니
- libft
- Today
- Total
뇌 마음 반반저장소
[42_pipex] 함수1. pipe, fork, wait, waitpid 자세한 설명 본문
open, close, read, write, malloc, free, perror, exit, strerror, ft_printf 함수들과 libft 라이브러리는 이미 언급한 적이 있기 때문에 아래의 링크로 확인할 수 있다. 펼치기! 👇
open, close, read, write
[42_GNL] get_next_line 개념 이해하기 (open, read, 버퍼사이즈) 👉 https://sudo-me.tistory.com/24
malloc : 프로그래머가 할당해서 사용하는 heap영역을 지정하는 함수
free
[42] NULL과NUL과 Nil과 0과 \0.... 👉https://sudo-me.tistory.com/7
perror, exit, strerror
[42_so_long] perror, strerror, exit 자세히 알아보기 👉 https://sudo-me.tistory.com/28
ft_printf
https://sudo-me.tistory.com/category/sudo_42/ft_printf
libft 라이브러리
아래의 용어들을 알기 위해 이전 포스트를 읽고 오는 것을 권장합니다!
[42_pipex] 탐구2. 프로세스의 과정과 부모·자식 프로세스
1. pipe
파이프는 말 그대로 파이프, 통로를 여는 함수라고 생각하면 되겠다.
위의 그림을 보면 독자적인 여러 프로세스가 있고 메모리가 각각 할당되어 있기 때문에, 여러 프로세스가 서로 교류를 하는 것은 컴퓨터적으로(?)는 불가능하다. 하지만 이 pipe 함수를 사용하면 통로를 열어줘 부모와 자식이 서로 데이터를 주고받을 수 있다!
#include <unistd.h>
int pipe(int fd[2]);
- 성공 시 0을 반환하고 실패시 -1을 반환한다.
- 이 함수는 크기가 항상 2인 함수여야만 한다. 왜냐하면 들어가는 입력 fd[0]와 출력 fd[1]이 항상 있기 때문이다!
- 파이프 자체는 fork함수에 의해 복사되지 않는다.
- 파이프는 입출구가 정해져 있다. 그래서 항상 시작은 read, 입력을 의미하는 fd[0]이고 끝은 write, 출력을 의미하는 fd[1]이다.
- 파이프를 두 개 만들어 부모가 자식에게, 자식이 부모에게 데이터를 전송할 수 있다.
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
void main()
{
int fd1[2];
int fd2[2];
pipe(fd1); //fd1의 파이프를 열어주고
pipe(fd2); //fd2의 파이프도 열어준다.
printf("fd1 in %d, fd1 out %d\n", fd1[0], fd1[1]);
printf("fd2 in %d, fd2 out %d\n", fd2[0], fd2[1]);
}
$ ./a.out
fd1 in 3, fd1 out 4 //in, out이 차례로 할당되었다.
fd2 in 5, fd2 out 6
2. fork
fork의 어원 중 가지를 나눈다는 뜻이 있다고 한다. (to divide into branches) 나는 뭐 찍어서 뭘 어떻게 하나 추측했었는데 이 함수는 fork함수를 수행하는 부모 프로세스와, 동시에 함께 실행되는 자식 프로세스라는 새 프로세스를 만든다.
#include <unistd.h>
pid_t fork(void)
- fork로 자식을 생성하고, 다음으로 들어오는 호출을 부모부터 자식까지 모두 명령어를 실행한다.
- 자식 프로세스 생성에 실패하면 -1, 성공하면 자식 프로세스의 반환값은 0, 부모는 자식의 주민등록번호(?) PID가 반환된다.
pid_t라는 데이터 타입이 궁금해서 찾아봤다.
pid_t는 pid의 고유번호만 출력해주는 signed int(unsigned int/int) 타입이다! pid_t의 헤더파일을 궁금해서 찾아봤더니, 오호! 구조체가 있었다. 그래서 결국 pid_t는 구조체였다능!
fork의 예제를 보자.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int parent;
int child;
pid_t pid;
pid = fork();
if (pid > 0) //부모 프로세스
{
printf("Parent pid ID : %d\n", getpid()); //getpid : pid 값 가져오는 함수
}
else if (pid == 0) //자식 프로세스
{
printf("Child pid ID : %d\n", getpid());
pid = fork(); // 또 자식 낳아버림
if (pid > 0) //부모가 된 자식프로세스
printf("Child as a parent pid ID : %d\n", getpid());
else if (pid == 0) //손자 프로세스
{
printf("Grand child pid ID : %d\n", getpid());
}
}
}
결과는!
$ ./a.out
Parent pid ID : 515 #부모님
Child pid ID : 516 #자식
Child as a parent pid ID : 516 #부모가 된 자식
Grand child pid ID : 517 #손자
3. wait
위의 부모자식 프로세스 설명 그림을 보면 "부모가 자식을 종료할 수 있어요~"라고 빨간색으로 쓰여있다. 부모가 자식 프로세스를 종료하기 위해서는 wait 함수를 사용한다. 참.. 이 함수는 잔인하게도 자식이 죽을 때까지 기다려준다는 의미이다..! 그것도 아무것도 하지 않은 채... 죽음을 기다려준다..
#include <sys/wait.h>
pid_t wait(int *wstatus);
- 자식 프로세스가 정상적으로 종료한다면 : 반환값은 프로세스 pid ID, 상위 8비트에는 종료되게 한 exit함수의 인수가 기록, 하위 8비트에는 0이 저장된다.
- 자식 프로세스가 비정상적으로 종료 한다면 : 반환값은 프로세스 pid ID, 상위 8비트에는 0이 저장되고, 하위 8비트에는 프로세스를 종료시킨 시그널의 번호가 저장된다.
- wait 함수가 오류가 나면 : 반환값은 -1, 에러 코드 반환
- wstatus의 값을 아는 방법은, 아래의 종료 후 상태를 반환해주는 매크로를 사용하면 된다.
WIF 뒤에 여러 가지 변형이 붙는다. 나는 이 함수의 어원을 알고 싶어서 마구 뒤졌지만 찾지 못했다..
- WIFEXITED(status) : 자식 프로세스가 정상적으로 종료되었다면 0이 아닌 수로 반환된다. main에서 반환하면 참을 반환한다.
- WEXITSTATUS(status) : 자식의 종료코드를 반환한다. 이 매크로는 WIFEXITED(집 나간 아내)가 참을 반환했을 때만 돌아간다. 이 함수는 실제로 무언가를 반환하지 않고 무언가를 평가하는 함수이다.
- WIFSIGNALED(status) : 자식 프로세스가 신호로 인해 종료되었다면 참을 반환한다.
- WTERMSIG(status) : 자식 프로세스가 종료된 신호의 번호를 반환한다. 이 매크로는 WIFSIGNALED(아내의 신호)가 참을 반환한 경우에만 돌아간다.
- WCOREDUMP(status) : 자식이 코어파일을 만든 경우에 참을 반환한다. 이 매크로는 WIFSIGNALED가 참을 반환한 경우에만 돌아간다.
- WIFSTOPPED(status) : 신호 때문에 자식프로세스가 종료된 경우 참을 반환한다. 이 매크로는 WUNTRACED 옵션으로 이루어졌거나 호출이 추적되고 있는 경우에만 의미가 있다.
- WSTOPSIG(status) : 자식을 멈추게 한 신호의 번호를 반환한다. 이 매크로는 WIFSTOPPED(멈춘 아내)가 참을 반환하는 경우에만 돌아간다.
- WIFCONTINUED(status) : 리눅스 2.6.10부터 SIGCONT 신호를 발행하여 하위 프로세스가 다시 시작된 경우 참을 반환한다.
이 함수는 예를 보는 게 더 빠르다. 위의 fork함수에서 wait 함수만 추가하였다.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
int parent;
int child;
int wstatus;
pid_t pid;
pid_t wait_pid;
pid = fork();
if (pid > 0) //부모 프로세스
{
wait_pid = wait(&wstatus); //wait을 걸어줬다.
printf("Parent pid ID : %d\n", getpid());
printf("child pid ID : %d, record of wait : %x\n", wait_pid, wstatus);
}
else if (pid == 0) //자식 프로세스
{
printf("Child pid ID : %d\n", getpid());
pid = fork(); // 또 자식 낳아버림
if (pid > 0) //부모가 된 자식프로세스
printf("Child as a parent pid ID : %d\n", getpid());
else if (pid == 0) //손자 프로세스
{
printf("Grand child pid ID : %d\n", getpid());
}
}
}
리턴값이 흥미롭다.
$ ./a.out
Child pid ID : 631
Child as a parent pid ID : 631
Grand child pid ID : 632
Parent pid ID : 630
child pid ID : 631, record of wait : 0
fork 함수 결과는 부모부터 자식으로 차례대로 내려왔는데, wait 함수를 넣어주니 결과값이 child 먼저 나오고 부모가 가장 나중에 나왔다. 이 말인즉슨 자식을 먼저 다 처리하고 부모로 온다는 뜻이군! wstatus 값은 main에서는 정상종료 되면 참이 반환되기 때문에 0으로 결과가 나온다.
4. waitpid
waitpid 함수는 wait함수보다 업그레이드된 함수이다. 하지만 이것도 자식 프로세스를 기다리고 종료상태를 받고 싶을 때 사용한다. 자식 프로세스와 부모프로세스가 함께 일하고 싶은 경우에 사용할 수 있고, 또한 기다릴 자식 프로세스를 좀 더 상세히 지정할 수 있다.
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
- 성공 시 프로세스 ID를 반환하고, 오류가 발생하면 -1을, 그 외의 경우에는 0을 반환한다.
- 매개변수 1. pid_t pid
- pid > 0 이라면? : pid ID 값의 자식 프로세스를 기다린다.
- pid == 0 이라면? : 프로세스 그룹 ID가 호출 프로세스의 ID와 동일한 자식 프로세스를 기다린다.
- pid < -1 이라면? : 프로세스 그룹 ID가 pid 의 절대값과 같은 자식 프로세스를 기다린다.
- pid == -1 이라면? : 하위 프로세스를 기다린다.
- 매개변수 2. int *status
- wait 함수와 같은 status, 반환값을 사용한다.
- 매개변수 3. int options
- 0 : 옵션 없이 진행한다.
- WNOHANG : 자식 프로세스가 종료되어있지 않으면 바로 리턴하게 해 준다. 강제종료 같은 기능.
- WUNTRACED : 자식이 중지되면 중단된 자식 프로세스의 상태를 반환해 준다.
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
int pid;
int wstatus;
int i;
pid = fork();
i = 0;
if (pid > 0)
{
printf("parent waiting\n");
waitpid(pid, &wstatus, 0);
printf("parents end\n");
return 0;
}
if (pid == 0);
{
while(i < 5)
{
i++;
printf("%d child progressing..\n", i);
}
printf("child end\n");
return(0);
}
}
$ ./a.out
parent waiting
1 child progressing..
2 child progressing..
3 child progressing..
4 child progressing..
5 child progressing..
child end
parents end
5. 코드 합치기
부모와 자식 관계를 함수와 함께 이미지로 정리해봤다.
위에서 습득한 내용을 하나의 코드로 만들어봤다!
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
# define BUFFER_SIZE 1024
void main()
{
int status;
int fd1[2];
int fd2[2];
char str1[BUFFER_SIZE];
char str2[BUFFER_SIZE];
pid_t pid;
pipe(fd1);
pipe(fd2);
fd1[0] = open("./file1.txt", O_RDWR | O_CREAT);
fd2[0] = open("./file2.txt", O_RDWR | O_CREAT);
pid = fork();
if (pid > 0) //부모님
{
waitpid(pid, &status, 0); //자식먼저 일하게 해주세요.
printf("***parents start***\n");
printf("!file1 text to fd2!\n");
read(fd1[0], str1, BUFFER_SIZE); //file1에 있는 txt를 읽어서
write(fd2[1], str1, BUFFER_SIZE); //file2에 써주셈
printf("fd2 : %s\n", str1);
printf("***parents end***\n");
}
if (pid == 0) //자식쓰
{
printf("***child start***\n");
printf("!file2 text to fd1!\n");
read(fd2[0], str2, BUFFER_SIZE); //file2에 있는 txt를 읽어서
write(fd1[1], str2, BUFFER_SIZE); //file1에 써주셈
printf("fd1 : %s\n", str2);
printf("***child end***\n");
}
close(fd1[2]);
close(fd2[2]);
}
결과는?
$ ./a.out
***child start***
!file2 text to fd1!
fd1 : message from child: Hi, I'm from file2
***child end***
***parents start***
!file1 text to fd2!
fd2 : message from parents: Hi, I'm from file1
***parents end***
위의 코드가 이해가 되지 않는다면 아래의 이미지를 참고해보자.
궁금해서 프로세스를 지정해주지 않고 작업을 해보았다.
$ ./a.out
***parents start***
!file1 text to file2!
fd2 : message from parents: Hi, I'm from file1
***parents end***
***child start***
!file2 text to file1!
fd1 : message from child: Hi, I'm from file2
***child end***
그래도 잘 나온다. 하지만 fork함수를 선언하고 부모와 자식을 나눠서 일하게 하지 않았더니 무한루프에 빠졌다.
$ fd1 : ��@`�
***child end***
후! 그럼 나머지 함수를 알아보러 가볼까!
'42 > pipex' 카테고리의 다른 글
[42_pipex] 함수2. dup, dup2, execve, access, unlink 자세한 설명 (0) | 2023.01.07 |
---|---|
[42_pipex] 탐구4. inode, 파일권한(chmod), 링크파일(하드, 심볼릭) 쉽게 알아보기 (0) | 2023.01.07 |
[42_pipex] 탐구3. 부모·자식 프로세스 개념 쉽게 알아보기 (0) | 2023.01.07 |
[42_pipex] 탐구2. 프로세스의 과정 쉽게 알아보기 (0) | 2023.01.06 |
[42_pipex] 탐구1. pipex 구조 자세히 살펴보기 및 예제 (0) | 2023.01.05 |