NAME
write - 파일 디스크립터에 쓰기
SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
DESCRIPTION
write()
는 buf
가 가리키는 버퍼에서 최대 count
바이트를 파일 디스크립터 fd
가 나타내는 파일로 기록한다.
기록한 바이트 수가 count
수보다 작을 수도 있다. 예를 들어 기반 물리 매체에 공간이 불충분하거나, RLIMIT_FSIZE
자원 제한에 걸리거나 (setrlimit(2) 참고), count
바이트보다 적게 기록한 후에 시그널 핸들러에 의해 호출이 중단된 경우에 그럴 수 있다. (pipe(7)도 참고.)
위치 탐색이 가능한 파일에선 (lseek(2)을 적용할 수 있는 파일, 예를 들어 정규 파일에선) 파일 오프셋에서 쓰기가 일어나며 실제로 기록한 바이트 수만큼 파일 오프셋이 증가한다. O_APPEND
를 써서 파일을 open(2) 했다면 먼저 파일 오프셋을 파일 끝으로 설정한 다음 쓰기를 한다. 파일 오프셋 조정과 쓰기 동작이 원자적인 한 단계로 수행된다.
POSIX에서는 write()
가 반환한 후에 이뤄진다고 입증할 수 있는 read(2)는 새 데이터를 반환하기를 요구한다. 모든 파일 시스템이 POSIX를 준수하는 것은 아니다.
POSIX.1에 따르면 count
가 SSIZE_MAX
보다 큰 경우의 결과는 구현에서 규정한다. 리눅스에서의 상한에 대해선 NOTES 참고.
RETURN VALUE
성공 시 쓴 바이트 수를 반환한다. 오류 시 -1을 반환하며 오류를 나타내도록 errno
를 설정한다.
write()
가 성공한 경우에도 count
바이트보다 적은 바이트를 이동시켰을 수 있다. 다양한 이유로 그런 불완전한 쓰기가 일어날 수 있는데, 예를 들어 요청한 바이트 전체를 기록하기엔 디스크 장치 공간이 불충분했을 수도 있고, 소켓이나 파이프 등에 대한 write()
가 일부만 이동시키고 요청한 바이트 전체를 이동시키진 못한 채 블록돼 있다가 시그널 핸들러에 의해 중단돼서일 수도 있다. 불완전한 쓰기가 발생했을 때 호출자는 남은 바이트들을 이동시키기 위해 또 다른 write()
호출을 할 수 있다. 그 호출은 바이트들을 더 이동시킬 수도 있고 (가령 이제 디스크가 가득 찼다면) 오류를 일으킬 수도 있다.
count
가 0이고 fd
가 정규 파일을 가리키는 경우에는 아래 오류들 중 하나를 탐지했을 때 write()
가 실패 상태를 반환할 수도 있다. 어떤 오류도 탐지하지 못한 경우에는, 또는 오류 탐지를 수행하지 않는 경우에는 0을 반환하며 달리 아무 영향도 끼치지 않는다. count
가 0이고 fd
가 정규 파일 아닌 파일을 가리키는 경우에는 그 결과가 명세돼 있지 않다.
ERRORS
EAGAIN
- 파일 디스크립터
fd
가 소켓 아닌 파일을 가리키고 있고 논블로킹 표시(O_NONBLOCK
)가 되어 있는데 쓰기가 블록하려 했다.O_NONBLOCK
플래그에 대한 자세한 내용은 open(2) 참고. EAGAIN
또는EWOULDBLOCK
- 파일 디스크립터
fd
가 소켓을 가리키고 있고 논블로킹 표시(O_NONBLOCK
)가 되어 있는데 쓰기가 블록하려 했다. POSIX.1-2001에서는 이 경우 어느 쪽이 반환되는 것도 허용하며 두 상수가 같은 값이어야 한다고 요구하지 않는다. 따라서 이식 가능한 응용에서는 두 가능성을 모두 확인하는 게 좋다. EBADF
fd
가 유효한 파일 디스크립터가 아니거나 쓰기용으로 열려 있지 않다.EDESTADDRREQ
fd
가 데이터그램 소켓을 가리키는데connect(2)
로 상대의 주소를 설정하지 않았다.EDQUOT
fd
가 가리키는 파일을 담은 파일 시스템에서 사용자의 디스크 블록 쿼터가 고갈되었다.EFAULT
buf
가 접근 가능한 주소 공간 밖에 있다.EFBIG
- 구현체에서 정의한 최대 파일 크기나 프로세스의 파일 크기 제한을 초과하는 쓰기 시도가 이뤄졌다. 또는 허용하는 최대 오프셋 너머 위치에 쓰기 시도가 이뤄졌다.
EINTR
- 데이터를 쓰기 전에 시그널에 의해 호출이 중단되었다. signal(7) 참고.
EINVAL
fd
가 쓰기에 적합하지 않은 객체에 연결돼 있다. 또는O_DIRECT
플래그를 써서 파일을 열었는데buf
에 지정한 주소나count
에 지정한 값, 또는 파일 오프셋이 올바로 정렬돼 있지 않다.EIO
- 아이노드를 변경하는 도중에 저수준 I/O 오류가 발생했다. 이 오류는 앞선
write()
에서 쓴 데이터의 write-back과 관련돼 있을 수도 있으며, 그write()
동작이 같은 파일에 대한 다른 파일 디스크립터에서 이뤄졌을 수도 있다. 리눅스 4.13부터는 write-back에 의한 오류가 후속write()
요청에 의해 보고될 수도 있으며, 후속 fsync(2)에 의해 (write()
로 보고됐는지와 상관없이) 보고된다는 약속이 있다. 네트워크 파일 시스템에서EIO
가 발생할 수 있는 또 다른 경우는 파일 디스크립터에서 권고형 락을 가져갔는데 그 락이 사라졌을 때다. 자세한 내용은 fcntl(2)의 락 상실 절 참고. ENOSPC
fd
가 가리키는 파일을 담은 장치에 데이터를 넣을 여유 공간이 없다.EPERM
- 파일 봉인 때문에 동작이 막혔다. fcntl(2) 참고.
EPIPE
fd
가 파이프나 소켓에 연결돼 있는데 읽는 쪽이 닫혀 있다. 이 경우엔 또 쓰기를 하는 프로세스가SIGPIPE
시그널을 받게 된다. (따라서 프로그램이 그 시그널을 잡거나 막거나 무시하는 경우에만 쓰기 반환 값을 보게 된다.)
fd
에 연결된 객체에 따라서 다른 오류가 발생할 수도 있다.
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
SVr4에선 데이터를 쓰기 전뿐 아니라 언제든 쓰기가 중단되어 EINTR
를 반환할 수 있다.
NOTES
size_t
와 ssize_t
타입은 각각 부호가 없거나 있는 정수 데이터 타입이며 POSIX.1에 명세돼 있다.
write()
가 성공적으로 반환하더라도 데이터가 디스크로 확실히 들어갔다는 어떤 보장도 없다. NFS를 포함한 어떤 파일 시스템들에선 데이터를 저장할 공간을 성공적으로 확보했다는 보장도 해 주지 않는다. 그 경우 어떤 오류가 이후의 write()
나 fsync(2), 심지어 close(2)까지 연기될 수도 있다. 안심할 수 있는 유일한 방법은 데이터를 모두 쓴 다음에 fsync(2)를 호출하는 것이다.
한 바이트도 기록하지 못하고 시그널 핸들러에 의해 write()
가 중단된 경우에는 호출이 EINTR
오류로 실패한다. 적어도 한 바이트는 기록한 다음에 중단된 경우에는 호출이 성공하며 기록한 바이트 수를 반환한다.
리눅스에서 write()
는 (그리고 유사한 시스템 호출들은) 최대 0x7ffff000 (2,147,479,552) 바이트까지 이동시키며 실제 이동된 바이트 수를 반환한다. (32비트 시스템과 64비트 시스템 모두에서 그렇다.)
직접 I/O를 이용하는 write()
수행 중의 오류 반환 값이 쓰기 전체가 실패했다는 뜻은 아니다. 일부 데이터가 기록되었을 수도 있으므로 write()
를 시도한 그 파일 오프셋의 데이터는 무결성이 깨져 있다고 보아야 한다.
BUGS
POSIX.1-2008/SUSv4의 XSI 2.9.7절("Thread Interactions with Regular File Operations")에 따르면:
다음 함수들 모두는 정규 파일이나 심볼릭 링크에 동작할 때 POSIX.1-2008에 명세된 효력들에 있어서 서로에게 원자적이다.
이어서 나오는 API들 중에는 write()
와 writev(2)가 있다. 그리고 스레드 (및 프로세스) 간에 원자적이어야 하는 효력들 중에는 파일 오프셋 갱신이 있다. 하지만 리눅스 버전 3.14 전에선 그렇지 않았다. 열린 파일 기술 항목(open(2) 참고)을 공유하는 두 프로세스가 동시에 write()
를 (또는 writev(2)를) 수행하면 그 I/O 동작들이 파일 오프셋 갱신 측면에서 원자적이지 않아서 두 프로세스가 출력한 데이터 블록들이 겹칠 수도 있었다. 리눅스 3.14에서 이 문제가 고쳐졌다.
SEE ALSO
close(2), fcntl(2), fsync(2), ioctl(2), lseek(2), open(2), pwrite(2), read(2), select(2), writev(2), fwrite(3)
2021-03-22