sendfile 함수는 두개의 파일 디스크립터 사이에서 데이터를 교환하는 함수이다. in_fd와 out_fd가 있는데 두개의 fd 모두 소켓을 사용할 수 있다고 한다. 그러나 리눅스 맨 페이지에서는 아직 in_fd에는 소켓을 사용할 수 없다고 되어 있다. 이 함수는 커널 레벨에서 구현되기 때문에 사용자 공간으로 데이터를 복사할 필요가 없어서 파일 전송에 불필요한 오버헤드를 줄여준다.
비록 sendfile 함수가 POSIX 표준 문서에는 나오지 않지만 리눅스. HP-UX, FreeBSD, Solaris에서 모두 구현되어 있다. 그러나 프로토타입이 다르기 때문에 멀티플랫폼을 지원하는 프로그램에서는 사용에 주의를 요한다.
그리고 Pure-FTPd에서는 sendfile 함수가 구현되지 않은 시스템에서도 read/write/lseek 함수 조합을 사용하지 않고 mmap을 사용해서 대상 파일을 메모리에 매핑시키고 write 함수를 사용해서 메모리에서 내용을 읽어서 전송하도록 되어있다. mmap에 대한 간단한 설명은 여기를 참조한다. 일반적으로 mmap이 read/write/lseek보다 성능이 좋다고 한다.
그런데 한가지 궁금한 점이 있는데.. 대상 파일의 사이즈가 엄청 커서 이 파일을 매핑시키는데 필요한 메모리가 모자른 경우 어떻게 되나? mmap은 한번에 파일 전체를 메모리에 매핑시키는것이 아니라 작은 부분으로 나눠서 하도록 구현되어 있나?
- 추가 -
http://fscked.org/writings/SHM/shm-2.html 내용 중,
-추가-
http://kldp.org/node/50774 에서 cinsk님의 답변 중,
실제 써 넣는 내용과 파일을 항상 동기화하는 것이 목적이라면 꼭 mmap()을 쓸 필요가 없습니다. 원하는 파일에 fsync()를 불러주면 됩니다.
mmap()을 잘못썼을 때 흔히 SIGSEGV와 SIGBUS가 발생합니다. 가장 흔한 예는 read only로 mapping한 memory등, 접근이 불가능한 곳에 access했을 경우 SIGSEGV, 현재 파일의 내용과 범위가 맞지 않는 곳을 접근하려 했을 때 SIGBUS가 발생합니다. 즉, 파일 크기가 0인데, 쓰려한다면 대개 SIGBUS가 발생합니다.
배열의 범위가 벗어나는 것을 잡아주는 debugging library인 Electric Fence(efence)는 바로 read-only로 mmap()했을 때 SIGSEGV가 발생하는 것에 힌트를 얻었습니다.
또한 실제 파일 크기보다 큰 공간을 mmap()으로 할당하면 (예: 파일 크기는 100byte이고, mmap에서 1024 byte를 할당한 경우), 나머지 1024 - 100 byte의 위치에는 0으로 채워지고, 이 곳에 쓰는 데이터는 나중에 무시됩니다. 즉 파일의 내용에 반영되지 않습니다. 앞에서 mmap()으로 파일의 크기를 변경하는 연산을 수행할 수 없다고 말한 것을 기억하기 바랍니다.
한가지 더, mmap()의 두번째 인자에 지정하는 길이는 보통 system page size의 배수가 되어야 합니다. 주어진 예제에서 "sizeof(int)*100"의 크기로 했는데, 바람직하지 않습니다. mmap()이 에러를 내고 errno를 EINVAL로 setting할 확률이 높습니다. 따라서 이 크기는 항상 page size의 배수가 되도록 하기 바랍니다. 앞에서도 말했듯이, file size보다 같거나 크며 page size의 배수가 되도록 하고, 이 때 남은 공간에는 0이 채워지고, 여기에 쓴 내용은 무시됩니다.
System의 page size는 sysconf(3)을 써서 얻을 수 있습니다.

comments
comments rss (+댓글 쓰러가기)