-
시스템 프로그래밍 실습 8주차 : IPCSystem Programming/Ubuntu Linux 2021. 10. 17. 14:04728x90
시스템 프로그래밍 실습 8주차 : IPC
[목차]
- IPC
- Open Files in Kernel
- I/O Redirection
- Pipes
- Anonymous Pipe
- Named Pipe (FIFOs)
IPC란?
IPC = Inter Process Communication!
- 프로세스들끼리 데이터를 주고 받는 기능
- IPC의 방법들
1. signals (7주차)
2. I/O Directions (3주차, 8주차)
3. Anonymous Pipe (3주차, 8주차)
4. Named Pipe (FIFO) (3주차, 8주차)
5. Shared Memory, Message Queue, etc.
https://doitnow-man.tistory.com/110
[프로세스간 통신] IPC(inter process communication) 종류
[프로세스간 통신] IPC(inter process communication) 종류 (Linux) 1. IPC의 종류 1) PIPE 2) Named PIPE 3) Message Queue 4) Shared Memory 5) Memory Map 6) socket 2. IPC 별 사용 시기 및 특징 IPC 종류 ..
doitnow-man.tistory.com
Open Files in Kernel
* 사실 모든 것들은 file descriptor로 표현할 수 있다!
유닉스 커널에서는 파일 여는 것을 다음과 같은 3단계로 표현한다
1) Descriptor table
- 프로세스 당 하나의 테이블
- open file table에 있는 entry를 가리킨다
2) Open File table
- 모든 프로세스들에서 공유된다
- v-node table에 있는 entry를 가리키며, 현재 파일 위치와 mode, reference count 등의 정보를 가지고 있다
3) v-node table
- 모든 프로세스들에서 공유된다
- 그 파일 자체에 대한 정보(size, permission, type, ....)를 가지고 있다
예시 1) Calling open( ) twice with the same filename / 하나의 프로세스에서 같은 파일 이름을 open()로 두번 불렀을 때
- 같은 파일을 두 번 열면 서로 다른 파일 디스크립터를 부여하고 서로 다른 파일 오프셋을 유지. 따라서 프로세스에서 파일 입출력은 open 함수로 연 작업을 구분하는 것이며 실제 물리적인 파일이 같은지는 구분하지 않는다
- 이 때 프로세스의 파일 디스크립터 테이블에는 각각의 파일 디스크립터를 부여하고 커널에서도 각각의 파일의 상태와 현재 작업 위치를 별도로 갖는다. 다만 이 둘은 같은 vnode를 참조!예시 2) Calling fork() / fork() 함수를 불렀을 때
- fork() 함수에 의해 프로세스가 부모 프로세스, 자식 프로세스 두 개가 된다. 이때 부모 프로세스와 자식 프로세스는 fork() 호출 시에 File Descriptor 를 공유하게 되며, 즉 자식 프로세스와 부모 프로세스가 같은 파이프를 가리킴
- 따라서 이 경우에는 결과값은 y가 출력될 것이다
[리눅스/유닉스 시스템 프로그래밍] 파일 테이블과 파일 디스크립터 테이블
3.5 파일 테이블과 파일 디스크립터 테이블 리눅스 시스템에서는 프로세스마다 파일 디스크립터 테이블을 갖고 있습니다. 그리고 파일 디스크립터 테이블에는 open에서 사용한 flags와 파일 테이
ehclub.co.kr
I/O Redirection
3주차에서도 한번 언급되었다! (https://asidefine.tistory.com/70)
간단하게 정리하자면, 표준 입력과 출력의 방향을 재지정한다. 즉, 모니터로 나와야하는 걸 파일로 지정할 수 있다!
( = standard I/O를 파일들로 리다이렉트한다 )
ls > foo.txt grep include < ex.c
- > : redirect stdout for overwrite (create if not exist) : stdout 결과를 파일에 덮어쓴다
- >> : redirect stdout for append : stdout 결과를 파일에 추가하여 쓴다
- < : redirect stdin
- 2> : redirect stderr
* dup2() 함수 = file descriptor 복제 함수 (이걸로 I/O direction을 구현할 수 있다)
#include <unistd.h> int dup2(int fd, int fd2);
첫 번째 인자로 열려진 파일 디스크립터를 전달하고 두 번째 인자로 파일 디스크립터를 전달하면,
첫 번째 인자로 열려진 파일 디스크립터가 참조하는 파일 테이블 엔트리를 두 번째 전달한 파일 디스크립터도 참조.
만약 두 번째 인자로 전달한 파일 디스크립터가 열려진 파일 디스크립터일 때는 먼저 닫고 난 후에 복제.
예시 1)
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> int main(int argc,char **argv) { int fd = 0; if(argc != 2) { fprintf(stderr,"usage: %s [file name]\n",argv[0]); return 1; } fd= open(argv[1],O_WRONLY|O_CREAT|O_TRUNC); if(fd == -1) { perror("failed open "); return 1; } if(dup2(fd,STDOUT_FILENO) == -1) { perror("failed dup2"); return 1; } printf("Hello World\n"); close(fd); return 0; }
- dup2(fd, STDOUT_FILENO);를 호출한 후에 printf 함수를 호출하면 fd로 연 파일에 쓰여진다.
예시 2)
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> int main(void){ int fd1, ret; char message[32]={"STDERR from fd1\n"}; //그림 1번 fd1=open("made_by_fd1",O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); if(fd1<0){ printf("file open error\n"); exit(0); } //표준 입출력으로 print됨 printf("file open\n"); //fd1의 파일 디스크립터가 명시한 STDOUT_FILENO의 파일 디스크립터로 //복제됨, //그림 2번 ret=dup2(fd1,STDOUT_FILENO); //fd1으로 출력됨 printf("fd1 :%d, ret:%d\n",fd1,ret); //STDERR_FILENO 디스크립터가 명시된 fd1으로 복제됨 //그림 3번 ret=dup2(STDERR_FILENO,fd1); //fd1은 에러로 출력됨 write(fd1,message,strlen(message)); //stdout이 file로 써짐 printf("printf를 썼지만 파일에 기록됨 \n"); close(fd1); }
- 우선 파일을 여는데 위의 예제와 같은 이름으로 파일을 엽니다. 그렇다면 fd1은 3이 되겠네요.
- dup2로 STDOUT_FILENO라는 파일 서술자를 명시된 fd1로 바꿔버립니다. dup2를 조금 더 쉽게 이해하려면 두 번째인자가 첫 번째 인자로 가리키는 화살표 방향이 바뀐다라고 이해하시면 됩니다. 그리고 dup2는 성공적으로 호출이 되면 두 번째 인자의 값을 반환합니다. 실패시 -1을 반환하므로 에러 처리는 필수인데 저는 귀찮아서 하지 않았습니다.
printf는 표준 출력인데 printf로 문자열을 출력한다면 fd1으로 출력하는 것과 같습니다. 그러니까 우리가 만든 파일로 출력이 되겠네요.
- 이제 fd1을 표준 에러로 redirect합니다. 방향을 바꿔버린다는 것이죠. 그렇게 되어 fd1으로 메시지를 출력하게 되면 표준 에러로 메시지를 출력하는 것과 같습니다.
$ gcc dup2_test.c $ ./a.out file open STDERR from fd1 $ cat made_by_fd1 fd1 :3, ret:1 # printf를 썼지만 파일에 기록됨
예시 3)
- dup2 호출 전
- dup2 호출 후
https://architectophile.tistory.com/10
[리눅스] 명령 실행 원리 3 : I/O 리다이렉션
[리눅스] 명령 실행 원리 3 : I/O 리다이렉션(Redirection) 1. I/O 리다이렉션 소개 이번 리눅스 명령 실행 원리 포스트에서 마지막으로 다룰 주제는 바로 리다이렉션(redirection) 이다. 앞서 보았던 포스
architectophile.tistory.com
https://reakwon.tistory.com/104
[리눅스] dup, dup2 설명 및 쉬운 사용법, 사용 예제(그림 포함)
파일 서술자 복제 함수 dup, dup2 함수 이름에서도 알 수 있듯이 무엇을 복제한다는 함수입니다. 무엇을 복제할까요? 바로 파일 서술자(file descriptor)입니다. 함수 이름이 너무 심플하네요. 이 함수
reakwon.tistory.com
Shell - Redirect 구현하기
아는 후배, Linux 과제 도와주다가.. redirect 관련 썼던 게시물이 없어서 한번 정리해서 올려본다. 1. Output redirection Output redirection은, 프로그램 결과를 특정 file descriptor에 write 하는것이다. 예..
sosal.kr
Pipe
* Pipe도 3주차에서도 한번 언급되었다! (https://asidefine.tistory.com/70)
한 프로세스의 output을 다른 프로세스의 input으로 넣을 수 있다!
$ cat test.txt | grep "a"
-> test.txt의 내용을 화면에 보여주고, test.txt로부터 문자열 a가 있는 곳을 다 검색한다
- 여러 개의 프로세스가 공통으로 사용하는 임시공간
- 임시 공간은 실제로 파일 시스템에 생성되는 임시 파일
- 하나의 프로세스가 파이프에 쓰게 되면 다른 프로세스는 그 파이프에서 읽는 방식으로 쓰게 됨
- 파이프는 시스템 내부에서 관리하는 임시 파일을 이용하므로, 다른 IPC 기법 중 하나인 SIGNAL 과는 다르게 대용량의 메시지도 전송이 가능
Pipe에는 두 가지 종류가 있는데 Anonymous pipe와 Named pipe(=FIFO)가 그것이다!
Pipes - Anonymous Pipe
Anonymous pipe는 일시적이고, 부모 프로세스와 자식 프로세스 간의 IPC이라 할 수 있다
① 파이프는 입구와 출구의 개념이 없음
- 방향성이 없기 때문에 자신이 쓴 메시지를 자신이 읽을 수 있음
- 두 개의 프로세스가 통신할 때는 읽기전용 파이프와 쓰기 전용 파이프의 두 개의 파이프를 사용, 두 개의 파이프를 생성하고, 한쪽 파이프를 일부러 닫아 버리는 것
- 파이프를 두 개 생성하여 하나의 방향으로만 갈 수 있도록 함 -> "pipe() 함수를 통해! "
* pipe() 함수
- pipe를 두 개 생성하여 하나는 읽기용 파이프 fd[0], 다른 하나는 쓰기용 파이프 fd[1]로 사용하기 위해 pipe() 함수를 사용한다!
- 쓰기용 파이프 fd[1]의 output은 읽기용 파이프 fd[0]의 input이 된다!② 파이프는 fork() 함수에 의해 복사 되지 않음
- fork() 함수에 의해서 프로세스가 생성되면, 자식 프로세스는 부모가 사용하던 변수를 복사하게 됨
- 파이프의 경우 복사되는 것이 아니라 File Descriptor 를 공유하게 되며, 즉 자식 프로세스와 부모 프로세스가 같은 파이프를 가리킴
③ 부모 자식프로세스 사이에서만 파이프를 사용 가능 (단점)
- 파이프는 운영체제에서 임시로 생성되는 파일이고, 접근 가능한 방법은 File Descriptor(파일 디스크립터) 를 공유하는 방법만이 존재함
- 익명의 파이프는 부모와 자식 프로세스만이 파일 디스크립터를 공유하므로 다른 프로세스는 파이프를 사용하여 통신이 불가능
※ Named PIPE 의 경우, 다른 프로세스도 지정한 이름으로 파일 디스크립터를 열수 있기 때문에, 부모 자식 프로세스 간이 아니더라도 사용할 수 있음
- 부모 프로세스에서 자식 프로세스로 보낼 때
위의 pipe 함수를 사용하여 생성한 fd[0]과 fd[1] 중,
부모는 자식에게 써서 줘야 하기 때문에 쓰기용 파이프 fd[1]를 이용하고, 읽기용 파이프 fd[0]을 닫는다
자식은 부모가 준 것을 읽어야 하기 때문에 읽기용 파이프 fd[0]를 이용하고, 쓰기용 파이프 fd[1]을 닫는다
- 자식 프로세스에서 부모 프로세스로 보낼 때
위의 pipe 함수를 사용하여 생성한 fd[0]과 fd[1] 중,
부모는 자식이 준 것을 읽어야 하기 때문에 읽기용 파이프 fd[0]를 이용하고, 쓰기용 파이프 fd[1]을 닫는다
자식은 부모에게 써서 줘야 하기 때문에 쓰기용 파이프 fd[1]를 이용하고, 읽기용 파이프 fd[0]을 닫는다
예시 1)
- 자식 프로세스에서 쓰기용 파이프 fd[1]를 닫고 부모 프로세스에선 읽기용 파이프 fd[0]을 닫았기 때문에, "부모가 쓴 것을 자식 프로세스에서 읽는" 코드라고 할 수 있다.
예시 2)
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> int main(void) { int fd[2], nbytes, rc = 0; pid_t childpid; char string[] = "Hello, world!\n"; char readbuffer[80]; if ((rc = pipe(fd)) < 0) { printf("Creating Pipe is Error [%d]\n", rc); } if((childpid = fork()) == -1) { perror("fork"); return 0; } if (childpid == 0) { /* 자식 프로세스는 Write할것이기에 Read FD는 닫아준다 */ close(fd[0]); /* Pipe에 메시지 보내기 */ write(fd[1], string, (strlen(string)+1)); return 0; } else { /* 부모 프로세스는 Read할것이기에 Write FD는 닫아준다 */ close(fd[1]); /* Pipe에서 메시지 읽기 */ nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); printf("Received Parent string: %s [%d]", readbuffer, nbytes); } return 0; }
예시 3)
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define MAX_BUF 1024 #define READ 0 #define WRITE 1 int main(){ int fdA[2],fdB[2]; pid_t pid; char buf[MAX_BUF]; int count=0; if(pipe(fdA) < 0){ printf("pipe error\n"); exit(1); } if(pipe(fdB) < 0){ printf("pipe error\n"); exit(1); } if((pid=fork())<0){ printf("fork error\n"); exit(1); } printf("\n"); if(pid>0){ //parent process close(fdA[READ]); close(fdB[WRITE]); while(1){ sprintf(buf,"parent %d",count++); write(fdA[WRITE],buf,MAX_BUF); memset(buf,0,sizeof(buf)); read(fdB[READ],buf,MAX_BUF); printf("parent got message : %s\n",buf); sleep(1); } }else{ //child process close(fdA[WRITE]); close(fdB[READ]); count=100000; while(1){ sprintf(buf,"child %d",count++); write(fdB[WRITE],buf,MAX_BUF); memset(buf,0,sizeof(buf)); read(fdA[READ],buf,MAX_BUF); printf("\tchild got message : %s\n",buf); sleep(1); } } exit(0); }
https://munsonghabnida.tistory.com/86
시스템프로그래밍 11주차
1교시 [파이프 개념] 파이프 개념 및 종류 1. 파이프 1) 개념 - 여러 개의 프로세스가 공통으로 사용하는 임시공간 - 임시 공간은 실제로 파일 시스템에 생성되는 임시 파일 - 하나의 프로세스가 파
munsonghabnida.tistory.com
https://doitnow-man.tistory.com/121
[IPC] pipe 예제 코드
[IPC] pipe 예제 코드 1. 구조 - 부모 프로세스와 자식 프로세스간에 통신을 할때 사용 한다. - 자세한 설명은 다음 포스트 참조 [프로세스간 통신] IPC(inter process communication) 종류 2. 예제 코드 #incl..
doitnow-man.tistory.com
https://reakwon.tistory.com/80
[리눅스] 파이프(pipe) 개념과 예제
파이프(Pipe) 파이프(Pipe)란 프로세스간 통신을 할때 사용하는 커뮤니케이션의 한 방법입니다. 가장 오래된 UNIX 시스템의 IPC로 모든 유닉스 시스템이 제공합니다. 하지만 두가지 정도의 한계점이
reakwon.tistory.com
https://velog.io/@hidaehyunlee/minishell-5.-%ED%8C%8C%EC%9D%B4%ED%94%84Pipe-%EC%B2%98%EB%A6%AC
[minishell] 5. 파이프(Pipe) 처리
"한 가지 일만 아주 잘하는 프로그램들을 작성하라. 프로그램들이 다른 프로그램들과 함께 일할 수 있도록 작성하라. 프로그램들이 텍스트 스트림을 처리할 수 있도록 작성하라. 왜냐하면 그것
velog.io
https://jihooyim1.gitbooks.io/unixbasic/content/contents/09.html
9. 파이프 · UNIXBasic
jihooyim1.gitbooks.io
Pipes - Named Pipe (FIFOs)
- 리눅스에서는 Named PIPE 기법을 위해 FIFO 라는 특수 파일을 제공
- 부모-자식간이 아닌 독립적인 프로세스 간에 통신하기 위해서는 이름 있는 파이프 사용
- FIFO로 사용할 특수파일을 명령이나 함수로 먼저 생성해야함
* 리눅스 파일 종류
- 일반 파일 : 텍스트 바이너리 형태의 데이터를 저장하는 파일 - 특수 파일 : 데이터 전송, 장치 접근에 사용하는 파일(device file) - 디렉터리 : 파일 저장 위치, 공간- 특수 파일로 생성되는 FIFO는 생성 방법에 차이는 있지만 PIPE와 거의 유사
- 익명의 파이프는 따로 이름이 없는 통신 채널인데 비해서 FIFO 는 mkfifo() 함수에 의해 실제로 생성되는 특수 파일
* mknod나 mkfifo 명령으로 FIFO 파일 생성
1) mknod 명령
#include <sys/stat.h>
int mknod(const char *path, mode_t mode, dev_t dev);
- path : 특수 파일을 생성할 경로
- mode : 특수 파일의 종류와 접근 권한 지정
- dev : 블록/문자 장치 설정값
- 개념 : FIFO 파일 / 특수 파일 생성
- 형식
mknod filename p
- 성공 시 : 0 리턴
- 실패 시 : -1 리턴
※ mode : 생성할 특수파일의 종류 지정
- S_IFIFO : FIFO 특수 파일
- S_IFCHAR : 문자장치 특수 파일
- S_IFDIR : 디렉토리
- S_IFDIR : 블록장치 특수파일
- S_IFREG : 일반파일
2) mkfifo명령
#include <sys/tpyes.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
- const char *pathname : fifo파일이 생성될 경로를 지정한다. 일반 파일의 경로를 지정하는 것과 동일
- mode_t mode : 생성 될fifo파일의 접근 권한을 지정한다.
- 형식 및 예
/usr/bin/mkfifi [-m mode] path....
- path로 지정한 경로에 접근 권한을 지정해 FIFO 파일 생성
- 성공 시 : 0 반환
- 실패 시 : -1 반환
① mkfifo() 함수가 정상적으로 실행되면 인자로 넘긴 경로에 특수 파일로 FIFO 가 등록
② 다른 프로세스는 일반 파일에서 사용하는 것처럼 FIFO를 사용 가능하지만 특정 프로세스가 FIFO를 쓰기용으로 열기 전까지는 다른 읽기용 개방은 블럭(Block) 됨- 어떤 프로세스라도 생성된 경로로 파일에 접근하여 다른 파일을 사용하듯 읽거나 쓰기가능
- FIFO로 pipe와 마찬가지로 단방향 통신, 반드시 읽기전용(O_RDONLY), 쓰기 전용(O_WRONLY) 으로만 열어야 함 (읽기쓰기로는 열 수 X -> 그래도 최초 개방 시에는 읽기쓰기로 열어야 한다! )
FIFOs 사용 방법!
예시 1)
- client (writer) 코드
#include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #define MSG_SIZE 80 #define PIPENAME "./named_pipe_file" int main(void) { char msg[MSG_SIZE]; int fd; int nread, i; /* named pipe 열기, Write 전용으로 열기 */ if ((fd = open(PIPENAME, O_WRONLY)) < 0) { printf("fail to open named pipe\n"); return 0; } /* Data를 보낸다. */ for (i = 0; i < 3; i++) { snprintf(msg, sizeof(msg), "Send Message[%i]", i); if ((nread = write(fd, msg, sizeof(msg))) < 0 ) { printf("fail to call write()\n"); return 0; } } return 0; }
- server (reader) 코드
#include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #define MSG_SIZE 80 #define PIPENAME "./named_pipe_file" int main(void) { char msg[MSG_SIZE]; int fd; int nread, rc; /* 기존에 named pipe가 있으면 삭제 */ if (access(PIPENAME,F_OK) == 0) { unlink(PIPENAME); } /* named pipe 생성하기 */ if ((rc = mkfifo(PIPENAME,0666)) < 0) { printf("fail to make named pipe\n"); return 0; } /* named pipe 열기, Read Write가능 해야 한다 */ if ((fd = open(PIPENAME, O_RDWR)) < 0) { printf("fail to open named pipe\n"); return 0; } while (1) { if ((nread = read(fd, msg, sizeof(msg))) < 0 ) { printf("fail to call read()\n"); return 0; } printf("recv: %s\n", msg); } return 0; }
https://www.softprayog.in/programming/interprocess-communication-using-pipes-in-linux
Pipes in Linux - SoftPrayog
The pipe is a fundamental interprocess communication mechanism in Linux. Interprocess communication using pipes is explained with an example.
www.softprayog.in
https://doitnow-man.tistory.com/120
[IPC] named pipe 예제 코드
[IPC] name pipe 예제 코드 1. 구조 - 단방향 통신 구조이며 한쪽에서 쓰면 다른 한쪽에서 읽을 수 있는 구조로 되어있다. - 자세한 설명은 다음 포스트 참조 [프로세스간 통신] IPC(inter process communicat
doitnow-man.tistory.com
IPC - 리눅스 Named PIPE 사용법 (FIFO)
리눅스에서는 Named PIPE 기법을 위해 FIFO 라는 특수 파일을 제공합니다. 특수 파일로 생성되는 FIFO 는 생성 방법에 차이는 있지만 PIPE 와 거의 유사합니다. 익명의 파이프는 따로 이름이 없는 통신
unabated.tistory.com
https://tuxthink.blogspot.com/2012/02/inter-process-communication-using-named.html
Inter process communication using named pipes(FIFO) in linux
In the last post we saw the how to create pipes for communication between processes in linux. One of the major disadvantage of pipes is t...
tuxthink.blogspot.com
https://www.softprayog.in/programming/interprocess-communication-using-fifos-in-linux
FIFOs in Linux - SoftPrayog
Any two processes can communicate using FIFOs in Linux. Interprocess communication using FIFOs is explained using a client-server example.
www.softprayog.in
http://cs.sookmyung.ac.kr/~chang/lecture/sp/chap13.pdf
728x90'System Programming > Ubuntu Linux' 카테고리의 다른 글
Shell 만들기 참고할 것 (0) 2021.10.28 시스템 프로그래밍 실습 9주차 : System V IPC (0) 2021.10.22 시스템 프로그래밍 실습 7주차 : Signals (0) 2021.10.10 시스템 프로그래밍 실습 6주차 : Daemon (0) 2021.10.03 시스템 프로그래밍 실습 5주차 : Processes (0) 2021.09.27