본문 바로가기
대외활동/시스템프로그래밍

프로세스 간 통신(파이프, ipc)

by hoshi03 2024. 8. 29.

프로세스 간에는 공유되는 부분이 없기에 별도의 방법이 필요하다

 

• 파이프

 

부모, 자식 프로세스 간의 통신을 위해서 사용한다

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    int pfd[2];
    pipe(pfd); // pfd[0] - read, pfd[1] - write 하는 파이프
    printf("%d, %d\n", pfd[0], pfd[1]);


    char buf[] = "hello parent";
    char buf2[1024];
    int n;
    switch (fork())
    {
    case -1:
        perror("fork error");
        exit(1);
        break;
    case 0:
        //자식 프로세스에서 쓰기 작업만 하기 위해서 read 디스크립터 닫기
        close(pfd[0]);
        // 1번 읽기 파일 디스크립터가 pfd[1]을 가리키게 하기
        dup2(pfd[1],1);
        close(pfd[1]);

        // write(1, buf, strlen(buf));
        execlp("ps", "ps", NULL);
        perror("execlp");
        exit(1);
        break;
    default:
        close(pfd[1]);
        dup2(pfd[0],0);
        execlp("wc", "wc", "-l", NULL);
        // n = read(0, buf2, sizeof(buf2));
        // buf2[n] == '\0';
        // printf("%s\n", buf2);
        wait(NULL);
        break;
    }
}

 

-- pipe() 메서드를  이용해서 배쉬 쉘에서 파이프 연산이 가능하게 하는 코드

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>

void redirect_out(char **arglist)
{
    int fd, i;
    for (int i = 0; arglist[i] != NULL; i++)
    {
        // 리다이렉트 기호가 있음
        if (strcmp(arglist[i], ">") == 0)
        {
            fd = open(arglist[i + 1], O_WRONLY | O_TRUNC | O_CREAT, 644);
            if (fd == -1)
            {
                perror("open");
                return;
            }
            // dup 쓰는방법
            // close(1);
            // dup(fd);
            dup2(fd, 1);
            close(fd);
            arglist[i] = NULL;
        }
    }
}

int pipe_check(char **arglist)
{
    for (int i = 0; arglist[i] != NULL; i++)
    {
        if (strcmp(arglist[i], "|") == 0)
        {
            return i;
        }
    }
    return 0;
}

int is_background(char **arglist)
{
    int i = 0;
    for (i = 0; arglist[i] != NULL; i++)
        ;
    // 마지막 인자가 &인 백그라운드 요청이면
    if (strcmp(arglist[i - 1], "&") == 0)
    {
        // execvp 메서드에 &가 전달되면 안됨
        arglist[i - 1] = NULL;
        return 1;
    }

    else
        return 0;
}

void run_command(char **arglist)
{
    pid_t cpid;
    int back_flag = 0;
    int pipe_pos = 0;

    if (is_background(arglist))
    {
        back_flag = 1;
    }

    if ((pipe_pos = pipe_check(arglist)) > 0)
    {
        int pfd[2];
        pipe(pfd);
        arglist[pipe_pos] = NULL;

        switch (cpid = fork())
        {
        case -1:
            break;
        case 0:
            signal(SIGINT, SIG_DFL);
            signal(SIGQUIT, SIG_DFL);

            //파이프
            close(pfd[0]);
            dup2(pfd[1],1);
            close(pfd[1]);
            //ls -l | grep bash
            redirect_out(arglist);
            execvp(arglist[0], arglist);
            perror("execvp");
            exit(1);
            break;

        default:
            // if (back_flag == 0) waitpid(cpid, NULL, 0);
            close(pfd[1]);
            dup2(pfd[0],0);
            close(pfd[0]);
            
            execvp(arglist[pipe_pos+1],&arglist[pipe_pos+1]);
            perror("execvp");
            exit(1);
            break;
        }
    }

    else
    {
        switch (cpid = fork())
        {
        case -1:
            break;

        case 0:
            signal(SIGINT, SIG_DFL);
            signal(SIGQUIT, SIG_DFL);
            


            redirect_out(arglist);
            execvp(arglist[0], arglist);
            perror("execvp");
            exit(1);
            break;

        default:
            if (back_flag == 0) waitpid(cpid, NULL, 0);
            break;
        }
    }
}

 

 

•  메세지 큐 

멀티 프로세스에서 양방향 통신

메세지를 타입으로 구분해서 선택적으로 수신한다

 

-- 송신자가 수신자에게 메세지 하나만 보내는 예제

 

- 수신자

#include <stdio.h>
#include <unistd.h>
#include <sys/msg.h>

#define MSQKEY 51234

struct msgbuf {
    long mtype; /* 메시지의 타입 : 0 이상의 정숫값 */
    char mtext[BUFSIZ]; /* 메시지의 내용 : 1바이트 이상의 문자열 */
};

