지난번에 read() 리눅스 시스템 콜을 이용하여 파일에 데이터를 써보았다면,
이번에는 리눅스 시스템 콜 중 write()를 이용하여 파일에 데이터를 써보도록 하겠습니다.
write()는 보자마자 아시듯이 read()의 반대 개념입니다.
read()와 동일하게 파일디스크립터를 이용하여 참조한 파일에 데이터를 씁니다.
먼저, write() 시스템 콜의 원형과 인자를 확인해 봅시다.
#include <unistd.h> ssize_t write (int fd, void *buf, size_t len) |
||
입력 인자 |
int fd | open() 시스템 콜로 열린 파일을 가리키는 파일 지정 번호 |
void* buf |
파일에 쓸 데이터를 저장하고 있는 메모리 공간 |
|
len | 파일에 쓸 데이터의 길이 |
|
반환값 |
ssize_t | 파일 쓰기 성공 : write된 바이트의 수 파일 쓰기 실패 : -1 |
그럼 기본적인 사용법부터 확인해 봅시다.
아래 예제는 파일을 열어 그 파일에 데이터를 쓰는 것이다.
위와 같이 예제를 작성하였다.
일단 buf란 char형 배열에 입력할 데이터를 저장하고, fd를 이용하여 파일에 데이터의 크기만큼 쓴다.
그럼 아래와 같은 결과를 볼 수 있다.
파일에 새로쓰는 것이 아닌, 파일에 덧붙이고 싶을 때도 있다. 그럴 땐 fd를 덧붙이기 모드로 열면된다.
여러가지 설정을 open()에 입력하는 것을 보았을 것이다. 그때처럼 open()시스템 콜의 인자로 O_APPEND를 붙여주면 현재 파일의 오프셋이 아닌 파일의 끝에 덧붙여서 작성한다.
덧붙이기 모드는 몇몇 문제를 방지할 수 있는데요, 예를 들어 생각해봅시다.
하나의 파일에 두가지 프로세스가 쓰기 작업을 하고 있습니다. 이 때 파일의 오프셋은 어떻게 될까?? 첫 프로세스가 파일 끝에 데이터를 쓴 다음에 두 번째 프로세스가 동일한 작업을 하면?? 첫 번째 프로세스의 파일 오프셋은 더 이상 파일의 끝을 가르키지 않게 됩니다. 즉, 첫 번째 프로세스는 두 번째 프로세스가 작성한 데이터의 길이 만큼을 더한 오프셋을 가지지 못하게 됩니다. 그 경우 서로 경쟁하게 되므로 동기화 작업이 필요해 진다. 동기화에 대해서는 추후 설명하겠습니다.
덧붙이기 모드는 항상 파일의 마지막에 오프셋이 위치하도록 설정하므로 이런 쓰기 작업의 문제 발생을
막을 수 있습니다.
write() 시스템 콜은 read()와 다르게 EOF 조건이 없습니다. 일반적인 파일에 대해 write()는 에러가 발생하지 않을 경우 요청받은 전체 쓰기 작업 수행을 보장 받을 수 있습니다.
자, 이제 좀 더 깊숙히 생각해봅시다.
우리가 예제를 실행시켜보면 실패할 때가 많지 않습니다. 그 말은 디스크에 데이터가 제대로 써졌다는 말이 되는데요, write() 시스템 콜은 항상 데이터를 디스크에 쓰는 것을 보장해 줄까요??
그렇지 않습니다.
write() 시스템 콜은 사용자 영역에서 커널로 데이터를 전송하기 위해 커널 버퍼에 데이터 복사된 상태까지만 보장을 합니다. 즉, 그 다음에 디스크에 데이터가 쓴다는 것은 보장하지 않는다는 것 입니다.
write() 시스템 콜을 호출하면 커널은 몇 가지 상황만 점검한 후 데이터를 커널 버퍼에 복사해 놓습니다. 그 뒤 백그라운드에서 디스크에 쓰는 작업을 하지요, 즉, 파일 쓰기가 제대로 됫다고 반환하는 것은 디스크에 써진 것이 아닌 커널 버퍼에 데이터 전달을 잘 했다는 의미가 되는 것입니다.아니면 디스크에 문제가 있다면? 디스크에 썻다고 반환된 값으로 확인하겠지만 제대로 써지지 않았을 것입니다.
이러한 상황이 가끔은 문제가 될 수 있겠죠? 사용자는 분명 데이터 쓰기가 완료됫다고 생각할 지 몰라도 가끔은 데이터가 디스크에 써지지 않은 채 버퍼에 그대로 일 수 있습니다. 만약 데이터가 디스크에 저장되지 않은 상황에서 컴퓨터가 꺼진다면? 데이터는 사라지고 없을 것 입니다.
이런 문제를 방지하기 위해 리눅스 커널은 많은 대책을 세워놨습니다. 제대로 데이터를 기록하기 위해 파일 버퍼의 쓰기 저장 기능을 강제로 켜거나 모든 쓰기를 동기식으로 만드는 방식 등 여러 방식이 있습니다.
이런 방식은 다음 동기식 입출력에서 한번 알아보도록 하겠습니다.
'Linux > System Programming' 카테고리의 다른 글
[파일입출력] 5. lseek()으로 파일 탐색하기 (0) | 2015.02.10 |
---|---|
[파일입출력] 4. 동기식 입출력 (1) | 2015.02.04 |
[파일입출력] 2. read()로 파일 읽기 (2) | 2015.02.01 |
[파일입출력] 1. 파일열기 (0) | 2015.01.18 |
fork (0) | 2013.05.01 |