OS수업을 듣던중 교수님께서 과제를 하나 내셨다
과제는 "채팅 프로그램" 멀티 프로세스를 이용해서 간단한 채팅 프로그램을 구현하는 것이었다.
이렇게 힌트를 주셔서 3일 정도 열심히 구글링하고 생각하면서 풀었다, 처음에는 프로세스가 뭔지도 잘 모르는 상태여서 이해하는데 오랜 시간이 걸렸고 구현한 다음 또 코드를 이해하는데 시간을 보냈다. ,, 잡다한 얘기는 여기까지 하고 풀이에 들어가보자!
하나 하나 설명하면서 이해하면 좋겠지만 너무 오래 걸릴수도 있고 글이 엄청 길어질 거 같기에 바로 코드부터 보겠다.
나는 보통 main부터 출발해서 그다음 구현한 함수들을 보는 편이기에 main부터 출발하겠다.
C로 프로세스를 처음 짜본다면 밑 링크를 참고하면 좋을 것 같다.
char *chatTtoJ = "./chatTtoJ";
char *chatJtoT = "./chatJtoT";
mkfifo(chatTtoJ, 0666);
mkfifo(chatJtoT, 0666);
fdw = open(chatTtoJ, O_WRONLY);
fdr = open(chatJtoT, O_RDONLY);
signal(SIGINT, died); //ctrl+c가 입력된 상황!
signal(SIGCHLD, kills); //자식 프로세스가 종료된 상황!
char *chatTtoJ ="./chatTtoJ" 그냥 변수명이다.
우리의 목표는 프로세스간 통신 연결이므로 mkfifo를 통해 FIFO를 생성하는 파일 이름을 알고 있다면 누구나 사용할 수 있습니다.
0666은 권한 설정이다. -> 읽기 쓰기 권한을 주었습니다.
어떤 형식으로 열어줄 것인지를 설정해준다.
signal(SIGINT, died); -> SIGINT는 Ctrl+C가 입력되면 died란 함수를 호출하겠다~라는 것이다, 밑에도 비슷한 맥락이다.
이제 while문으로 들어가보자 그전에 프로세스를 이해하고 왔다면 못 했다면 위에 올린 링크를 참고하자.
fork()를 통해 우리는 자식프로세스를 만들었다.
pid = fork();
while(1){
if(pid == 0){//자식 프로세스
Parent_pid = getppid(); //부모 프로세스 번호 구하기
Child_pid = getpid(); //자식 프로세스 번호 구하기
fgets(Jerry, 80, stdin); //Jerry를 입력문으로 받기
write(fdw, Jerry, strlen(Jerry)+1); //내가 Tom한테 글을 쓴다.
}
만약 자식프로세스면 부모, 자식프로세스 고유번호를 먼저 구해준다.
getppid(), getpid()가 그것이다.
그리고 Jerry배열에 쓴 문장을 저장하고 write를 통해 Tom에게 넘긴다. -> write는 C에 존재하는 함수이다.
write함수에 인자는 (fdw ->chatJtoT, write형식으로, 내용을 담은 버퍼, 쓰기할 바이트 개수)순이다.
그러면 Tom 프로세스의 read함수에서 받는다.
else if(pid > 0){ //부모 프로세스이다
int i = 0, n = 0;
while((n=read(fdr, &Jerry[i], 1))>0){ //Jerry가 나에게 쓴 글을 읽어 user배열에 넣는다.
if(Jerry[i] == '\0')
break;
if(i == 78){
Jerry[++i] = '\0';
break;
}
i++;
}
위는 Tom.c에 부모 프로세스 실행 부분이다.
만약 부모프로세스라면 read(fdr 형식으로, Jerry[i]에 , 하나씩)>0
이 코드의 뜻은 문장이 끝날때 까지 계속 받아서 Jerry에 저장한다는 뜻이다.
if(n<=0){
kill(Child_pid, 9); //자식 프로세스를 죽인다!.
close(fdr); //read모드 닫기
close(fdw); //write모드 닫기
return 0;
}
printf("Jerry : %s\n", Jerry);
sleep(1);
만약 n<=0이면
통신이 끊켰다는 이유기 때문에 좀비프로세스 방지를 위해 바로 자식 프로세스를 죽여준다
-> 통신이 끊킨걸 아는 이유 n<=0이면 엔터하나도쳐도 공백이므로 값이 받아진다. 통신이 끊키면 0미만의 수가 전달된다.
추가로 통신이 끊켰다는 것은 Jerry에서 문제가 발생했다는 것이므로 자식프로세스를 바로 죽여준다.
void died(){
printf("자식 프로세스 종료");
kill(Child_pid, 9);
}
void kills(){
int status;
waitpid(-1, &status, WNOHANG);
exit(1);
}
자식 프로세스가 종료된 이후에 부모 프로세스가 자식 프로세스의 상태를 알고 싶을 수 있기 때문에 커널은 자식 프로세스가 종료되더라도 최소한의 정보(프로세스 ID, 프로세스 종료 상태 등)를 가지고 있게 된다. ->좀비프로세스
그래서 kills()함수로 만약 자식프로세스가 죽으면 자식프로세스에 자원을 회수할때까지 기다리게 만든다.
회수 하지 않는다면 자식 프로세스가 죽어있는 상태긴 하지만 좀비프로세스로 남아있는 상태이기 때문에 자원을 모두 회수해줘 좀비프로세스를 없애주자! 그리고 exit()을 통해 부모프로세스도 함께 종료해주면! 정상적으로 프로그램 종료가 되도록 설정해줌
주 고민 내용은 좀비프로세스 처리 과정이였다.
잘 이해가 안 간다면 좀비프로세스 관련 글을 읽어보고 오면 이해하 될 거라고 생각한다.
https://codetravel.tistory.com/31
이런 식으로 Tom ,Jerry 프로세스를 구성하면 된다.
Jerry.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
pid_t pid;
int Parent_pid, Child_pid;
void died(){
printf("자식프로세스 종료");
kill(Child_pid,9);
}
void kills(){
int status;
waitpid(-1, &status, WNOHANG);
exit(0);
}
int main(){
char Tom[80], Jerry[80];
int fdw, fdr;
char *chatTtoJ = "./chatTtoJ";
char *chatJtoT = "./chatJtoT";
mkfifo(chatTtoJ, 0666);
mkfifo(chatJtoT, 0666);
fdr = open(chatTtoJ, O_RDONLY);
fdw = open(chatJtoT, O_WRONLY);
signal(SIGINT, died);
signal(SIGCHLD, kills); //자식 프로세스가 죽으면 kills메소드로 이동하여 시그널을 처리한다.
pid = fork();
while(1){
if(pid == 0){//자식 프로세스
Parent_pid = getppid(); //부모 프로세스 번호 구하기
Child_pid = getpid(); //자식 프로세스 번호 구하기
fgets(Jerry, 80, stdin); //Jerry를 입력문으로 받기
write(fdw, Jerry, strlen(Jerry)+1); //내가 Tom한테 글을 쓴다.
}
else if(pid > 0) { //부모 프로세스
int i = 0, n = 0;
while ((n=read(fdr, &Tom[i], 1)) > 0) { //Tom이 나에게 쓴 글을 읽어 user배열에 저장한다.
//Tom이 쓴 문장 끝을 판변하는 로직
if (Tom[i] == '\0') break;
if (i == 78) {
Tom[++i] = '\0';
break;
}
i++;
}
if (n <= 0) { //자식 프로세스
kill(Child_pid, 9);
close(fdr);
close(fdw);
return 0;
}
printf("Tom : %s\n", Tom);
sleep(1);
}
else{
printf("fork 오류");
return 0;
}
}
}
Tom.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
pid_t pid;
int Parent_pid, Child_pid;
int end=0;
void died(){
printf("자식 프로세스 종료");
kill(Child_pid, 9);
}
void kills(){
int status;
waitpid(-1, &status, WNOHANG);
exit(1);
}
int main(){
char Jerry[80], Tom[80];
int fdw, fdr;
char *chatTtoJ = "./chatTtoJ";
char *chatJtoT = "./chatJtoT";
mkfifo(chatTtoJ, 0666);
mkfifo(chatJtoT, 0666);
fdw = open(chatTtoJ, O_WRONLY);
fdr = open(chatJtoT, O_RDONLY);
signal(SIGINT, died); //ctrl+c가 입력된 상황!
signal(SIGCHLD, kills); //자식 프로세스가 종료된 상황!
pid = fork();
while(1){
if(pid == 0){ //pid=0이면 자식 프로세스이다
Parent_pid = getppid(); //부모 번호 구하기
Child_pid = getpid(); //자식 번호 구하기
fgets(Tom, 80, stdin); //user값을 받는다
write(fdw, Tom, strlen(Tom)+1);
//내가 Jerry에게 글을 쓴다.
}
else if(pid > 0){ //부모 프로세스이다
int i = 0, n = 0;
while((n=read(fdr, &Jerry[i], 1))>0){ //Jerry가 나에게 쓴 글을 읽어 user배열에 넣는다.
if(Jerry[i] == '\0')
break;
if(i == 78){
Jerry[++i] = '\0';
break;
}
i++;
}
if(n<=0){
kill(Child_pid, 9); //자식 프로세스를 죽인다!.
close(fdr); //read모드 닫기
close(fdw); //write모드 닫기
return 0;
}
printf("Jerry : %s\n", Jerry);
sleep(1);
}
else{
printf("fork 오류");
return 0;
}
}
}
'CS > 운영체제' 카테고리의 다른 글
[운영체제] 멀티프로세스 vs 멀티스레드 (0) | 2023.04.07 |
---|