System/System Programming

    8. 지정한 위치 읽고 쓰기 - pread(), pwrite()

    개요 지난번 게시글에서는 lseek()를 통해 파일 오프셋을 원하는 곳으로 바꾸어서 파일 입출력을 하는 방법에 대해 다루어 보았다. 한편 리눅스에서는 lseek() 대신 읽고 쓸 파일 오프셋을 지정할 수 있는 read()와 write()와 비슷한 사촌격의 시스템 콜이 있다. 둘 모두 각각의 읽기, 쓰기 작업을 마친 후 파일 오프셋을 갱신하지 않는다. pread()와 pwrite() pread() 시스템 콜을 사용하면 읽을 오프셋을 지정할 수 있다. 1234#include #define _XOPEN_SOURCE 500 ssize_t pread(int fd, void *buf, size_t count, off_t pos);cs pread()를 사용하면 파일 디스크립터 fd에서 pos (지정된 오프셋) 에 있..

    7. 파일 탐색하기 - lseek()

    개요 파일 입출력을 좀 더 재미있게 할 수 있는 방법은 없을까? 파일 입출력을 할 때 무료함을 느낀다면, 그건 파일 입출력 오프셋 위치 때문일 것이다. 지금까지 알아보았던 방식 (write() 시스템 콜이나 O_APPEND 플래그를 사용하는 것)으로는 항상 파일의 시작이나 끝에서밖에 입출력을 하는 것에서 그쳤다. 하지만 중간에 다른 데에서부터 입출력을 하고 싶을 때도 있다. 이를 위해서 파일 탐색 방법을 알아두어야 할 것이다. lseek() 시스템 콜 이번에 배울 lseek()는 파일 디스크립터에 연결된 파일의 오프셋을 특정 값으로 지정할 수 있게 해주는 시스템 콜이다. 사실 이 기능을 제외하고는 다른 동작은 하지 않으며, 어떤 입출력도 하지 않는다. 단지 파일 오프셋만 옮길 뿐이다. 1234#inclu..

    6. 파일 닫기 - close()

    파일 닫기 지금까지 파일을 열고, 읽고 쓰는 작업을 지난 포스팅까지 알아보았다. 파일 다루기 작업을 끝내고 나면 파일을 닫아 주어야 안전하게 애플리케이션을 종료할 수 있다. close() 시스템 콜 파일을 닫는 데 사용되는 시스템 콜 close()는 파일 매핑을 끊으며, 원형은 다음과 같다. 123#include int close(int fd); cs 인자로 받은 파일 디스크립터 fd와 연관된 파일 매핑을 해제하며, 프로세스에서 파일을 떼어내는 작업을 한다. 이 과정을 거치면 파일 디스크립터는 더는 유효하지 않으며, 커널이 다음 open()이나 creat() 호출에서 다시 사용할 수 있게 된다. close()도 지금까지 다룬 대부분의 시스템 콜과 마찬가지로 성공하면 0, 실패하면 -1을 반환하고 errn..

    5. 직접 입출력

    직접 입출력 다른 운영체제들과 마찬가지로 리눅스 커널은 디바이스와 응용 프로그램 사이에 캐시, 버퍼링, 입출력 관리 등과 같은 복잡한 계층을 구현하고 있다. 성능이 중요한 응용 프로그램에서는 이런 복잡한 계층을 우회해서 직접 입출력을 관리하고 싶을 수도 있다. 독자적으로 입출력 시스템을 운영하는 것은 확실히 ... 매력적이다! 전반적인 파일 시스템 구조를 뜯어보면서 지식을 더 쌓아나가고, 더 나아가 결과물까지 구현이 된다면 좋은 포트폴리오로 남을 수 있을 것이다. 하지만 보통 들인 노력에 비해 효과가 미미하며, 사실 운영체제 수준에서 제공하는 도구는 애플리케이션에서 가능한 방법보다 훨씬 뛰어난 성능을 낸다. 물론 데이터베이스 시스템은 독자적인 캐시를 선호한다. 또한 운영체제의 개입을 가능한 최소한으로 줄..

    4. 동기식 입출력 - fsync(), fdatasync()

    개요 쓰기 작업이 지연되고 있어! 어떡하지??? 이는 큰 문제는 아니다. 쓰기 버퍼링은 오히려 에러가 아니라 성능을 향상시키는 과정이라고 할 수 있다. 흔히 '최신' 운영체제라고 하면 버퍼를 통해 지연된 쓰기 작업을 구현하고 있기 때문에 안심해도 된다. 하지만 프로그램에서 데이터가 기록되는 시점을 직접 컨트롤할 수 있다면? 이럴 때를 대비해 리눅스 커널에서는 입출력을 동기화하는 시스템 콜을 제공하고 있다. 물론 성능은 ... 비교적 희생해야 하는 면이 없지 않아 있다. fsync()와 fdatasync() POSIX에 정의된 데이터가 디스크에 기록되도록 확인할 수 있는 가장 단순한 방법은 fsync() 시스템 콜을 사용하는 것이다. 123#include int fsync(int fd);cs 인자로는 파일..

    3. 파일 쓰기 - write()

    write()로 쓰기 파일을 열고, 읽는 것까지 했다. 이번 포스팅에서는 write() 시스템 콜을 이용하여 파일에 사용자가 넣고 싶어하는 내용을 써보자. write() 시스템 콜은 파일에 데이터를 기록하기 위해 가장 기본적인 시스템 콜로, read()와 반대 개념이며 똑같이 POSIX.1에 정의되어 있다. read()의 기본 형태는 다음과 같다. 123#include ssize_t write(int fd, const void *buf, size_t count);cs 코드를 보면 알 수 있듯이 반환값은 ssize_t 형이다. (이에 대해서는 이전 포스팅에서 이미 설명을 했었다.) write() 호출은 count 바이트만큼 파일 디스크립터(fd)가 참조하는 파일의 현재 파일 위치에 시작 지점이 buf인 내..

    2. 파일 읽기 - read()

    개요 저번 포스팅에서는 open(), creat() 시스템 콜을 이용해 파일을 열어보는 것 까지 했다. 파일을 열었으니 이제 읽는 방법에 대해 알아볼 차례이다. 파일 읽기 read() 시스템 콜 가장 잘 알려진 파일 읽는 방법은 read() 시스템 콜을 사용하는 것이다. 이는 POSIX.1에 정의되어 있는 메커니즘이기도 하다. read()의 기본적인 형태는 다음과 같다. 123#include ssize_t read(int fd, void *buf, size_t len);cs read()는 해당 함수가 호출될 때마다 파일 디스크립터(이하 fd)가 참조하는 파일의 현재 file offset에서 len 바이트만큼 buf로 읽어들인다. 읽어들이는 작업에 성공하면 buf에 쓴 바이트 숫자를 반환하고, 실패하면 이..