시간 제한을 포함하여 특정 크기로 증가하는 파일 (fd가 있음)을 보는 함수를 작성했습니다. kqueue()
/kevent()
을 사용하여 파일이 "확장"될 때까지 기다렸지 만 파일이 커졌다는 알림을받은 후에 파일 크기를 확인하고 원하는 크기와 비교해야합니다. 그렇게 쉬운 것 같지만 POSIX에서 안정적으로 그렇게 할 수있는 방법을 찾지 못했습니다.주어진 파일 디스크립터가 주어진 POSIX/OS X에서 파일 크기를 결정하는 신뢰할 수있는 방법
NB : 지정된 시간 동안 파일이 전혀 증가하지 않으면 시간 초과가 발생합니다. 따라서 이것은 절대 시간 초과가 아니며 시간 초과가 발생하면 파일이 생성됩니다. 나는 OS X에 있지만이 질문은 OSX와 내가 생각하는 BSD가되어야하는 "kevent()
/kqueue()
"을 가진 모든 POSIX에 대한 것이다.
- 는 kevent에게 파일 성장을위한
- 확인 크기
- 대기 설정 : 그래서 기본 알고리즘은 다음과 같은 방식으로 작동
/** * Blocks until `fd` reaches `size`. Times out if `fd` isn't extended for `timeout` * amount of time. Returns `-1` and sets `errno` to `EFBIG` should the file be bigger * than wanted. */ int fwait_file_size(int fd, off_t size, const struct timespec *restrict timeout) { int ret = -1; int kq = kqueue(); struct kevent changelist[1]; if (kq < 0) { /* errno set by kqueue */ ret = -1; goto out; } memset(changelist, 0, sizeof(changelist)); EV_SET(&changelist[0], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_EXTEND, 0, 0); if (kevent(kq, changelist, 1, NULL, 0, NULL) < 0) { /* errno set by kevent */ ret = -1; goto out; } while (true) { { /* Step 1: Check the size */ int suc_sz = evaluate_fd_size(fd, size); /* IMPLEMENTATION OF THIS IS THE QUESTION */ if (suc_sz > 0) { /* wanted size */ ret = 0; goto out; } else if (suc_sz < 0) { /* errno and return code already set */ ret = -1; goto out; } } { /* Step 2: Wait for growth */ int suc_kev = kevent(kq, NULL, 0, changelist, 1, timeout); if (0 == suc_kev) { /* That's a timeout */ errno = ETIMEDOUT; ret = -1; goto out; } else if (suc_kev > 0) { if (changelist[0].filter == EVFILT_VNODE) { if (changelist[0].fflags & NOTE_RENAME || changelist[0].fflags & NOTE_DELETE) { /* file was deleted, renamed, ... */ errno = ENOENT; ret = -1; goto out; } } } else { /* errno set by kevent */ ret = -1; goto out; } } } out: { int errno_save = errno; if (kq >= 0) { close(kq); } errno = errno_save; return ret; } }
:
은 여기 내 기능의 내 현재 버전의
파일이 원하는 크기에 도달 할 때까지 2 단계와 3 단계가 반복됩니다.
이 코드가에 대한 < 0
를 돌려 "일부 오류가 발생하거나 원하는 것보다 더 큰 파일을"하는 함수 int evaluate_fd_size(int fd, off_t wanted_size)
, == 0
을 사용하여 "충분히 아직 큰 파일", 또는 파일에 대한 > 0
이 원하는 크기에 도달했습니다.
명백하게 이것은 evaluate_fd_size
이 파일 크기를 결정하는 데 신뢰할 만 한 경우에만 작동합니다. 내 첫 번째 시도는 off_t eof_pos = lseek(fd, 0, SEEK_END)
으로 구현하고 eof_pos
과 wanted_size
을 비교하는 것입니다. 죄송합니다. lseek
(으)로 결과를 캐시하고 있습니다. 따라서 kevent
이 NOTE_EXTEND
으로 반환 되어도 파일이 커지더라도 결과는 동일 할 수 있습니다! 그런 다음 fstat
으로 변경하고 found articles that fstat
caches as well으로 변경했습니다.
내가 시도한 마지막 것은 전에 off_t eof_pos = lseek(fd, 0, SEEK_END);
전에 갑자기 일이 시작되었습니다. 그러나 : 그것은 재현하기 정말 어렵다하지만 하나의 경우를 보았다 :
- 아무것도
- 내가 때문에 성능
편집의 fsync()
싶지 않아 fsync()
정말 내 문제를 해결한다고 없다 어느 fsync()
도움이되지 않았다. NOTE_EXTEND
이벤트가 사용자 공간에 도달 한 후 파일 크기가 커질 때까지 약간의 시간이 소요되는 것 같습니다. fsync()
은 아마도 sleep()
만큼 훌륭하게 작동하기 때문에 대부분의 경우 작동합니다.
다른 말로하면 : 파일 이름을 모르기 때문에 내가 할 수없는 파일을 열거 나 닫지 않고 POSIX에서 파일 크기를 안정적으로 확인하는 방법. 또한 도움이 될 것이라는 보증을 찾을 수 없습니다.
By 덧붙여 : int new_fd = dup(fd); off_t eof_pos = lseek(new_fd, 0, SEEK_END); close(new_fd);
캐싱 문제를 해결하지 못했습니다.
편집 2 : all in one demo program도 만들었습니다. 종료하기 전에 Ok, success
을 인쇄하면 모든 것이 정상적으로 진행됩니다. 그러나 일반적으로 경쟁 조건을 나타내는 Timeout (10000000)
을 출력합니다. 트리거 된 마지막 kevent에 대한 파일 크기 확인은이 순간의 실제 파일 크기보다 작습니다. 이상하게도 ftruncate()
을 사용하여 write()
대신 파일을 생성하면 작동합니다 (테스트하려면 -DUSE_FTRUNCATE
으로 테스트 프로그램을 컴파일 할 수 있습니다).
stat를 호출하기 전에'fchown (fd, -1, -1)'을 할 수 있습니까? – cnicutar
@cnicutar 그게 도움이되지 않았다 :-( –