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

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;
}

anonymous memory mapping, p308

https://www.miroch.ru/2017/01/17/linux-process-memory-layout/
proc pid maps

여기를 참조 했다.

#include <stdio.h>
#include <sys/mman.h>

int main(int argc, char *argv[]) {
	char *ptr;
	int ret;

	ptr = (char *) mmap((void *) 0x10000, 512 * 1024, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
	printf("page size is %0x\n",getpagesize());
	printf("ptr is %p\n",ptr);

	sleep(10);
	/* all done with 'p', so give back the 512 KB mapping */
	ret = munmap (ptr, 512 * 1024);
	if (ret)
		perror ("munmap");
}
pi@raspberrypi:~/Project/cCode/systemProgram $ ./a.out &
[1] 3566
pi@raspberrypi:~/Project/cCode/systemProgram $ page size is 1000
ptr is 0xb6ff0000

pi@raspberrypi:~/Project/cCode/systemProgram $ cat /proc/3566/maps
00010000-00011000 r-xp 00000000 b3:02 386325     /home/pi/Project/cCode/systemProgram/a.out
00020000-00021000 r--p 00000000 b3:02 386325     /home/pi/Project/cCode/systemProgram/a.out
00021000-00022000 rw-p 00001000 b3:02 386325     /home/pi/Project/cCode/systemProgram/a.out
014a8000-014c9000 rw-p 00000000 00:00 0          [heap]
b6e54000-b6f8c000 r-xp 00000000 b3:02 2038       /lib/arm-linux-gnueabihf/libc-2.28.so
b6f8c000-b6f9c000 ---p 00138000 b3:02 2038       /lib/arm-linux-gnueabihf/libc-2.28.so
b6f9c000-b6f9e000 r--p 00138000 b3:02 2038       /lib/arm-linux-gnueabihf/libc-2.28.so
b6f9e000-b6f9f000 rw-p 0013a000 b3:02 2038       /lib/arm-linux-gnueabihf/libc-2.28.so
b6f9f000-b6fa2000 rw-p 00000000 00:00 0 
b6fb1000-b6fb5000 r-xp 00000000 b3:02 12827      /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so
b6fb5000-b6fc4000 ---p 00004000 b3:02 12827      /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so
b6fc4000-b6fc5000 r--p 00003000 b3:02 12827      /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so
b6fc5000-b6fc6000 rw-p 00004000 b3:02 12827      /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so
b6fc6000-b6fe6000 r-xp 00000000 b3:02 1963       /lib/arm-linux-gnueabihf/ld-2.28.so
b6ff0000-b6ff6000 rw-p 00000000 00:00 0 
b6ff6000-b6ff7000 r--p 00020000 b3:02 1963       /lib/arm-linux-gnueabihf/ld-2.28.so
b6ff7000-b6ff8000 rw-p 00021000 b3:02 1963       /lib/arm-linux-gnueabihf/ld-2.28.so
bec5d000-bec7e000 rw-p 00000000 00:00 0          [stack]
bef82000-bef83000 r-xp 00000000 00:00 0          [sigpage]
bef83000-bef84000 r--p 00000000 00:00 0          [vvar]
bef84000-bef85000 r-xp 00000000 00:00 0          [vdso]
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

0x10000에 메모리를 요청했으나, 불가능하여 0xb6ff0000에 0x2000 byte를 얻었다. 512 KB로 다시 맵핑하여 cat/proc/PID/maps로 확인했다.

메모리 영역 크기, 주소 비교.
  • heap 영역 밖 노란열에 512KB를 할당 받았다.(anonymous memory mapping)
  • a.out이 3KB(1K+1K+1K)메모리를 사용한다. 10,000~11,000까지 한 블럭, 20,000 ~ 21,000까지 한 블록. 나머지 21,000 ~ 22,000는 offset 1K를 주고 사용한다.
  • a.out 파일 크기는 8KB 이다. 나머지는 메모리의 다른 영역에 넣는 듯 하다.
  • libc-2.28.so는 817KB 메모리를 사용한다.(0x138+0x10+0x2+0x1).
  • b5dbd000 ~ d6ef5000에서 0x138KB를 사용하고, 그 다음 주소에 libc-2.28.so 파일 offset 0x138KB만큼 주고 사용한다. 그 뒤로 같은 방식으로 사용한다.
  • 별도 설정을 하지 않을 경우, heap과 stack은 같은 크기를 갖는다??

inotify 예제

linux system programming, 283p

리눅스에 파일, 디렉토리를 감시하는 watch가 있다. 특정 경로를 설정하여 감시하여 적절한 이벤트를 작성할 수 있다.

#include <sys/inotify.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#define BUF_LEN 128 

//int inotify_init1 (int flags);
void main(){
	int fd; //file descritor
	int wd; //watch descriptor
	const char path[50]="/home/now0930/test/ttt";
	fd = inotify_init1 (0);
	if (fd == -1) {
		perror("inotify_init1");
		exit(EXIT_FAILURE);
		}

	//printf("%d", IN_MODIFY);
	wd = inotify_add_watch(fd, path, IN_OPEN);
	if (wd == -1){
		perror("add notify");
		exit(EXIT_FAILURE);
	}
	printf("file descriptor is %d\n",fd);
	printf("path is %s\n",path);
	
	//__attribute__ compiler가 제공하는 메모리 번지 정렬. 4바이트 단위로 정렬.
	//https://blog.naver.com/PostView.nhn?blogId=msyang59&logNo=220885276415
	char buf[BUF_LEN] __attribute__((aligned(4)));
	//printf("buf[0] is  0x%ld\n",&buf[0]);
	//printf("buf[1] is  0x%ld\n",&buf[1]);
	//printf("buf[2] is  0x%ld\n",&buf[2]);
	//printf("buf[3] is  0x%ld\n",&buf[3]);
	//printf("buf[4] is  0x%ld\n",&buf[4]);
	//printf("buf[5] is  0x%ld\n",&buf[5]);
	//
	//
	ssize_t len, i = 0;

	for (int j=0;j<5;j++){
		i = 0;
		/* read BUF_LEN bytes' worth of events */
		len = read (fd, buf, BUF_LEN);
		printf("char is %ld\n",buf);
		printf("len is %ld\n",len);
		//보통 이런 방식으로 사용.
		/* loop over every read event until none remain */
		while (i < len) {
			struct inotify_event *event =
			(struct inotify_event *) &buf[i];
			printf ("wd=%d mask=%d cookie=%d len=%d dir=%s\n", event->wd, event->mask, event->cookie, event->len, (event->mask & IN_ISDIR) ? "yes" : "no");
			/* if there is a name, print it */
			if (event->len)
				printf ("name=%s\n", event->name);
				/* update the index to the start of the next event */
			//printf("i is %d\n",i);
			//printf("buf address is 0x%lx\n",&buf[i]);
			if (event->mask && IN_ACCESS)
				printf("the file was read\n");
			i += sizeof (struct inotify_event) + event->len;
			}
	}


	int ret;
	ret = inotify_rm_watch(fd, wd);
	if(ret)
		perror("remove watch\n");
	
}

[21:45:06]>rm ./a.out ;gcc notify.c;./a.out 
notify.c: In function ‘main’:
notify.c:45:21: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘char *’ [-Wformat=]
   printf("char is %ld\n",buf);
                   ~~^    ~~~
                   %s
file descriptor is 3
path is /home/now0930/test/ttt
char is 140729024548928
len is 16
wd=1 mask=32 cookie=0 len=0 dir=no
the file was read
char is 140729024548928
len is 16
wd=1 mask=32 cookie=0 len=0 dir=no
the file was read