[태그:] linux

  • 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

  • c 더블 포인터 이해하기.

    c는 포인터가 혼란스러운데 이해했다고 믿고???? 나중에 다시 생각하려고 남긴다.

    다음 코드를 만들었고 실패했다. bufToStruct를 실행할 때 포인터로 넘겼어도 buf를 받으면서 임시 공간에 변수를 만든다. 함수 실행을 끝내면서 그 공간도 날아가 도로묵 된다.

    #include <stdio.h>
    #include "module1.h"
    int main()
    {
        struct mysqlStruct oneSqlData;
        char readbuf[MAX_LEN];
        int ret;
        openfile(readbuf);
        printf("버퍼는 %s\n",readbuf);
        do{ 
            ret=bufToStruct(readbuf, &oneSqlData);
            printf("%p\n", readbuf);
            sleep(1);
        }while(ret==-1);
        //bufToStruct(readbuf, &oneSqlData);
    
        return 0;
    }
    int bufToStruct(char* buf, struct mysqlStruct *sqlData){
    
    	//buf를 읽어 날자, 시각, 온도로 분리.
    	//,로 분리되어 delimiter로 분리..
    	//한번 읽을때마다 buf 한 줄 내용을 sqlData로 이동.
    	//성공하면 0,
    	//마지막이면 1,
    	//실패하면 -1을 return.
    
    	size_t len;
    	char* temp;		//시작 위치
    	len=strlen(buf);
    	char tempbuf[len];		//strtok로 작업할 내용. strtok이 원래 버퍼를 수정함.
    	char *year,*month, *day, *hour, *minute, *temperature;
    	int year2, month2, day2, hour2, minute2, temperature2;
    	//strtok이 원래 버퍼를 잘라 버려.
    	//임시로 카피해서 사용.
    	//원래 포인터는 토큰 크기만큼 넘김.
    	strcpy(tempbuf, buf);
    
    	//첫줄을 읽을 경우 무시할 방법 필요.
    	//그냥 넣어서 에러, 정상 확인.
    	temp = strtok(tempbuf, "\n");
    
    	len=strlen(temp);
    	year=strtok(temp, "-");
    	//무슨 데이터인지 모르겠지만, 일단 숫자료 변경.
    	year2=atoi(year);
    
    	//buf 내용 삭제..temp 만큼.
    	//buf 포인터를 temp 만큼 이동.
    	for(int i=0;i<len;i++)
    		buf++;
    
    	if(year2 < 2000 || year2 > 2100)
    		return -1;
    
    	//정상대로 진행.
    	sqlData->year = year2;
    	printf("year2 is %d\n",year2);
    	month=strtok(temp,"-");
    	month=strtok(temp,"-");
    	month2=atoi(month);
    	sqlData->month = month2;
    
    	
    }

    위 내용을 gdb로 확인해 보았다.

    (gdb) run
    Starting program: /home/pi/Project/cCode/module1/insertTemperature 
    버퍼는 날자, 온도
    2020-7-9 10:00:00, 24.5
    2020-7-9 10:01:00, 24.5
    2020-7-9 10:02:00, 24.5
    2020-7-9 10:03:00, 24.5
    
    
    Breakpoint 1, main () at main.c:11
    11			ret=bufToStruct(readbuf, &oneSqlData);
    (gdb) display &readbuf
    1: &readbuf = (char (*)[200]) 0xbefff30c
    (gdb) s
    bufToStruct (
        buf=0xbefff30c "날자, 온도\n2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n", sqlData=0xbefff3d4)
        at module1.c:42
    42	int bufToStruct(char* buf, struct mysqlStruct *sqlData){
    (gdb) l
    37	
    38		// ruturn 성공.
    39		return 0;
    40	}
    41	
    42	int bufToStruct(char* buf, struct mysqlStruct *sqlData){
    43	
    44		//buf를 읽어 날자, 시각, 온도로 분리.
    45		//,로 분리되어 delimiter로 분리..
    46		//한번 읽을때마다 buf 한 줄 내용을 sqlData로 이동.
    (gdb) 
    47		//성공하면 0,
    48		//마지막이면 1,
    49		//실패하면 -1을 return.
    50	
    51		size_t len;
    52		char* temp;		//시작 위치
    53		len=strlen(buf);
    54		char tempbuf[len];		//strtok로 작업할 내용. strtok이 원래 버퍼를 수정함.
    55		char *year,*month, *day, *hour, *minute, *temperature;
    56		int year2, month2, day2, hour2, minute2, temperature2;
    (gdb) display &buf
    2: &buf = (char **) 0xbefff2cc
    (gdb) display buf
    3: buf = 0xbefff30c "날자, 온도\n2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n"

    bufToStruct로 readbuf를 전달할 때 주소를 전달하는 직접 전달하길 원했다. bufToStruct로 전달하는 0xbefff2cc는 어디에서 나왔을까? 임시 변수로 자기가 만들었다. 임시로 만든(콜 하고 없어질) 주소에 포인터 값을 넣었다. 그러니 함수 내부에서 열심히 포인터를 움직여도 메인 함수 포인터는 변하지 않았다. &buf = 0xbefff30c로 바꿔야 한다. 이중 포인터가 그 역할을 한다.

    다음 그림을 참조하면 쉽게?? 이해할 수 있다. 이게 머라고 삽질하고 있는지..

    #include <stdio.h>
    #include "module1.h"
    int main()
    {
    	struct mysqlStruct oneSqlData;
    	char readbuf[MAX_LEN];
    	char* readbufPtr;
    	int ret;
    	openfile(readbuf);
    	readbufPtr = readbuf;
    	//printf("버퍼는 %s\n",readbufPtr);
    	do{
    		ret=bufToStruct(&readbufPtr, &oneSqlData);
    		printf("%s\n", readbufPtr);
    		sleep(1);
    	}while(ret==-1);
    	//bufToStruct(readbuf, &oneSqlData);
    
    	return 0;
    }
    
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include "module1.h"
    #include <string.h>
    #include <stdlib.h>
    
    int openfile(char* readbuf){
    
    
    	//파일을 열어,
    	//readbuf로 기록.
    
    	int fd;
    	//char buf[MAX_LEN];
    	struct stat myfileStat;
    	fd = open("./temperatureRecord.txt", O_RDONLY);
    	if (fd == -1){
    		perror("error to open file\n");
    		return -1;
    	}
    
    	//읽을 파일 크기 확인.
    	fstat(fd, &myfileStat);
    	//최대 버퍼량 대비 파일이 큰지 확인
    	if(MAX_LEN < myfileStat.st_size){
    		perror("file is too big\n");
    		//return  실패
    		return -1;
    	}
    	
    	read(fd, readbuf, myfileStat.st_size);
    	//printf("버퍼는 %s\n",buf);
    	close(fd);
    
    	// ruturn 성공.
    	return 0;
    }
    
    int bufToStruct(char** buf, struct mysqlStruct *sqlData){
    
    	//buf를 읽어 날자, 시각, 온도로 분리.
    	//,로 분리되어 delimiter로 분리..
    	//한번 읽을때마다 buf 한 줄 내용을 sqlData로 이동.
    	//성공하면 0,
    	//마지막이면 1,
    	//실패하면 -1을 return.
    
    	size_t len, len2;
    	char* temp;		//시작 위치
    	len=strlen(*buf);
    	char tempbuf[len];		//strtok로 작업할 내용. strtok이 원래 버퍼를 수정함.
    	char *year,*month, *day, *hour, *minute, *temperature;
    	int year2, month2, day2, hour2, minute2, temperature2;
    	//strtok이 원래 버퍼를 잘라 버려.
    	//임시로 카피해서 사용.
    	//원래 포인터는 토큰 크기만큼 넘김.
    	strcpy(tempbuf, *buf);
    
    	//첫줄을 읽을 경우 무시할 방법 필요.
    	//그냥 넣어서 에러, 정상 확인.
    	temp = strtok(tempbuf, "\n");
    
    	len=strlen(temp);
    	year=strtok(temp, "-");
    	//무슨 데이터인지 모르겠지만, 일단 숫자로 변경.
    	year2=atoi(year);
    
    	//buf 내용 삭제..temp 만큼.
    	//buf 포인터를 temp 만큼 이동.
    	//printf("함수 내부:포인터 이동 전\n %s",*buf);
    	*buf=*buf+len+strlen("\n");
    
    	//printf("함수 내부:포인터 이동 후\n %s",*buf);
    
    
    	if(year2 < 2000 || year2 > 2100)
    		return -1;
    
    	//정상대로 진행.
    	sqlData->year = year2;
    	printf("year2 is %d\n",year2);
    	month=strtok(temp,"-");
    	month=strtok(temp,"-");
    	month2=atoi(month);
    	sqlData->month = month2;
    }
    
    Breakpoint 1, main () at main.c:13
    13			ret=bufToStruct(&readbufPtr, &oneSqlData);
    1: readbufPtr = 0xbefff30c "날자, 온도\n2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n"
    2: &readbuf = (char (*)[200]) 0xbefff30c
    3: *readbufPtr = 235 '\353'
    (gdb) s
    bufToStruct (buf=0xbefff308, sqlData=0xbefff3d4) at module1.c:42
    warning: Source file is more recent than executable.
    42	int bufToStruct(char** buf, struct mysqlStruct *sqlData){
    (gdb) display &buf
    4: &buf = (char ***) 0xbefff2d4
    (gdb) display buf
    5: buf = (char **) 0xbefff308
    (gdb) display *buf
    6: *buf = 0xbefff30c "날자, 온도\n2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n"
    (gdb) l
    37	
    38		// ruturn 성공.
    39		return 0;
    40	}
    41	
    42	int bufToStruct(char** buf, struct mysqlStruct *sqlData){
    43	
    44		//buf를 읽어 날자, 시각, 온도로 분리.
    45		//,로 분리되어 delimiter로 분리..
    46		//한번 읽을때마다 buf 한 줄 내용을 sqlData로 이동.
    (gdb) 
    47		//성공하면 0,
    48		//마지막이면 1,
    49		//실패하면 -1을 return.
    50	
    51		size_t len, len2;
    52		char* temp;		//시작 위치
    53		len=strlen(*buf);
    54		char tempbuf[len];		//strtok로 작업할 내용. strtok이 원래 버퍼를 수정함.
    55		char *year,*month, *day, *hour, *minute, *temperature;
    56		int year2, month2, day2, hour2, minute2, temperature2;
    (gdb) 
    57		//strtok이 원래 버퍼를 잘라 버려.
    58		//임시로 카피해서 사용.
    59		//원래 포인터는 토큰 크기만큼 넘김.
    60		strcpy(tempbuf, *buf);
    61	
    62		//첫줄을 읽을 경우 무시할 방법 필요.
    63		//그냥 넣어서 에러, 정상 확인.
    64		temp = strtok(tempbuf, "\n");
    65	
    66		len=strlen(temp);
    (gdb) 
    67		year=strtok(temp, "-");
    68		//무슨 데이터인지 모르겠지만, 일단 숫자로 변경.
    69		year2=atoi(year);
    70	
    71		//buf 내용 삭제..temp 만큼.
    72		//buf 포인터를 temp 만큼 이동.
    73		//printf("함수 내부:포인터 이동 전\n %s",*buf);
    74		*buf=*buf+len+strlen("\n");
    75	
    76		//printf("함수 내부:포인터 이동 후\n %s",*buf);
    (gdb) b 74
    Breakpoint 3 at 0x10814: file module1.c, line 74.
    (gdb) n
    53		len=strlen(*buf);
    4: &buf = (char ***) 0xbefff2d4
    5: buf = (char **) 0xbefff308
    6: *buf = 0xbefff30c "날자, 온도\n2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n"
    (gdb) c
    Continuing.
    
    Breakpoint 3, bufToStruct (buf=0xbefff308, sqlData=0xbefff3d4) at module1.c:74
    74		*buf=*buf+len+strlen("\n");
    4: &buf = (char ***) 0xbefff2d4
    5: buf = (char **) 0xbefff308
    6: *buf = 0xbefff30c "날자, 온도\n2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n"
    (gdb) n
    79		if(year2 < 2000 || year2 > 2100)
    4: &buf = (char ***) 0xbefff2d4
    5: buf = (char **) 0xbefff308
    6: *buf = 0xbefff31b "2020-7-9 10:00:00, 24.5\n2020-7-9 10:01:00, 24.5\n2020-7-9 10:02:00, 24.5\n2020-7-9 10:03:00, 24.5\n"
    (gdb) 
    

    아래 사이트를 찾아 봤는데, 잘 이해가 안된다. 디버거로 주소 확인하는게 가장 확실했다. ㅠㅠ. 별 내용도 없는데, 왜케 오래걸리는지…

    https://www.geeksforgeeks.org/double-pointer-pointer-pointer-c/
    https://stackoverflow.com/questions/766893/how-do-i-modify-a-pointer-that-has-been-passed-into-a-function-in-c
    https://chanywa.com/343
  • Linux System Programming

    Linux System Programming

    isbn: 978-1449339531

    사 보진 않았지만 참 유용하다. 인터넷을 검색하면 pdf를 쉽게 찾을 수 있다. 400 페이지 책을 자세히 읽고 있으면 지친고 괴롭다. 예제 중심으로 읽고 세세한 나머지 부분을 통과 했다. 나중에 필요할 경우 다시 찾아 보면 된다. 다만 예제는 직접 실행했고 조금씩 수정해 보았다.

    전에 도서관에서 비슷한 책을 빌렸는데 그때 왜 이런 책을 읽는지 이해가 안됐었다. 비주얼 스튜디오 온라인 레퍼런스를 읽는 기분이었다. c 함수를 설명하고, 왜 이렇게 사용하는지 설명했는데 “그래서 어쩌라는 거지? 아, 나중에 이렇게 사용하나 보다”로 생각하니 힘들어 중간에 던져 버렸다. 그 때는 목적없이 읽었기 때문이다.

    이번에는 목적을 load하고 읽었다. 라즈베리 파이를 사용하려다 보니 c, 그것도 gnu c가 필수였다. stack overflow에서 알음알음 답을 찾는데 지쳤다. 여기 물어보기(최대한 비슷한 답을 찾기) 보다 일단 좋은 책 한 권 보고 기초를 탑재함이 낫다 생각했다. stack overflow 솔루션이 업계 표준으로 경험으로 얻는 그것인 줄 알았는데, 책 내용과 대부분 일치하는 부분이 많다. 프로그램을 어떻게 작성하는지 사람 맘대로지만, 개념업이 작성하면 많이 힘들다. 목적에 따라 이렇게 다른 결과를 내니 어이없다. 시험 전날 벼락치기가 왜 좋은지 증명했다.

    리눅스 개요를 1장에서 설명하는데 이 부분을 자세히 봐야 한다. 2장부터는 1장 내용을 반복, 세부 설명한다. 한 번만 읽고 작가 의도를 정확하게 판단하지 못했지만, 1장은 다시 읽어 볼만하다. 이런 기초없이 삽질부터 하면 시간 낭비가 정말 심할 듯 하다.

    리눅스 역시 사람 생각으로 만들다 보니 과거 내용을 그대로 사용하는 부분도 많다. gnu c를 사용함은 과거 실패를 수많은 사람이 수정하고 개선한 내용을 내가 쉽게 사용함과 같다. 고맙기도 하고 집단 지식 대표적 사례다. 그 만큼 시간 많은 인간이 많다.

  • signal, page 342

    signal 예제. shell에서 kill -l로 해당 PID에 시그널을 보낼 수 있다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    /* handler for SIGINT */
    
    
    static void signal_hanlder (int signo)
    {
    /*
    * Technically, you shouldn't use printf() in a
    * signal handler, but it isn't the end of the
    * world. I'll discuss why in the section
    * "Reentrancy."
    */
    	if(signo == SIGINT){
    		printf ("Caught SIGINT!\n");
    		exit (EXIT_SUCCESS);
    	}
    	else if(signo == SIGCHLD){
    		printf ("SIGCHLD occured!\n");
    		//exit (EXIT_SUCCESS);
    
    	}
    }
    
    static void sigchld_handler (int signo)
    {
    /*
    * Technically, you shouldn't use printf() in a
    * signal handler, but it isn't the end of the
    * world. I'll discuss why in the section
    * "Reentrancy."
    */
    	printf ("SIGCHLD occured!\n");
    	//exit (EXIT_SUCCESS);
    }
    
    static void sighup_handler (int signo)
    {
    /*
    * Technically, you shouldn't use printf() in a
    * signal handler, but it isn't the end of the
    * world. I'll discuss why in the section
    * "Reentrancy."
    */
    	printf ("SIGHUP occured!\n");
    	printf ("ignoring SIGHUP\n");
    	if (signal(SIGHUP, SIG_IGN) == SIG_ERR){
    		fprintf (stderr, "Cannot ignore SIGHUP!\n");
    		exit (EXIT_FAILURE);
    	}
    
    }
    
    
    int main (void)
    	{
    	/*
    	* Register signal_hanlder as our signal handler
    	* for SIGINT.
    	*/
    	int pid;
    
    	if (signal (SIGINT, signal_hanlder) == SIG_ERR) {
    		fprintf (stderr, "Cannot handle SIGINT!\n");
    		exit (EXIT_FAILURE);
    	}
    	if (signal(SIGCHLD, signal_hanlder) == SIG_ERR){
    		fprintf (stderr, "Cannot handle SIGCHLD!\n");
    		exit (EXIT_FAILURE);
    
    	}
    
    	//signal hangup 등록
    	//주석 처리 후 run.. shell로 signal 전송.
    	//pi@raspberrypi:~ $ kill -1 pid
    	//하면 
    	//pi@raspberrypi:~/Project/cCode/systemProgram $ rm ./a.out ;gcc signal.c ;./a.out
    	//I'm child, died
    	//SIGCHLD occured!
    	//끊어짐
    	//주석 처리하면
    	//pi@raspberrypi:~ $ kill -1 pid
    	//하면 무시함.
    
    	if (signal(SIGHUP, SIG_IGN) == SIG_ERR){
    		fprintf (stderr, "Cannot ignore SIGHUP!\n");
    		exit (EXIT_FAILURE);
    	}
    
    
    	//더 이어서..
    	//한번 sighup을 주면 메세지를 주고,
    	//두번째 받으면 무시되도록 작성.
    	if (signal(SIGHUP, sighup_handler) == SIG_ERR){
    		fprintf (stderr, "Cannot redefine SIGHUP!\n");
    		exit (EXIT_FAILURE);
    	}
    
    	pid=fork();
    
    	if(pid == 0){
    		//child process
    		printf("I'm child, died\n");
    		sleep(1);
    		_exit(EXIT_SUCCESS);
    	}
    
    	for (;;)
    		pause ();
    
    	return 0;
    }