프로세스간 통신(IPC) 방법 중 하나인 pipe를 이용한 프로그램을 완성하는 과제
원본 링크 : https://jewel-draw-b30.notion.site/pipex-5770547891954e81bb3d1b621921342f
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
typedef struct s_exec {
char **cmd_split;
char *cmd_path;
int fd;
int status;
} t_exec;
typedef struct s_pipe {
int write_fd;
int pipe_fd[2];
pid_t pid[2];
int status;
} t_pipe;
이미 존재하는 파일을 열거나 새로운 파일을 생성하는 system call 함수.
open으로 열린 파일은 read, write, lseek, close의 함수와 사용 가능하다.
#include <fcntl.h>
int open(const char *pathname, int flags);
pathname
: open할 파일에 대한 절대경로의 파일명 혹은 상대경로 파일명
flag
반환값
0 이상 : 정상적으로 파일을 열었으며, 열려진 파일의 fd값을 반환
-1 : 오류 발생. 상세한 오류는 errno에 설정됨.
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
int fd_hello;
int fd_test;
fd_hello = open("hello.txt", O_RDONLY);
printf("fd_hello : %d\n", fd_hello);
//OUTPUT : 3
fd_test = open("test.txt", O_RDONLY);
printf("fd_test : %d\n", fd_test);
//OUTPUT : 4
return 0;
}
따라서 0, 1, 2는 시스템적으로 건들면 안되는 플래그이므로 정상적으로 open 한 파일의 위치를 가리키는 fd는 3부터 부여된다.
추가적으로, 동일한 파일은 두 번 open하면 3, 4 차례로 fd값이 부여됨.
open()함수로 열기한 파일을 사용 중지함.
#include <unistd.h>
int close(int fd);
fd
: 닫기할 파일의 fd 값
반환값
open()함수로 열기한 파일의 내용을 읽기함
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
fd
: 파일 디스크립터
*buf
: 파일을 읽어들일 버퍼. 어떤 자료형을 읽어들일지 몰라 void *로 설정돼있음.
ex) int형 자료를 받고자하면 매개변수의 buf를 int형 배열로 선언해주면 됨.
nbytes
: 버퍼의 크기. 읽어들일 글자 수
반환값
사용 예시
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd_hello;
int fd_test;
int readbuf_hello;
int readbuf_test;
char buf[100];
//정상적으로 open되었는지 확인
fd_hello = open("hello.txt", O_RDONLY);
fd_test = open("test.txt", O_RDONLY);
printf("hello, test : %d, %d\n", fd_hello, fd_test);
readbuf_hello = read(fd_hello, buf, 5);
readbuf_test = read(fd_test, buf, 4);
printf("hello : %d\n", readbuf_hello);
//OUTPUT : 5
printf("test : %d\n", readbuf_test);
//OUTPUT : 4
close(fd_hello);
close(fd_test);
return 0;
}
fd를 복제하는 함수
#include <unistd.h>
int dup(int fd);
fd
: 전달받아 복제해 반환할 d값. 사용하지 않는 디스크립터 번호 하나가 자동으로 지정됨
반환값
사용 예시
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd_hello;
int fd_test;
int fd_duphello;
int fd_duptest;
fd_hello = open("hello.txt", O_RDONLY);
fd_test = open("test.txt", O_RDONLY);
if (fd_hello == -1 || fd_test == -1)
printf("fail to open file\n");
fd_duphello = dup(fd_hello);
fd_duptest = dup(fd_test);
printf("duphello : %d, duptest : %d\n", fd_duphello, fd_duptest);
//OUTPUT : 5. 6
write(fd_duphello, "duphello", 8);
write(fd_duptest, "duptest", 7);
return 0;
}
fd를 새 fd값으로 복제하는 함수
#include <unistd.h>
int dup2(int fd, int fd2);
💡 [https://www.it-note.kr/5](https://www.it-note.kr/5)
[https://nomad-programmer.tistory.com/109](https://nomad-programmer.tistory.com/109)
filename이 가리키는 파일을 실행하는 함수.
execve를 불러낸 프로세스가 execve의 프로세스로 교체됨.
pipex에서는 메인 프로세스가 교체되고 종료되는 것을 방지하기 위해 fork로 자식 프로세스를 생성, 자식프로세스에서 execve를 실행
#include <unistd.h>
int execve(const char *filename, const *char argv[], char *const envp[]);
file
: 파일 이름. path. 실행가능한 binary(바이너리 실행파일)거나 shell(스크립트 파일)이어야 함.
argv[]
: 파일인자의 포인터
envp[]
: 환경변수의 포인터. 마지막은 NULL이어야함
실행예시
argv와 envp는 포인터 배열로 filename의 인자로 들어감. 마지막에 NULL 문자열 저장.
아래 코드는 /bin/sh 를 실행시키는 코드
#include <unistd.h>
int main(void)
{
char *str[2];
str[0] = "/bin/sh";
str[1] = NULL;
execve(str[0], str, NULL);
}
현재 실행되는 프로세스에 대해 복사본 프로세스를 생성하는 함수
#include <unistd.h>
pid_t fork(void);
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int counter = 0;
pid_t pid;
printf("create child process\n");
pid = fork();
switch(pid)
{
case -1 :
printf("fail to create child process\n");
return (-1);
case 0 :
printf("countdown with child process\n");
while (1)
{
printf("child : %d\n", counter--);
sleep(1);
}
default :
printf("countdown with parent process\n");
printf("child's pid is %d\n", pid);
while (1)
{
printf("parent : %d\n", counter++);
sleep(1);
}
}
return (0);
}
create child process
countdown with parent process
child's pid is 59932
parent : 0
countdown with child process
child : 0
child : -1
parent : 1
child : -2
parent : 2
child : -3
parent : 3
child : -4
parent : 4
child : -5
parent : 5
child : -6
parent : 6
child : -7
parent : 7
child : -8
parent : 8
^C
오류 메세지를 출력하는 함수. 전역변수 errono의 값을 해석해 이에 해당하는 시스템 오류 메세지를 stderr에 출력.
#include <stdio.h>
void perror(const char *str);
str
: 시스템 오류 메시지 다음에 이어 출력할 사용자 정의 문자열 메세지. 관습적으로 프로그램의 이름이 인자로 사용됨.
사용 예시
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd_hello;
fd_hello = open("no.txt", O_RDONLY);
if (fd_hello < 0)
perror("There is no file named no\n");
else
close(fd_hello);
return (0);
}
There is no file named no
: No such file or directory
오류 메세지 문자열을 가리키는 포인터를 반환하는 함수. errnum의 값을 통해 발생했던 오류에 알맞은 오류 메세지를 가리키는 포인터를 리턴.
#include <string.h>
char *strerror(int errnum);
errnum
: 오류 번호
반환값
: 오류 번호에 해당하는 오류 문자열을 가리키는 포인터
사용 예시
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main(void)
{
int fd_hello;
fd_hello = open("no.txt", O_RDONLY);
if (fd_hello < 0)
printf("no file named no : %s\n", strerror(errno));
return (0);
}
no file named no : No such file or directory
정상적으로 프로세스를 종료시키는 함수
프로그램 종료 → 열려있는 파일 스트림을 닫고 atexit함수로 등록한 함수들을 수행
프로세스 내 파일입출력 중인 것을 저장함과 동시에 프로세스를 종료하며 운영체제에 권한을 넘김
#include <stdlib.h>
void exit(int status);
status
: 호스트 환경에게 알려줄 종료 값. main의 리턴값과 똑같음
return과 의 차이
: exit()함수는 바로 프로세스 종료. return ()은 뒤 문장을 실행하며 종료.
#include <unistd.h>
int pipe(int fd[2]);
파일 또는 디렉토리의 사용자 권한을 체크하는 함수
#include <unistd.h>
int access(const char *pathname, int mode);
pathname
: 체크하고자할 디렉토리 또는 파일명
mode
반환값
사용예시
int main(void)
{
int mode = R_OK;
if (access("hello.txt", mode) == 0)
printf(" can read and write\n");
else
printf("you don't have any right");
return 0;
}
sujilee@MacBook-Pro:~/Desktop(master⚡) » ./a.out
can read and write
파일을 삭제하는 system call 함수
#include <unistd.h>
int unlink(const char *pathname);
pathname
: 삭제할 파일명
반환값
사용 예시
#include <stdio.h>
#include <unistd.h>
int main(void)
{
if (unlink("hello.txt") == 0)
printf("successfuly unlinked\n");
else
printf("unlink faliled\n");
return 0;
}
자식 프로세스 작업이 끝날때까지 대기하며, 자식 프로세스가 종료한 상태를 구하는 함수
#include <sys/wait.h>
pid_t wait(int *status);
status
: 자식 프로세스의 종료 상태. 굳이 알 필요 없으면 NULL전달.
반환값
실행 예시
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int pid = fork();
//pid == 0 : 본인이 자식 프로세스란 뜻
if (pid == 0)
{
printf("parent = %d, child = %d\n", getpid(), getppid());
while (1)
return (0);
}
printf("parent = %d\n", getpid());
wait(NULL);
return (0);
}
sujilee@MacBook-Pro:~/Desktop(master⚡) » ./a.out
parent = 69199
parent = 69200, child = 69199
부모는 자식 프로세스가 종료될 때까지 계속 블록에 걸려있다.
좀 더 세부적인 wait 함수라고 생각하면 됨
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int option);
pid
감시할 자식 프로세스 id
status
자식 프로세스의 종료 상태 정보
option
대기를 위한 옵션
반환값
-1 : 오류. 지정한 pid의 프로세스 또는 프로세스 그룹이 없을 경우 발생 & pid가 자식프로세스가 아닐 경우 발생
0 : WHOHANG을 사용했고 자식 프로세스가 종료되지 않았을 경우
0 이상 : 정상 종료된 자식 프로세스 id
💡 [https://kirkim.github.io/42seoul/2021/08/04/pipex_func.html](https://kirkim.github.io/42seoul/2021/08/04/pipex_func.html)</aside>
execve()함수의 세번째 인자로 받을 환경변수를 어떻게 처리할 것이냐...
프로그램에서 환경변수를 불러오는 방법 중 main()의 매개변수를 추가하는 방법을 택했다. 왜냐? 젤 간단해보임
#include <stdio.h>
int main(int argc, char **envp)
{
int i;
i = 0;
while (i < 80)
{
printf("%d >> %s\n", i, envp[i]);
i++;
}
return 0;
}
OUTPUT
sujilee@MacBook-Pro:~/Desktop(master⚡) » ./a.out
0 >> ./a.out
1 >> (null)
2 >> TERM_SESSION_ID=w1t0p0:4DB52014-E1B9-4FDE-AE82-5D45EC2B6A02
3 >> SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.6eedkvzIF7/Listeners
4 >> LC_TERMINAL_VERSION=3.4.15
5 >> COLORFGBG=7;0
6 >> ITERM_PROFILE=Default
7 >> XPC_FLAGS=0x0
8 >> LANG=ko_KR.UTF-8
9 >> PWD=/Users/sujilee/Desktop
10 >> SHELL=/bin/zsh
11 >> __CFBundleIdentifier=com.googlecode.iterm2
12 >> TERM_PROGRAM_VERSION=3.4.15
13 >> TERM_PROGRAM=iTerm.app
14 >> PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/bin:/Library/Apple/usr/bin
15 >> LC_TERMINAL=iTerm2
16 >> COLORTERM=truecolor
17 >> COMMAND_MODE=unix2003
18 >> TERM=xterm-256color
19 >> HOME=/Users/sujilee
20 >> TMPDIR=/var/folders/3h/zdj9rkg1549csmzrgzrw6dk40000gn/T/
21 >> USER=sujilee
22 >> XPC_SERVICE_NAME=0
23 >> LOGNAME=sujilee
24 >> ITERM_SESSION_ID=w1t0p0:4DB52014-E1B9-4FDE-AE82-5D45EC2B6A02
25 >> __CF_USER_TEXT_ENCODING=0x0:3:51
26 >> SHLVL=1
27 >> OLDPWD=/Users/sujilee
28 >> ZSH=/Users/sujilee/.oh-my-zsh
29 >> PAGER=less
30 >> LESS=-R
31 >> LSCOLORS=Gxfxcxdxbxegedabagacad
32 >> _=/Users/sujilee/Desktop/./a.out
33 >> (null)
34 >> executable_path=./a.out
35 >>
36 >>
37 >>
38 >> ptr_munge=
39 >> main_stack=
40 >> executable_file=0x1a01000004,0x2cb154
41 >> dyld_file=0x1a01000004,0xfffffff000d8a2d
42 >> th_port=
43 >> (null)
[1] 72371 segmentation fault ./a.out