좋은 시스템 프로그래밍 문서 소개
http://people.redhat.com/drepper/defprogramming.pdf
늘 헛소리만 쓰더가 첨으로 정보가 될만한 글을 써봅니다.
여러 프로세스가 동일한 파일 (로그 파일같은..)에 데이터를 쓰고 있었습니다.
그런데 한 놈이 파일을 지우거나 이름을 바꾸면 어떻게 될까요?
이런 의문에서 시작되서 위의 문서를 읽고 개과천선을 했습니다.
제 시나리오는 이렇습니다.
자식 프로세스가 파일을 열고 락을 걸고, 데이터를 쓰고, 파일의 이름을 바꿉니다.
부모 프로세스는 파일을 열고 락을 대기하지요.
부모 프로세스가 락을 얻은 다음에는, 파일의 크기가 0으로 바뀐 것을 알았으면 좋겠습니다.
아래 소스를 만들어서 실행해봤습니다.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <wait.h> int file_lock(int fd) { struct flock lock; int rc; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; lock.l_type = F_WRLCK; if ((rc = fcntl(fd, F_SETLKW, &lock)) != 0) { return errno; } return rc; } int file_unlock(int fd) { struct flock lock; int rc; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; lock.l_type = F_UNLCK; if ((rc = fcntl(fd, F_SETLKW, &lock)) != 0) { return errno; } return rc; } #define ORIG_FILE_MSG "<CHILD>THIS IS ORIG FILE\n" #define NEW_FILE_MSG "<CHILD>THIS IS NEW FILE\n" int rename_process(void) { int orig_fd; orig_fd = open("file.txt",O_WRONLY|O_CREAT, S_IRWXU); if (orig_fd < 0) { perror("OPEN FAIL"); return orig_fd; } printf("CHILD OPEN file.txt\n"); if (file_lock(orig_fd) < 0) { perror("LOCK FAIL"); close(orig_fd); } printf("CHILD<%d> lock file\n", getpid()); if (write(orig_fd, ORIG_FILE_MSG, strlen(ORIG_FILE_MSG)) <= 0) { perror("WRITE ORIG-MSG ERROR"); } printf("CHILD RENAME FILE!!--->LOCK IS OK??\n"); if (rename("file.txt", "file.bak") < 0) { perror("RENAME FAIL"); close(orig_fd); } /* File descriptor is the same, and this message is written at file.bak */ if (write(orig_fd, NEW_FILE_MSG, strlen(NEW_FILE_MSG)) <= 0) { perror("WRITE NEW-MSG ERROR"); } /* make parent try lock file */ sleep(10); printf("CHILD<%d> unlock file\n", getpid()); if (file_unlock(orig_fd) < 0) { perror("UNLOCK FAIL"); close(orig_fd); } /* wake other processes */ sleep(3); close(orig_fd); return 0; } int main(void) { pid_t childp; childp = fork(); if (childp == 0) { printf("CHILD<%d> START\n", getpid()); rename_process(); printf("CHILD<%d> END\n", getpid()); } else if (childp > 0) { int orig_fd; char readbuf[128]; int waitstat; struct stat orig_stat; printf("PARENT<%d> START\n", getpid()); orig_fd = open("file.txt",O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); if (orig_fd < 0) { perror("OPEN FAIL"); return orig_fd; } printf("PARENT OPEN file.txt\n"); /* child lock&rename file first! */ sleep(3); if (file_lock(orig_fd) < 0) { perror("PARENT LOCK FAIL"); close(orig_fd); } printf("PARENT<%d> lock file\n", getpid()); if (stat("file.txt", &orig_stat) == 0) { printf("PARENT READ FILE SIZE of file.txt: %d\n", orig_stat.st_size); } else { perror("STAT FAIL"); } if (fstat(orig_fd, &orig_stat) == 0) { printf("PARENT READ FILE SIZE of file.txt: %d\n", orig_stat.st_size); } else { perror("STAT FAIL"); } if (read(orig_fd, readbuf, 127) > 0) { printf("PARENT READ <%s>\n", readbuf); } else { printf("PARENT CANNOT READ\n"); } if (write(orig_fd, "HELLO", 5) <= 0) { perror("WRITE ORIG-MSG ERROR"); } /* File descriptor is the same, and this message is written at file.bak */ if (write(orig_fd, "BYE", 3) <= 0) { perror("WRITE NEW-MSG ERROR"); } if (read(orig_fd, readbuf, 127) > 0) { printf("PARENT READ <%s>\n", readbuf); } else { printf("PARENT CANNOT READ\n"); } printf("PARENT<%d> unlock file\n", getpid()); if (file_unlock(orig_fd) < 0) { perror("UNLOCK FAIL"); close(orig_fd); } close(orig_fd); printf("PARENT<%d> END\n", getpid()); wait(&waitstat); } else { perror("WARNING! fork failed\n"); } return 0; }
파일 이름이 바뀌면 락도 따라 바뀌고 원래 파일의 락을 기다리던 프로세스들이 락을 얻게 됩니다.
이것은 어떻게보면 당연한것 같습니다.
그런데 부모 프로세스가 락에서 깨어난 후이상하게 행동합니다.
파일에 데이터를 쓰긴 하지만 읽지는 못하고, 파일에 대한 정보도 얻질 못합니다.
아래는 실행 결과를 스크립한 것입니다.
CHILD<20189> START CHILD OPEN file.txt CHILD<20189> lock file CHILD RENAME FILE!!--->LOCK IS OK?? ===> 자식 프로세스가 파일 이름 변경 PARENT<20188> START PARENT OPEN file.txt PARENT<20188> lock file ==============> 자식 프로세스가 파일 락을 안풀었어도 이름이 바꼈으므로 락 가능 STAT FAIL: No such file or directory========> stat콜은 실패함 PARENT READ FILE SIZE of file.txt: 49 ======> fstat 콜은 성공?? PARENT CANNOT READ PARENT CANNOT READ PARENT<20188> unlock file PARENT<20188> END CHILD<20189> unlock file =============> 락 걸린 파일의 이름이 바뀌면 락이 깨짐!! CHILD<20189> END gio-desk:rename$ cat file.txt HELLOBYE =========================> 부모 프로세스는 데이터를 정상적으로 쓰지만 읽지 못함 gio-desk:rename$ cat file.bak <CHILD>THIS IS ORIG FILE =============> 자식 프로세스는 데이터를 정상적으로 씀 <CHILD>THIS IS NEW FILE gio-desk:rename$
첨부한 문서에 따르면 파일의 이름을 바꾸거나 파일을 지웠다 하더라도 다른 프로세스가 동일한 파일을 열고 있었다면
해당 파일의 정보가 모두 사라지지 않는다고 합니다.
따라서 파일에 접근할 때, 내가 원했던 파일이 맞는지 - 누가 다른 파일로 바꿔치기를 했거나 등등.. - 을
확인해야한다고 하네요.
결론은
1. 프로세스간에 공유되는 파일은 지우거나 이름을 바꾸지 말자.
2. 파일같은 자원에 접근하는 프로세스는 하나만 만들어서 자원 접근이 시리얼하게 되도록 설계하자.
3. 좋은 문서를 많이 찾아보자.
댓글
좋은 자료 감사
좋은 자료 감사 ^^
댓글 달기