int main(int argc, char **argv)
{
    key_t key;
    int n, msqid;
    struct msgbuf mb;
    key = MSQKEY;

    /* 메시지 큐의 채널을 생성한다. */
    if((msqid = msgget(key, IPC_CREAT | 0666)) < 0) {
        perror("msgget()");
        return -1;
    }

    /* 메시지 큐에서 데이터를 가져온다. */
    while((n = msgrcv(msqid, &mb, sizeof(mb), 0, 0)) > 0) {
        switch (mb.mtype) {
            /* 메시지 타입(mtype)이 1이면 화면에 가져온 데이터를 출력한다. */
            case 1:
		mb.mtext[n]='\0';
              	write(1, mb.mtext, n);
                break;
            /* 메시지 타입(mtype)이 2이면 메시지 큐의 채널을 삭제한다. */
            case 2:
                if(msgctl(msqid, IPC_RMID, (struct msqid_ds *) 0) == -1) {
                    perror("msgctl()");
                    return -1;
                }
                break;
        }
    }
                msgctl(msqid, IPC_RMID, (struct msqid_ds *) 0) ;

    return 0;
}

 

- 송신자

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>

#define MSQKEY 51234

struct msgbuf {
    long mtype;
    char mtext[BUFSIZ];
};

int main(int argc, char **argv)
{
    key_t key;
    int rc, msqid;
    char* msg_text = "hello world\n";

    struct msgbuf *mb;
    mb = (struct msgbuf*)malloc(sizeof(struct msgbuf) + strlen(msg_text));

    key = MSQKEY;
    if((msqid = msgget(key, IPC_CREAT|0666)) < 0) { 		/* 메시지 큐의 채널을 가져온다. */
        perror("msgget()");
        return -1;
    }

    /* mtype을 1로 설정하고 hello world라는 문자열을 보낸다. */
    mb->mtype = 1;
    strcpy(mb->mtext, msg_text);
    rc = msgsnd(msqid, mb, sizeof(msg_text)+1, 0); 	/* 메시지 큐로 데이터를 보낸다. */
    //rc = msgsnd(msqid, mb, sizeof(struct msgbuf), 0); 	/* 메시지 큐로 데이터를 보낸다. */
    if(rc == -1) {
        perror("msgsnd()");
        return -1;
    }

    /* mtype을 2로 설정하고 보낸다. */
    mb->mtype = 2;
    memset(mb->mtext, 0, strlen(mb->mtext));
    if(msgsnd(msqid, mb, strlen(mb->mtext), 0) < 0) {
        perror("msgsnd()");
        return -1;
    }
}

 

 

• 공유 메모리

프로세스들이 메모리를 공유하면서 사용하는 방식

 

송신

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


int main()
{
	int shmid, i,j ;
	char *shmaddr;

	if((shmid=shmget(0x123400, 30, 0660|IPC_CREAT|IPC_EXCL))==-1) {
		if((shmid=shmget(0x123400, 30, 0660))==-1){
			perror("shmget");
			exit(1);
		}
	}
 	if((shmaddr=shmat(shmid, (char *)0, 0))== (char *)-1) {
		perror("shmat");
		exit(1);
	}
 	for(i=0; i<20; i++) {
 		sprintf(shmaddr, "shared memory test %d", i+1);
 		printf("send : %s\n", shmaddr);
 		for(j=0; j<500000000; j++);
 	}
 	sprintf(shmaddr, "end");

	if(shmdt(shmaddr)==-1 ) {
		perror("shmdt");
		exit(1);
	}

	return 0;
}

수신

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
	int shmid, i, j;
	char *shmaddr;

	if((shmid=shmget(0x123400, 30, 0660|IPC_CREAT|IPC_EXCL))==-1) {
		if((shmid=shmget(0x123400, 30, 0660))==-1){
			perror("shmget");
			exit(1);
		}
	}
 	if((shmaddr= shmat(shmid, (char *)0, 0))== (char *)-1) {
		perror("shmat");
		exit(1);
	}
 	while(1) {
 		if(!strcmp(shmaddr,"end"))
 			break;
 		if(!strcmp(shmaddr,""))
 			continue;
 		printf("recv : %s\n", shmaddr);
		for(j=0; j<499999999; j++);
 	}

	if(shmdt(shmaddr) == -1 ) {
		perror("shmdt");
		exit(1);
	}
	if(shmctl(shmid, IPC_RMID, (struct shmid_ds *)0) == -1 ) {
		perror("shmctl");
		exit(1);
	}
	return 0;
}

 

• 세마포어

 

P, V 연산

 

p연산은 임계구역 전에 세마포 변수를 감소키기고

 

v 연산은 임계구역에서 작업이 이루어진 후 세마포 변수를 증가시킨다