[태그:] c

  • semaphore p223 ~ p238

    name semaphore는 /dev/shm에 저장된다. 이름을 엄한 /tmp/xxx 이런 식으로 만들면 세마포어를 만들 수 없다. segment error로 죽는다. 책에서는 /tmp/로 넣어도 잘 동작해서 확인하는데 오래 걸렸다.

    디버그 하다 중간에 멈추면 /dev/shm에 sem*로 파일을 지울 수 없어 다음 실행에 세마포어를 받을 수 없다. 처음 시작 전 자기 이름으로 된 세마포어 있으면 지워야 한다.

    thread를 돌리면 미친 race condition이 피곤하게 한다. 참 헷갈리는데, 자주보면 익숙해 지겠지. thread를 돌리면 순서는 이제 별 의미 없는 듯 하다. sem_post하는 즉시 자기 thread를 실행하는게 아니라 다른 thread로 가서 프로그램을 실행한다. wait로 thread 이후 행을 잘 막는 게 핵심인 듯 하다.

    /* include main */
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <semaphore.h>
    #include <sys/stat.h>
    #define	NBUFF	 10
    #define	SEM_MUTEX	"mutex"	 	/* these are args to px_ipc_name() */
    #define	SEM_NEMPTY	"empty"
    #define	SEM_NSTORED	"nstored"
    
    int		nitems;					/* read-only by producer and consumer */
    struct {	/* data shared by producer and consumer */
      int	buff[NBUFF];
      sem_t	*mutex, *nempty, *nstored;
    } shared;
    
    void	*produce(void *), *consume(void *);
    
    int
    main(int argc, char **argv)
    {
    	pthread_t	tid_produce, tid_consume;
    	int errno;
    	
    
    	if (argc != 2)
    		perror("usage: prodcons1 <#items>");
    	nitems = atoi(argv[1]);
    		/* 4create three semaphores */
    	shared.mutex = sem_open(SEM_MUTEX, O_CREAT | O_EXCL,
    							0660, 1);
    	shared.nempty = sem_open(SEM_NEMPTY, O_CREAT | O_EXCL,
    							 0660, NBUFF);
    	shared.nstored = sem_open(SEM_NSTORED, O_CREAT | O_EXCL,
    							  0660, 0);
    
    		/* 4create one producer thread and one consumer thread */
    	//pthread_setconcurrency(2);
    	//pthread_setconcurrency();
    	pthread_create(&tid_produce, NULL, produce, NULL);
    	pthread_create(&tid_consume, NULL, consume, NULL);
    
    		/* 4wait for the two threads */
    	pthread_join(tid_produce, NULL);
    	pthread_join(tid_consume, NULL);
    
    		/* 4remove the semaphores */
    	sem_unlink(SEM_MUTEX);
    	sem_unlink(SEM_NEMPTY);
    	sem_unlink(SEM_NSTORED);
    	exit(0);
    }
    /* end main */
    
    /* include prodcons */
    void *
    produce(void *arg)
    {
    	int		i;
    	int tmpnempty, tmpmutex, tmpnstored;
    
    	for (i = 0; i < nitems; i++) {
    
    
    		//value check
    		//sem_getvalue(shared.nempty, &tmpnempty);
    		//sem_getvalue(shared.nstored, &tmpnstored);
    		//sem_getvalue(shared.mutex, &tmpmutex);
    
    		sem_wait(shared.nempty);	/* wait for at least 1 empty slot */
    		sem_wait(shared.mutex);
    
    		//value check
    		//sem_getvalue(shared.nempty, &tmpnempty);
    		//sem_getvalue(shared.nstored, &tmpnstored);
    		//sem_getvalue(shared.mutex, &tmpmutex);
    
    
    
    		shared.buff[i % NBUFF] = i;	/* store i into circular buffer */
    		sem_post(shared.mutex);
    		sem_post(shared.nstored);	/* 1 more stored item */
    		//value check
    		//sem_getvalue(shared.nempty, &tmpnempty);
    		//sem_getvalue(shared.nstored, &tmpnstored);
    		//sem_getvalue(shared.mutex, &tmpmutex);
    
    
    	}
    	return(NULL);
    }
    
    void *
    consume(void *arg)
    {
    	int		i;
    	int tmpnempty, tmpmutex, tmpnstored;
    
    	for (i = 0; i < nitems; i++) {
    		//value check
    		//sem_getvalue(shared.nempty, &tmpnempty);
    		//sem_getvalue(shared.mutex, &tmpmutex);
    		//sem_getvalue(shared.nstored, &tmpnstored);
    
    
    		sem_wait(shared.nstored);		/* wait for at least 1 stored item */
    		sem_wait(shared.mutex);
    		//value check
    		//sem_getvalue(shared.nempty, &tmpnempty);
    		//sem_getvalue(shared.mutex, &tmpmutex);
    		//sem_getvalue(shared.nstored, &tmpnstored);
    
    
    		if (shared.buff[i % NBUFF] = i)
    			printf("buff[%d] = %d\n", i, shared.buff[i % NBUFF]);
    		sem_post(shared.mutex);
    		sem_post(shared.nempty);		/* 1 more empty slot */
    		//value check
    		//sem_getvalue(shared.nempty, &tmpnempty);
    		//sem_getvalue(shared.mutex, &tmpmutex);
    		//sem_getvalue(shared.nstored, &tmpnstored);
    
    
    	}
    	return(NULL);
    }
    /* end prodcons */
    
    pi@raspberrypi:~/Project/cCode/IPC $ gdb --args ./a.out  3
    GNU gdb (Raspbian 8.2.1-2) 8.2.1
    Copyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "arm-linux-gnueabihf".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from ./a.out...done.
    ...
    
    (gdb) info b
    Num     Type           Disp Enb Address    What
    1       breakpoint     keep y   0x000107b4 in produce at semaphore.c:72
    	breakpoint already hit 3 times
    2       breakpoint     keep y   0x00010884 in consume at semaphore.c:108
    	breakpoint already hit 3 times
    (gdb) r
    Starting program: /home/pi/Project/cCode/IPC/a.out 3
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
    [New Thread 0xb6e31460 (LWP 2842)]
    [New Thread 0xb6630460 (LWP 2843)]
    [Switching to Thread 0xb6e31460 (LWP 2842)]
    
    Thread 2 "a.out" hit Breakpoint 1, produce (arg=0x0) at semaphore.c:72
    72			sem_wait(shared.nempty);	/* wait for at least 1 empty slot */
    1: shared = {buff = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, mutex = 0xb6ff8000, 
      nempty = 0xb6ff7000, nstored = 0xb6ff6000}
    2: *shared.nempty = {
      __size = "\024\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 20}
    3: *shared.nstored = {
      __size = "\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 0}
    4: *shared.mutex = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    (gdb) c
    Continuing.
    [Switching to Thread 0xb6630460 (LWP 2843)]
    
    Thread 3 "a.out" hit Breakpoint 2, consume (arg=0x0) at semaphore.c:108
    108			sem_wait(shared.nstored);		/* wait for at least 1 stored item */
    1: shared = {buff = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, mutex = 0xb6ff8000, 
      nempty = 0xb6ff7000, nstored = 0xb6ff6000}
    2: *shared.nempty = {
      __size = "\024\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 20}
    3: *shared.nstored = {
      __size = "\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 0}
    4: *shared.mutex = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    (gdb) c
    Continuing.
    [Switching to Thread 0xb6e31460 (LWP 2842)]
    
    Thread 2 "a.out" hit Breakpoint 1, produce (arg=0x0) at semaphore.c:72
    72			sem_wait(shared.nempty);	/* wait for at least 1 empty slot */
    1: shared = {buff = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, mutex = 0xb6ff8000, 
      nempty = 0xb6ff7000, nstored = 0xb6ff6000}
    2: *shared.nempty = {
      __size = "\022\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 18}
    3: *shared.nstored = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    4: *shared.mutex = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    (gdb) c
    Continuing.
    [Switching to Thread 0xb6630460 (LWP 2843)]
    
    Thread 3 "a.out" hit Breakpoint 2, consume (arg=0x0) at semaphore.c:108
    108			sem_wait(shared.nstored);		/* wait for at least 1 stored item */
    1: shared = {buff = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, mutex = 0xb6ff8000, 
      nempty = 0xb6ff7000, nstored = 0xb6ff6000}
    2: *shared.nempty = {
      __size = "\022\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 18}
    3: *shared.nstored = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    4: *shared.mutex = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    (gdb) c
    Continuing.
    [Switching to Thread 0xb6e31460 (LWP 2842)]
    
    Thread 2 "a.out" hit Breakpoint 1, produce (arg=0x0) at semaphore.c:72
    72			sem_wait(shared.nempty);	/* wait for at least 1 empty slot */
    1: shared = {buff = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, mutex = 0xb6ff8000, 
      nempty = 0xb6ff7000, nstored = 0xb6ff6000}
    2: *shared.nempty = {
      __size = "\022\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 18}
    3: *shared.nstored = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    4: *shared.mutex = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    (gdb) c
    Continuing.
    buff[1] = 1
    [Thread 0xb6e31460 (LWP 2842) exited]
    [Switching to Thread 0xb6630460 (LWP 2843)]
    
    Thread 3 "a.out" hit Breakpoint 2, consume (arg=0x0) at semaphore.c:108
    108			sem_wait(shared.nstored);		/* wait for at least 1 stored item */
    1: shared = {buff = {0, 1, 2, 0, 0, 0, 0, 0, 0, 0}, mutex = 0xb6ff8000, 
      nempty = 0xb6ff7000, nstored = 0xb6ff6000}
    2: *shared.nempty = {
      __size = "\022\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 18}
    3: *shared.nstored = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    4: *shared.mutex = {
      __size = "\002\000\000\000\200\000\000\000\000\000\000\000\000\000\000", 
      __align = 2}
    (gdb) c
    Continuing.
    buff[2] = 2
    [Thread 0xb6ffa070 (LWP 2841) exited]
    [Inferior 1 (process 2841) exited normally]
    (gdb) 
    
    https://unix.stackexchange.com/questions/275650/where-is-a-named-semaphore-stored
  • sync, mutex. unix network programming p161

    다시 thread로 돌아왔다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
    #define	MAXNITEMS 		1000000
    #define	MAXNTHREADS			100
    #define MIN(X,Y) ((X)<(Y) ? (X) : (Y))
    
    
    
    //gcc -lpthread 옵션으로 컴파일.
    int		nitems;			/* read-only by producer and consumer */
    
    
    //shared를 초기화 하는데,
    //맨 처음에 mutext가 있음.
    //나머지는 0으로 초기화됨..
    struct {
      pthread_mutex_t	mutex;
      int	buff[MAXNITEMS];
      int	nput;
      int	nval;
    } shared = { PTHREAD_MUTEX_INITIALIZER };
    
    void	*produce(void *), *consume(void *);
    
    
    int
    main(int argc, char **argv)
    {
    	int			i, nthreads, count[MAXNTHREADS];
    	pthread_t	tid_produce[MAXNTHREADS], tid_consume;
    	//앞에서부터 초기화.
    	struct test {
    		int no1;
    		int no2;
    	} teststruct = {10};
    
    	if (argc != 3)
    		perror("usage: prodcons2 <#items> <#threads>");
    	nitems = MIN(atoi(argv[1]), MAXNITEMS);
    	nthreads = MIN(atoi(argv[2]), MAXNTHREADS);
    
    	//pthread_setconcurrency();
    		/* 4start all the producer threads */
    	for (i = 0; i < nthreads; i++) {
    		count[i] = 0;
    		pthread_create(&tid_produce[i], NULL, produce, &count[i]);
    	}
    
    		/* 4wait for all the producer threads */
    	for (i = 0; i < nthreads; i++) {
    		pthread_join(tid_produce[i], NULL);
    		printf("count[%d] = %d\n", i, count[i]);	
    	}
    
    		/* 4start, then wait for the consumer thread */
    	pthread_create(&tid_consume, NULL, consume, NULL);
    	pthread_join(tid_consume, NULL);
    
    	exit(0);
    }
    /* end main */
    
    /* include producer */
    void *
    produce(void *arg)
    {
    	for ( ; ; ) {
    		pthread_mutex_lock(&shared.mutex);
    		if (shared.nput >= nitems) {
    			pthread_mutex_unlock(&shared.mutex);
    			return(NULL);		/* array is full, we're done */
    		}
    		shared.buff[shared.nput] = shared.nval;
    		shared.nput++;
    		shared.nval++;
    		pthread_mutex_unlock(&shared.mutex);
    		*((int *) arg) += 1;
    	}
    }
    
    void *
    consume(void *arg)
    {
    	int		i;
    
    	for (i = 0; i < nitems; i++) {
    		if (shared.buff[i] != i)
    			printf("buff[%d] = %d\n", i, shared.buff[i]);
    	}
    	return(NULL);
    }
    /* end producer */
    
  • message queue, unix network programming 75p ~ 90p

    fifo는 먼저 들어온 순서로 동작하지만, message queue는 빠른 우선순위로 동작한다.

    signal handler를 정의하여 사용할 수도 있다. message noty는 queue가 비어있을 경우 한번 동작한다. queue가 차 있다면 signal을 무시한다. 한 번 사용하면 다시 시그널을 등록해야 한다.

    pi@raspberrypi:~/Project/cCode/IPC $ cat mesquereceive.c 
    //message que header
    #include <unistd.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <mqueue.h>
    #include <signal.h>
    #define MAX_SIZE 100
    
    //signal 처리하기 위한 hanlder
    static void sig_usr1(int mysig);
    
    struct sigevent sigev;
    //signal hanlder로 사용하려고 global로 등록.
    mqd_t	mqd;
    struct mq_attr attr;
    int priorecv;
    char buff[100];
    
    int main(){
    	int		c, flags;
    	char const *quename="/tmp_message";
    	char messageptr[50]= "this is a test.";
    	//메세지 큐 초기화
    	attr.mq_flags = 0;
        attr.mq_maxmsg = 10;
        attr.mq_msgsize = MAX_SIZE;
        attr.mq_curmsgs = 0;
    
    
    	//message를 넣기전에 mq_notify 정의
    	signal(SIGUSR1, sig_usr1);
    	sigev.sigev_notify = SIGEV_SIGNAL;
    	sigev.sigev_signo = SIGUSR1;
    	mq_notify(mqd, &sigev);
    
    	flags = O_RDWR | O_CREAT;
    	//gcc 컴파일시 gcc mesque.c -lrt
    	//로 해야 컴파일 됨.
    	mqd = mq_open(quename, flags, 0666, &attr);
    	size_t len = sizeof(messageptr);
    	//mq_send(mqd, messageptr, len, 10);
    	//sleep(1);
    
    
    	//넣은 메세지를 확인
    	//mq_receive(mqd, buff, attr.mq_msgsize, &priorecv);
    	//printf("받은 메세지는\n %s를 %d 우선 순위로\n", buff, priorecv);
    	//mq_receive(mqd, buff, attr.mq_msgsize, &priorecv);
    	//printf("받은 메세지는\n %s를 %d 우선 순위로\n", buff, priorecv);
    	for(;;)
    		pause();
    	mq_close(mqd);
    	return 0;
    }
    
    void sig_usr1(int mysig){
    	//signal hanlder로 message receive..
    
    	//한번 사용한 noty는 다시 등록.
    	mq_notify(mqd, &sigev);
    	mq_receive(mqd, buff, attr.mq_msgsize, &priorecv);
    	printf("받은 메세지는\n %s를 %d 우선 순위로\n", buff, priorecv);
    	return;
    
    
    
    }
    
    pi@raspberrypi:~/Project/cCode/IPC $ cat mesquesender.c 
    //message que header
    #include <unistd.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <mqueue.h>
    #include <stdlib.h>
    #include <signal.h>
    #define MAX_SIZE 100
    
    
    mqd_t	mqd;
    struct mq_attr attr;
    int priorecv;
    char buff[100];
    
    int main(int argc, char** argv){
    	int		c, flags;
    	int prio;
    	char const *quename="/tmp_message";
    	char messageptr[50]= "this is a test.";
    	//메세지 큐 초기화
    	attr.mq_flags = 0;
        attr.mq_maxmsg = 10;
        attr.mq_msgsize = MAX_SIZE;
        attr.mq_curmsgs = 0;
    
    	flags = O_RDWR | O_CREAT;
    	//gcc 컴파일시 gcc mesque.c -lrt
    	//로 해야 컴파일 됨.
    	mqd = mq_open(quename, flags, 0666, &attr);
    	size_t len = sizeof(messageptr);
    
    	prio = atoi(argv[1]);
    	mq_send(mqd, messageptr, len, prio);
    	mq_close(mqd);
    	return 0;
    }
    
    pi@raspberrypi:~/Project/cCode/IPC $ mv mesque.c mesquereceive.c
    pi@raspberrypi:~/Project/cCode/IPC $ gcc -lrt mesquereceive.c -o recv
    pi@raspberrypi:~/Project/cCode/IPC $ gcc -lrt mesquesender.c -o send
    pi@raspberrypi:~/Project/cCode/IPC $ cat /dev/mqueue/tmp_message 
    QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 
    세그멘테이션 오류
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 10
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 20
    pi@raspberrypi:~/Project/cCode/IPC $ cat /dev/mqueue/tmp_message 
    QSIZE:100        NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 100
    pi@raspberrypi:~/Project/cCode/IPC $ ./recv 
    ^Z
    [1]+  Stopped                 ./recv
    pi@raspberrypi:~/Project/cCode/IPC $ bg
    [1]+ ./recv &
    pi@raspberrypi:~/Project/cCode/IPC $ cat /dev/mqueue/tmp_message 
    QSIZE:150        NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 15
    pi@raspberrypi:~/Project/cCode/IPC $ bg
    -bash: bg: job 1 already in background
    pi@raspberrypi:~/Project/cCode/IPC $ jobs
    [1]+  Running                 ./recv &
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 20
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv 
    16120
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv |xargs kill -10
    받은 메세지는
     this is a test.를 100 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv |xargs kill -10
    받은 메세지는
     this is a test.를 20 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv |xargs kill -10
    받은 메세지는
     this is a test.를 20 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv |xargs kill -10
    받은 메세지는
     this is a test.를 15 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv |xargs kill -10
    받은 메세지는
     this is a test.를 10 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ pidof recv |xargs kill -10
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 100
    받은 메세지는
     this is a test.를 100 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ cat /dev/mqueue/tmp_message 
    QSIZE:0          NOTIFY:0     SIGNO:10    NOTIFY_PID:16120 
    pi@raspberrypi:~/Project/cCode/IPC $ ./send 10
    받은 메세지는
     this is a test.를 10 우선 순위로
    pi@raspberrypi:~/Project/cCode/IPC $ cat /dev/mqueue/tmp_message 
    QSIZE:0          NOTIFY:0     SIGNO:10    NOTIFY_PID:16120 
    pi@raspberrypi:~/Project/cCode/IPC $ 
    
    https://www.joinc.co.kr/w/Site/system_programing/IPC/MessageQueue

    gcc 기본 컴파일하면 에러를 본다. 옵션 넣어야 한다. -lrt.

    https://stackoverflow.com/questions/19964206/weird-posix-message-queue-linking-issue-sometimes-it-doesnt-link-correctly
  • fifo 예제. unix network programming 55p

    pipe는 이름을 없어 parent, child process만 통신이 가능하다. 이를 보안한게 named pipe인데, fifo라고도 한다. 책은 다른 유닉스 버전으로 작성하여, linux에 맞게 수정했다. 결론적으로 pipe와 비슷하고 더 쉽다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    
    #define	FIFO1	"/tmp/fifo.1"
    #define	FIFO2	"/tmp/fifo.2"
    #define MAXLINE 100
    
    void	client(int, int), server(int, int);
    
    void server(int readfd, int writefd){
    	/*
    	char	buff[MAXLINE];
    	int n;
    	n = read(readfd, buff, MAXLINE);
    	printf("서버가 읽은 data는 %s\n",buff);
    	// 샘플 테스트용..
    
    	*/
    	int fd;
    	ssize_t n;
    	char	buff[MAXLINE+1];
    
    	/* 4read pathname from IPC channel */
    	if ( (n = read(readfd, buff, MAXLINE)) == 0)
    		perror("end-of-file while reading pathname");
    	buff[n] = '\0';		/* null terminate pathname */
    
    	if ( (fd = open(buff, O_RDONLY)) < 0) {
    			/* 4error: must tell client */
    		snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n",
    				 strerror(errno));
    		n = strlen(buff);
    		write(writefd, buff, n);
    
    	} else {
    			/* 4open succeeded: copy file to IPC channel */
    		while ( (n = read(fd, buff, MAXLINE)) > 0)
    			write(writefd, buff, n);
    		close(fd);
    	}
    
    }
    
    void client(int readfd, int writefd)
    {
    	size_t	len;
    	ssize_t	n;
    	char	buff[MAXLINE];
    
    	/* 4read pathname */
    	fgets(buff, MAXLINE, stdin);
    	len = strlen(buff);		/* fgets() guarantees null byte at end */
    	if (buff[len-1] == '\n')
    		len--;				/* delete newline from fgets() */
    
    	/* 4write pathname to IPC channel */
    	write(writefd, buff, len);
    
    	/* 4read from IPC, write to standard output */
    	while ( (n = read(readfd, buff, MAXLINE)) > 0)
    		write(STDOUT_FILENO, buff, n);
    }
    
    
    int
    main(int argc, char **argv)
    {
    	int		readfd, writefd;
    	pid_t	childpid;
    
    		/* 4create two FIFOs; OK if they already exist */
    	if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST))
    		printf("can't create %s", FIFO1);
    	if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST)) {
    		unlink(FIFO1);
    		printf("can't create %s", FIFO2);
    	}
    
    	if ( (childpid = fork()) == 0) {		/* child */
    		readfd = open(FIFO1, O_RDONLY, 0);
    		writefd = open(FIFO2, O_WRONLY, 0);
    
    		server(readfd, writefd);
    		exit(0);
    	}
    		/* 4parent */
    	writefd = open(FIFO1, O_WRONLY, 0);
    	readfd = open(FIFO2, O_RDONLY, 0);
    
    	client(readfd, writefd);
    
    	waitpid(childpid, NULL, 0);		/* wait for child to terminate */
    
    	close(readfd);
    	close(writefd);
    
    	unlink(FIFO1);
    	unlink(FIFO2);
    	exit(0);
    }
    
    pi@raspberrypi:~/Project/cCode/IPC $ ls
    a.out      fifo.c  simplepipe.c  unpv22e
    fduplex.c  pipe.c  test.txt      unpv22e.tar
    pi@raspberrypi:~/Project/cCode/IPC $ cat test.txt 
    안녕하세요.
    hello.
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    ./test.txt
    안녕하세요.
    hello.
    pi@raspberrypi:~/Project/cCode/IPC $ 
    

    이 장에 여러 내용도 많은데 일단 다른 유닉스 버전도 보이고 시간도 없어 다음 장으로 넘어간다.

    https://www.geeksforgeeks.org/named-pipe-fifo-example-c-program/
  • pipe 예제. unix network programming 47p

    pipe를 이해하기 전, 일단 gdb를 사용하는 방법을 알아야 했다. pipe가 서로 다른 프로세스를 연결하는 수단이라, gdb 기본 설정으로는 pipe로 어떤 내용을 확인하기 어려웠다. 아래 코드를 디버그 하기로 했다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <string.h>
    #define MAXLINE 100
    
    void client(int, int), server(int, int);
    
    void server(int readfd, int writefd){
    	char	buff[MAXLINE];
    	int n;
    	n = read(readfd, buff, MAXLINE);
    	printf("서버가 읽은 data는 %s\n",buff);
    }
    
    void client(int readfd, int writefd)
    {
    	size_t	len;
    	ssize_t	n;
    	char	buff[MAXLINE];
    
    	/* 4read pathname */
    	fgets(buff, MAXLINE, stdin);
    	len = strlen(buff);		/* fgets() guarantees null byte at end */
    	if (buff[len-1] == '\n')
    		len--;				/* delete newline from fgets() */
    
    	/* 4write pathname to IPC channel */
    	write(writefd, buff, len);
    
    	/* 4read from IPC, write to standard output */
    	while ( (n = read(readfd, buff, MAXLINE)) > 0)
    		write(STDOUT_FILENO, buff, n);
    }
    
    
    int main(int argc, char **argv)
    {
    	int		pipe1[2], pipe2[2];
    	pid_t	childpid, childpid2;
    	pid_t zombieFlag;
    
    	if(	pipe(pipe1) < 0)
    		exit(1);
    	if (pipe(pipe2) < 0)
    		exit(1);
    
    
    	printf("made pipe1, pipe2\n");
    	//child process pipe 닫음.
    	if( (childpid = fork()) == 0) {		//난 자식..
    		close(pipe1[1]);
    		close(pipe2[0]);
    		childpid2 = getpid();
    
    		printf("자식 process는 %d\n",childpid2);
    		server(pipe1[0], pipe2[1]);
    
    		//    pipe1 ----> server, server process 입력은 pipe1 출력.
    		//    pipe2 <---- server, server process 출력은 pipi2 입력.
    		exit(0);
    	}
    	// 난 부모.
    	close(pipe1[0]);
    	close(pipe2[1]);
    	client(pipe2[0], pipe1[1]);
    
    	zombieFlag = waitpid(childpid, NULL, 0);
    	printf("종료된 자식 process는 %d\n",zombieFlag);
    	//sleep(1);
    
    	exit(0);
    }
    
    

    이 내용을 gdb로 확인했다. gdb를 그냥 실행하면 detach-on-fork 옵션을 on으로 갖는다. child process로 분기하면 기존 프로세스를 끝까지 실행하는 옵션인 듯 하다. parent 프로세스를 정지시키고, inferior로 child 프로세스로 이동하여 나머지를 실행하고 싶었다. 그러나 detach-on-fork off 에서는 프로세스가 끝나 inferior로 이동해도 실행할 수 없다. detatch-on-fork off로 기존 프로세스를 멈추고 inferior로 다른 프로세스로 옮겨 확인했다.

    pi@raspberrypi:~/Project/cCode/IPC $ gdb ./a.out 
    GNU gdb (Raspbian 8.2.1-2) 8.2.1
    Copyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "arm-linux-gnueabihf".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from ./a.out...done.
    (gdb) show detach-on-fork 
    Whether gdb will detach the child of a fork is on.
    (gdb) set detach-on-fork off
    (gdb) show follow-fork-mode 
    Debugger response to a program call of fork or vfork is "parent".
    (gdb) l
    31		write(writefd, buff, len);
    32	
    33		/* 4read from IPC, write to standard output */
    34		while ( (n = read(readfd, buff, MAXLINE)) > 0)
    35			write(STDOUT_FILENO, buff, n);
    36	}
    37	
    38	
    39	int main(int argc, char **argv)
    40	{
    (gdb) l 2
    1	#include <stdlib.h>
    2	#include <stdio.h>
    3	#include <unistd.h>
    4	#include <sys/types.h>
    5	#include <sys/wait.h>
    6	#include <string.h>
    7	#define MAXLINE 100
    8	
    9	void client(int, int), server(int, int);
    10	
    (gdb) l
    11	void server(int readfd, int writefd){
    12		char	buff[MAXLINE];
    13		int n;
    14		n = read(readfd, buff, MAXLINE);
    15		printf("서버가 읽은 data는 %s\n",buff);
    16	}
    17	
    18	void client(int readfd, int writefd)
    19	{
    20		size_t	len;
    (gdb) b 14
    Breakpoint 1 at 0x10658: file pipe.c, line 14.
    (gdb) l
    21		ssize_t	n;
    22		char	buff[MAXLINE];
    23	
    24		/* 4read pathname */
    25		fgets(buff, MAXLINE, stdin);
    26		len = strlen(buff);		/* fgets() guarantees null byte at end */
    27		if (buff[len-1] == '\n')
    28			len--;				/* delete newline from fgets() */
    29	
    30		/* 4write pathname to IPC channel */
    (gdb) b 25
    Breakpoint 2 at 0x106a4: file pipe.c, line 25.
    (gdb) r
    Starting program: /home/pi/Project/cCode/IPC/a.out 
    made pipe1, pipe2
    [New inferior 2 (process 2918)]
    Reading symbols from /home/pi/Project/cCode/IPC/a.out...done.
    Reading symbols from /usr/lib/debug/.build-id/ef/dd27c16f5283e5c53dcbd1bbc3ef136e312d1b.debug...done.
    Reading symbols from /usr/lib/debug/.build-id/fb/85e699c11db06c7b24f74de2cdada3146442a8.debug...done.
    
    Thread 1.1 "a.out" hit Breakpoint 2, client (readfd=5, writefd=4)
        at pipe.c:25
    25		fgets(buff, MAXLINE, stdin);
    (gdb) n
    this is a test.
    26		len = strlen(buff);		/* fgets() guarantees null byte at end */
    (gdb) display buff
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    27		if (buff[len-1] == '\n')
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    28			len--;				/* delete newline from fgets() */
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    31		write(writefd, buff, len);
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    34		while ( (n = read(readfd, buff, MAXLINE)) > 0)
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    ^Z
    Thread 1.1 "a.out" received signal SIGTSTP, Stopped (user).
    0xb6f2187c in __GI___libc_read (fd=5, buf=0xbefff384, nbytes=100)
        at ../sysdeps/unix/sysv/linux/read.c:26
    26	../sysdeps/unix/sysv/linux/read.c: 그런 파일이나 디렉터리가 없습니다.
    (gdb) info inferiors
      Num  Description       Executable        
    * 1    process 2915      /home/pi/Project/cCode/IPC/a.out 
      2    process 2918      /home/pi/Project/cCode/IPC/a.out 
    (gdb) inferior 2
    [Switching to inferior 2 [process 2918] (/home/pi/Project/cCode/IPC/a.out)]
    [Switching to thread 2.1 (process 2918)]
    #0  arch_fork (ctid=0xb6ff9cf8) at ../sysdeps/unix/sysv/linux/arch-fork.h:40
    40	../sysdeps/unix/sysv/linux/arch-fork.h: 그런 파일이나 디렉터리가 없습니다.
    (gdb) c
    Continuing.
    
    Thread 2.1 "a.out" received signal SIGTSTP, Stopped (user).
    arch_fork (ctid=0xb6ff9cf8) at ../sysdeps/unix/sysv/linux/arch-fork.h:40
    40	in ../sysdeps/unix/sysv/linux/arch-fork.h
    (gdb) c
    Continuing.
    
    Thread 2.1 "a.out" received signal SIGTSTP, Stopped (user).
    arch_fork (ctid=0xb6ff9cf8) at ../sysdeps/unix/sysv/linux/arch-fork.h:40
    40	in ../sysdeps/unix/sysv/linux/arch-fork.h
    (gdb) 
    Continuing.
    자식 process는 2918
    
    Thread 2.1 "a.out" hit Breakpoint 1, server (readfd=3, writefd=6)
        at pipe.c:14
    14		n = read(readfd, buff, MAXLINE);
    (gdb) display buff
    2: buff = "x\371\377\266,\003\001\000P\241\377\266\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266\062\071\061\070\000:\215\371\001\000\000\000h\351\377\266p\b\001\000T\005\001", '\000' <repeats 14 times>, "\360\377\266$\364\377\276pD\352\266\354\363\377\276\000:\215\371\000\000\000\000\364\a\001\000\024\t\001"
    (gdb) n
    15		printf("서버가 읽은 data는 %s\n",buff);
    2: buff = "this is a test.\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266\062\071\061\070\000:\215\371\001\000\000\000h\351\377\266p\b\001\000T\005\001", '\000' <repeats 14 times>, "\360\377\266$\364\377\276pD\352\266\354\363\377\276\000:\215\371\000\000\000\000\364\a\001\000\024\t\001"
    (gdb) n
    서버가 읽은 data는 this is a test.
    16	}
    2: buff = "this is a test.\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266\062\071\061\070\000:\215\371\001\000\000\000h\351\377\266p\b\001\000T\005\001", '\000' <repeats 14 times>, "\360\377\266$\364\377\276pD\352\266\354\363\377\276\000:\215\371\000\000\000\000\364\a\001\000\024\t\001"
    (gdb) n
    main (argc=1, argv=0xbefff574) at pipe.c:63
    63			exit(0);
    (gdb) c
    Continuing.
    [Inferior 2 (process 2918) exited normally]
    (gdb) c
    Continuing.
    
    Thread 1 "a.out" received signal SIGTSTP, Stopped (user).
    0xb6f2187c in __GI___libc_read (fd=5, buf=0xbefff384, nbytes=100)
        at ../sysdeps/unix/sysv/linux/read.c:26
    26	../sysdeps/unix/sysv/linux/read.c: 그런 파일이나 디렉터리가 없습니다.
    (gdb) c
    Continuing.
    종료된 자식 process는 2918
    [Inferior 1 (process 2915) exited normally]
    

    다음은 수정된 코드이다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <string.h>
    #include <errno.h>
    #define MAXLINE 100
    
    void client(int, int), server(int, int);
    
    void server(int readfd, int writefd){
    	/*
    	char	buff[MAXLINE];
    	int n;
    	n = read(readfd, buff, MAXLINE);
    	printf("서버가 읽은 data는 %s\n",buff);
    	// 샘플 테스트용..
    
    	*/
    	int fd;
    	ssize_t n;
    	char	buff[MAXLINE+1];
    
    	/* 4read pathname from IPC channel */
    	if ( (n = read(readfd, buff, MAXLINE)) == 0)
    		perror("end-of-file while reading pathname");
    	buff[n] = '\0';		/* null terminate pathname */
    
    	if ( (fd = open(buff, O_RDONLY)) < 0) {
    			/* 4error: must tell client */
    		snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n",
    				 strerror(errno));
    		n = strlen(buff);
    		write(writefd, buff, n);
    
    	} else {
    			/* 4open succeeded: copy file to IPC channel */
    		while ( (n = read(fd, buff, MAXLINE)) > 0)
    			write(writefd, buff, n);
    		close(fd);
    	}
    
    }
    
    void client(int readfd, int writefd)
    {
    	size_t	len;
    	ssize_t	n;
    	char	buff[MAXLINE];
    
    	/* 4read pathname */
    	fgets(buff, MAXLINE, stdin);
    	len = strlen(buff);		/* fgets() guarantees null byte at end */
    	if (buff[len-1] == '\n')
    		len--;				/* delete newline from fgets() */
    
    	/* 4write pathname to IPC channel */
    	write(writefd, buff, len);
    
    	/* 4read from IPC, write to standard output */
    	while ( (n = read(readfd, buff, MAXLINE)) > 0)
    		write(STDOUT_FILENO, buff, n);
    }
    
    
    int main(int argc, char **argv)
    {
    	int		pipe1[2], pipe2[2];
    	pid_t	childpid, childpid2;
    	pid_t zombieFlag;
    
    	if(	pipe(pipe1) < 0)
    		exit(1);
    	if (pipe(pipe2) < 0)
    		exit(1);
    
    
    	printf("made pipe1, pipe2\n");
    	//child process pipe 닫음.
    	if( (childpid = fork()) == 0) {		//난 자식..
    		close(pipe1[1]);
    		close(pipe2[0]);
    		childpid2 = getpid();
    
    		printf("자식 process는 %d\n",childpid2);
    		server(pipe1[0], pipe2[1]);
    
    		//    pipe1 ----> server, server process 입력은 pipe1 출력.
    		//    pipe2 <---- server, server process 출력은 pipi2 입력.
    		exit(0);
    	}
    	// 난 부모.
    	close(pipe1[0]);
    	close(pipe2[1]);
    	client(pipe2[0], pipe1[1]);
    
    	zombieFlag = waitpid(childpid, NULL, 0);
    	printf("종료된 자식 process는 %d\n",zombieFlag);
    	//sleep(1);
    
    	exit(0);
    }
    pi@raspberrypi:~/Project/cCode/IPC $ cat test.txt 
    안녕하세요.
    hello.
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    made pipe1, pipe2
    자식 process는 4890
    this is a test.
    this is a test.: can't open, No such file or directory
    종료된 자식 process는 4890
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    made pipe1, pipe2
    자식 process는 4893
    ./test
    ./test: can't open, No such file or directory
    종료된 자식 process는 4893
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    made pipe1, pipe2
    자식 process는 4895
    ./test.txt
    안녕하세요.
    hello.
    종료된 자식 process는 4895
    

    잘 실행된다. pipe를 만들고 파이프 양 끝에 입력, 출력으로 생각했는데, 이러면 헷갈린다. 프로세스 기준으로 입력, 출력을 생각하고 파이프로 연결한다 생각하면 쉽다.

    하… 가벼운 마음으로 들어왔는데 IPC를 책 한 권에 걸쳐 설명하네.

    https://www.geeksforgeeks.org/pipe-system-call/
    https://woosunbi.tistory.com/94
    https://moss.cs.iit.edu/cs351/gdb-inferiors.html