터미널 창을 두 개 띄우고,
두 창에서 각각 프로그램을 실행 시킨다.
두 프로그램이 실행되어 프로세스 A 와 프로세스 B 가 구동되었다고 할 떄, 두 프로세스가 다음의 동작이 될 수 있도록 프로그래밍 해보자.프로그램이 실행되면, 각 프로세스들은 다음과 같은 상태가 된다.
[프로세스 A 화면] [프로세스 B 화면]
입력대기: 수신대기...여기서 프로세스 B 는 입력이 불가능하고, 수신 대기만 하고 있다.
만약 프로세스 A 가 문자열을 입력하고 엔터키를 치면, 프로세스 B 에서 해당 문자열이 출력된다.다음번엔 프로세스 B 가 입력대기 상태, 프로세스 A 가 수신대기 상태가 된다.
[프로세스 A 화면] [프로세스 B 화면]
입력대기: 수신대기...
Hi A) Hi수신대기... 입력대기:
정리하면,
프로세스 A 에서 키보드 입력으로 "Hi" 를 타이핑하고 엔터키를 치면 프로세스 B 의 화면에 "A) Hi" 가 나온다.프로세스 B 에서 키보드 입력으로 "Bye" 를 타이핑하고 엔터키를 치면 프로세스 A 의 화면에 "B) Bye" 가 나온다.
이렇게 두 프로세스는 한 번씩 돌아가며 채팅이 가능하다.
채팅 중 누구라도 "exit" 를 입력하면 A, B 프로세스 모두 종료 된다.
#include <fcntl.h> // 파일 제어 함수를 위한 헤더 파일
#include <stdio.h> // 표준 입출력 함수를 위한 헤더 파일
#include <stdlib.h> // 일반 유틸리티 함수를 위한 헤더 파일
#include <string.h> // 문자열 처리 함수를 위한 헤더 파일
#include <sys/stat.h> // 파일 상태 정보를 위한 헤더 파일
#include <unistd.h> // POSIX 운영 체제 API를 위한 헤더 파일
#define FIFO_A_TO_B "fifo_a_to_b" // A에서 B로 데이터를 보낼 FIFO 파일의 이름
#define FIFO_B_TO_A "fifo_b_to_a" // B에서 A로 데이터를 받을 FIFO 파일의 이름
int main() {
int fd; // 파일 디스크립터를 저장할 변수
char readbuf[80]; // 데이터 수신을 위한 버퍼
char input[80]; // 데이터 송신을 위한 입력 버퍼
// FIFO 파일 생성
mkfifo(FIFO_A_TO_B, S_IFIFO|0666); // A에서 B로의 통신을 위한 FIFO 파일 생성
mkfifo(FIFO_B_TO_A, S_IFIFO|0666); // B에서 A로의 통신을 위한 FIFO 파일 생성
while (1) {
// 입력 받기
printf("입력대기: ");
fgets(input, sizeof(input), stdin); // 사용자 입력 받기
input[strcspn(input, "\n")] = 0; // 입력된 문자열에서 개행 문자 제거
fd = open(FIFO_A_TO_B, O_WRONLY); // 쓰기 전용으로 FIFO 파일 열기
write(fd, input, strlen(input)+1); // 입력받은 데이터를 FIFO를 통해 전송
close(fd); // 파일 디스크립터 닫기
if (strcmp(input, "exit") == 0) // 입력받은 데이터가 exit이면 종료
break;
// 수신 대기
printf("수신대기...\n");
fd = open(FIFO_B_TO_A, O_RDONLY); // 읽기 전용으로 FIFO 파일 열기
read(fd, readbuf, sizeof(readbuf)); // FIFO를 통해 데이터 수신
printf("A) %s\n\n", readbuf); // 수신된 데이터 출력
close(fd); // 파일 디스크립터 닫기
if (strcmp(readbuf, "exit") == 0) // 수신된 데이터가 exit이면 종료
break;
}
unlink(FIFO_A_TO_B); // FIFO 파일 삭제
unlink(FIFO_B_TO_A); // FIFO 파일 삭제
return 0; // 프로그램 종료
}
#include <fcntl.h> // 파일 제어 함수를 위한 헤더 파일
#include <stdio.h> // 표준 입출력 함수를 위한 헤더 파일
#include <stdlib.h> // 일반 유틸리티 함수를 위한 헤더 파일
#include <string.h> // 문자열 처리 함수를 위한 헤더 파일
#include <sys/stat.h> // 파일 상태 정보를 위한 헤더 파일
#include <unistd.h> // POSIX 운영 체제 API를 위한 헤더 파일
#define FIFO_A_TO_B "fifo_a_to_b" // A에서 B로의 통신을 위한 FIFO 파일 이름
#define FIFO_B_TO_A "fifo_b_to_a" // B에서 A로의 통신을 위한 FIFO 파일 이름
int main() {
int fd; // 파일 디스크립터를 저장할 변수
char readbuf[80]; // 데이터 수신을 위한 버퍼
char input[80]; // 데이터 송신을 위한 입력 버퍼
// FIFO 파일이 이미 생성되어 있다고 가정
while (1) {
// 수신 대기
printf("수신대기...\n");
fd = open(FIFO_A_TO_B, O_RDONLY); // 읽기 전용으로 FIFO 파일 열기
read(fd, readbuf, sizeof(readbuf)); // FIFO를 통해 데이터 수신
printf("A) %s\n\n", readbuf); // 수신된 데이터 출력
close(fd); // 파일 디스크립터 닫기
if (strcmp(readbuf, "exit") == 0) // 수신된 데이터가 exit이면 반복 종료
break;
// 입력 받기
printf("입력대기: ");
fgets(input, sizeof(input), stdin); // 사용자 입력 받기
input[strcspn(input, "\n")] = 0; // 입력된 문자열에서 개행 문자 제거
fd = open(FIFO_B_TO_A, O_WRONLY); // 쓰기 전용으로 FIFO 파일 열기
write(fd, input, strlen(input)+1); // 입력받은 데이터를 FIFO를 통해 전송
close(fd); // 파일 디스크립터 닫기
if (strcmp(input, "exit") == 0) // 입력받은 데이터가 exit이면 반복 종료
break;
}
return 0; // 프로그램 종료
}
△ Process A 실행 화면 △ Process B 실행 화면
Named PIPE 방법 기반으로 구현하였으며 두 개의 Window PowerShell을 통해 SSH 연결하여 두 프로세스를 구비했다. 먼저 Process A의 코드의 진행 과정을 먼저 보자면 첫 번째로 FIFO 파일 생성을 생성한다. mkfifo() 함수를 사용해 fifo_a_to_b와 fifo_b_to_a 두 개의 FIFO 파일을 생성한다. 이 파일들은 서로 다른 프로세스 간의 데이터 전송 통로 역할을 합니다. 두 번째는 데이터 전송을 한다. 사용자로부터 입력을 받은 후 fifo_a_to_b FIFO 파일을 통해 Process B로 데이터를 전송한다. open(), write(), 그리고 close() 함수들을 이용하여 데이터를 쓰고 파일 디스크립터를 관리한다. 세 번째는 데이터 수신을 한다. fifo_b_to_a FIFO 파일을 통해 Process B로부터 데이터를 수신한다. 여기에는 open(), close()와 더불어 read() 함수를 사용하는데 이 점이 데이터 전송과의 차별을 가진다. 종료 조건으로는 exit 문자열을 전송하거나 수신받으면, FIFO 파일을 unlink() 함수로 삭제하고 프로그램을 종료한다.
Process B는 Process A와 달리 FIFO 파일이 존재한다고 가정을 한다. 이는 Process A에게 첫 전송에 대한 우선권이 있기 때문이다. 대신 이미 생성된 FIFO 파일을 열고 사용한다. 이 프로세스는 FIFO 파일 생성을 담당하지 않는다. 다음은 데이터 전송을 먼저하는 Process A와 달리 데이터를 먼저 수신한다. fifo_a_to_b FIFO 파일을 통해 Process A로부터 데이터를 먼저 수신한다. 다음에서야 데이터를 전송한다. 사용자로부터 입력을 받은 후 fifo_b_to_a FIFO 파일을 통해 Process A로 데이터를 전송한다. Process A와 같이 exit 문자열을 전송하거나 수신받으면 프로그램을 종료한다.
이 두 프로그램은 각각 다른 터미널이나 콘솔에서 실행한다. 본 목표는 SSH를 통해 Linux를 연결하였으며, 두 개의 Window PowerShell을 통해 진행했다. 두 프로그램은 FIFO 파일을 통해 서로 동기화되며, 한 프로그램이 데이터를 보내면 다른 프로그램이 이를 읽는다. 그렇기에 두 프로그램이 동시에 실행되고 있을 때 정상적으로 기능한다. 각 프로세스는 데이터를 전송하거나 수신할 준비가 되면 다음 단계로 넘어간다. 그리고 FIFO는 내부적으로 데이터가 쓰여잇는 순서대로 데이터를 전달한다.
본 목표를 진행하며, 아쉽다는 생각밖에 들지 않았던 것 같다. 두 프로세스간 IPC 기반 실시간 채팅 프로그램을 꼭 만들고 싶었으나 어려움을 겪었다. Message Queue를 이용하여 동시에 데이터를 접근하게 하고 이를 통해 해야겠다는 계획은 있었으나, 구현 단계에서 막혀 못하게 되었다. 두 가지 문제에 막히게 되었는데, 첫째는 exit를 작성하면 두 프로세스가 아닌 한 프로세스만 종료가 되는 문제였다. 둘째는 다섯번 반복해서 메시지를 입력하여야 다른 프로세스로 전송되는 문제였다. 그래서 아쉬움을 뒤로 하고 방향을 틀어 Named PIPE 개념을 이용하여 턴 방식의 채팅 프로그램이라도 구현했다. 이번 과제를 통해 많이 성장함을 느꼈지만 약간은 아쉽고 부족하다고 생각한다. 추가적인 시간을 할당하여 결국에는 해결하지 못한 저 두 문제점을 어떻게든 해결하여 꼭 실시간 채팅 프로그램을 만들어보아야겠다는 다짐을 하게 되었다. 뿐만 아니라, 이번 목표는 내가 IPC 그 중에서도, Named PIPE 개념을 이해하는데 큰 도움을 주었다. 직접 해봄으로써, 어떻게 동작하는지 더욱 직관적으로 확인할 수 있게 되었고, 이는 나에게 오랜 기억 속에 남는 개념 중 하나로 자리 잡았다고 생각한다. 앞으로의 운영체제 공부 중에서도 새롭게 나오는 개념이 있다면 직접 경험해봄으로써 견고하게 쌓아가야겠다는 생각이 들었다.