카테고리 없음
(작성중) [C][소켓][POSIX] 다른 스레드에서 FD 관찰에 대한 고찰
2026. 4. 14. 12:45반응형
poll()은 소켓 전용이 아니라 "이벤트를 기다릴 수 있는 모든 fd" 에 사용 가능합니다. (즉, inotify fd도 pollfd 배열에 넣을 수 있다.)
inotify fd + eventfd 동시 poll 예제
소스코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/eventfd.h>
#include <sys/inotify.h>
#include <poll.h>
#include <errno.h>
#define WATCH_PATH "/tmp"
#define EVENT_BUF_SIZE (1024 * (sizeof(struct inotify_event) + 16))
typedef struct {
int inotify_fd;
int efd; // eventfd (종료 신호용)
} ThreadArgs;
// -----------------------------------------------
// Thread A: poll로 inotify_fd + eventfd 동시 감시
// -----------------------------------------------
void *watcher_thread(void *arg) {
ThreadArgs *args = (ThreadArgs *)arg;
char buf[EVENT_BUF_SIZE];
struct pollfd pfds[2];
pfds[0].fd = args->inotify_fd;
pfds[0].events = POLLIN;
pfds[1].fd = args->efd;
pfds[1].events = POLLIN;
printf("[Watcher] 시작 - \"%s\" 감시 중...\n", WATCH_PATH);
while (1) {
int ret = poll(pfds, 2, -1);
if (ret < 0) {
perror("[Watcher] poll 실패");
break;
}
// 종료 신호 확인 (우선 체크)
if (pfds[1].revents & POLLIN) {
uint64_t val;
read(args->efd, &val, sizeof(val));
printf("[Watcher] 종료 신호 수신, 스레드 종료\n");
return NULL;
}
// inotify 이벤트 확인
if (pfds[0].revents & POLLIN) {
ssize_t len = read(args->inotify_fd, buf, sizeof(buf));
if (len < 0) {
perror("[Watcher] inotify read 실패");
break;
}
// 이벤트 파싱
char *ptr = buf;
while (ptr < buf + len) {
struct inotify_event *event = (struct inotify_event *)ptr;
if (event->mask & IN_CREATE)
printf("[Watcher] 파일 생성: %s\n", event->name);
if (event->mask & IN_DELETE)
printf("[Watcher] 파일 삭제: %s\n", event->name);
if (event->mask & IN_MODIFY)
printf("[Watcher] 파일 수정: %s\n", event->name);
ptr += sizeof(struct inotify_event) + event->len;
}
}
}
return NULL;
}
// -----------------------------------------------
// Thread B: 8초 후 종료 신호 전송
// -----------------------------------------------
void *stopper_thread(void *arg) {
ThreadArgs *args = (ThreadArgs *)arg;
printf("[Stopper] 8초 후 종료 신호 전송 예정...\n");
sleep(8);
uint64_t val = 1;
write(args->efd, &val, sizeof(val));
printf("[Stopper] 종료 신호 전송 완료\n");
return NULL;
}
int main() {
// 1. inotify 초기화
int inotify_fd = inotify_init1(IN_NONBLOCK); // nonblock 권장
if (inotify_fd < 0) {
perror("inotify_init1 실패");
return 1;
}
// 2. 감시 경로 등록
int wd = inotify_add_watch(inotify_fd, WATCH_PATH,
IN_CREATE | IN_DELETE | IN_MODIFY);
if (wd < 0) {
perror("inotify_add_watch 실패");
return 1;
}
// 3. eventfd 생성
int efd = eventfd(0, 0);
// eventfd(0, EFD_CLOEXEC);는 fork()/exec() 시 다르게 처리하고 싶을 때 사용한다
if (efd < 0) {
perror("eventfd 실패");
return 1;
}
// 4. 스레드 인자 구성
ThreadArgs args = {
.inotify_fd = inotify_fd,
.efd = efd,
};
// 5. 스레드 생성
pthread_t watcher, stopper;
pthread_create(&watcher, NULL, watcher_thread, &args);
pthread_create(&stopper, NULL, stopper_thread, &args);
// 6. 메인: 테스트용 파일 생성/삭제
sleep(2);
system("touch /tmp/test_file_a.txt");
sleep(2);
system("touch /tmp/test_file_b.txt");
sleep(2);
system("rm /tmp/test_file_a.txt");
// 7. 스레드 종료 대기
pthread_join(stopper, NULL);
pthread_join(watcher, NULL);
// 8. 리소스 정리
inotify_rm_watch(inotify_fd, wd);
close(inotify_fd);
close(efd);
printf("[Main] 정상 종료\n");
return 0;
}
빌드 및 실행
gcc -o inotify_example inotify_example.c -lpthread
./inotify_example
예상 출력
[Watcher] 시작 - "/tmp" 감시 중...
[Stopper] 8초 후 종료 신호 전송 예정...
[Watcher] 파일 생성: test_file_a.txt
[Watcher] 파일 생성: test_file_b.txt
[Watcher] 파일 삭제: test_file_a.txt
[Stopper] 종료 신호 전송 완료
[Watcher] 종료 신호 수신, 스레드 종료
[Main] 정상 종료
poll()은 "커널이 이벤트를 알려줄 수 있는 fd" 라면 종류에 상관없이 배열에 혼합해서 넣을 수 있다.
따로 분리할 필요가 없다.
반응형