|
in shell command)Kernel 내부에 Buffer를 위치시켜 해당 공간에 읽고 씀을 통해서 data를 전송한다.
Kernel에 buffer를 위치시키는 만큼 안전하지만, 느리고 많은 데이터를 전송하기 힘들다.
Window에는 CreateMailSlot이라는 함수를 사용하는데 linux는 못찾았다... message queue가 그나마 가장 유사한 가능을 포함하고 있기는 하지만, message box라고 할 수 있을지
2개 이상의 process가 특정 영역의 memory를 공유하고, 이 memory를 통해서 data를 전송한다.
kernel을 안거치기 때문에 빠르지만, 사용자가 race condition을 비롯한 각종 error에 대해서 책임을 져야한다.(사실 이건 굉장히 어려운 주제이기도 하다... OS 13-16 참조)
linux에서는 주로 mmap을 이용하여 구현한다.
Producer는 Buffer에 data를 채우고, consumer는 buffer에서 data를 꺼내 처리한다.
가운데 위치하는 저 buffer의 크기에 따라서 unbounded-buffer, bounded-buffer로 나뉜다.
Producer와 Consumer사이의 Concurrency 문제는 정말 많은 이야기를 할 수 있는 주제이지만... OS에서 다루도록 하겠다.
typedef struct { … } item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int inc(int v) { return (v+1) % BUFFER_SIZE; }
int empty() { return in==out; }
int full() { return inc(in) == out; }
void enqueue(item item) {
while (full()); buffer[in]=item; in=inc(in);
}
item dequeue() {
while (empty()); item=buffer[out]; out=inc(out); return item;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pipe_fd[2]; // create ordinary pipe
pid_t pid;
char message[] = "Hello, Pipe!";
if (pipe(pipe_fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // Child process
close(pipe_fd[1]); // Close write end in the child
char buffer[100];
read(pipe_fd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer);
close(pipe_fd[0]);
} else { // Parent process
close(pipe_fd[0]); // Close read end in the parent
write(pipe_fd[1], message, sizeof(message));
close(pipe_fd[1]);
}
return 0;
}
pipe()
를 통해서 kernel 상에 buffer를 만든다.pipe(int fd[]) -> fd[0]: read, fd[1]: write)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_FILE "/tmp/myfifo"
int main() {
mkfifo(FIFO_FILE, 0666); // create name pipe
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // Child process
int fd = open(FIFO_FILE, O_RDONLY); // link pipe -> output entry
char buffer[100];
read(fd, buffer, sizeof(buffer)); // receive message to pipe
printf("Child received: %s\n", buffer);
close(fd);
} else { // Parent process
int fd = open(FIFO_FILE, O_WRONLY); // link pipe -> input entry
char message[] = "Hello, FIFO!";
write(fd, message, sizeof(message)); // send message to pipe
close(fd);
unlink(FIFO_FILE); // Remove the FIFO file
}
return 0;
}
임시 fifo 파일을 하나 만들고, 이 파일을 공유하는 방식으로 이루어지는 IPC
오히려 pipe보다 직관적인 이해는 더 쉬울 듯... 파일 이름만 알면 진짜 file descriptor 처럼 사용할 수 있으니까...
#define READ 0
#define WRITE 1
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t pid;
if (pipe(pipefd) < 0) { // pipe 생성
printf("Cannot create pipe.\n");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid > 0) parent(pipefd) // parent 동작
else if (pid == 0) child(pipefd); // child 동작
else printf("Cannot fork.\n");
// parent/child do not return
return EXIT_FAILURE;
}
void child(int pfd[2])
{
// close unused read end and redirect
// standard out to write end of pipe
close(pfd[READ]); // 읽기 쪽 pipe는 닫는다
dup2(pfd[WRITE], STDOUT_FILENO); // STDOUT을 pipe의 쓰기 fd와 연결
// 사실 이후에 write pipe는 닫아버려도 무방하다.
// arguments for execv call
char *argv[] = {
"/bin/ls",
"-l",
"/etc",
NULL,
};
// exec does not return on success
execv(argv[0], argv); // 이제 여기서 실행한 결과가 stdout으로 출력되고, 그게 pipe를 타고 이동해서 parent에 전해지겠지
// exec failed
exit(EXIT_FAILURE);
}
void parent(int pfd[2])
{
// close unused write end
close(pfd[WRITE]); // 마찬가지로 쓰기 쪽 입구는 닫아버린다.
// read from pipe
int even = 1;
char c;
while (read(pfd[READ], &c, 1) > 0) {
// every even character: upper case
// every odd character: lower case
if (even) c = toupper(c);
else c = tolower(c);
even = !even;
write(STDOUT_FILENO, &c, sizeof(c)); // pipe로 전해져 오는 data를 읽어들인다!
}
exit(EXIT_SUCCESS);
}