Skip to content

10. System-Level IO {CSAPP}

README#

이 파일은 사실 11. Network Programming {CSAPP}, 웹 서버 만드는 데 필요한 RIO 패키지 때문에 넘어왔다. 파일 디스크립터와 입력출력에 대한 내용을 빠르게 짚고 바로 RIO 패키지 구현체로 넘어갈 것. 대충 봤는데 버퍼가 없어 빠르다고?

유닉스 세상에서 모든 것은 파일이다.

대학시절 교수님이 귀에 딱지가 앉도록 말씀하셨다. 그래서 파일을 어떻게 다루는지, file descriptor가 무엇인지, 파일 메타데이터를 어떻게 쓰는지 등등에 대한 내용을 0016 Systems Programming {ssu2021-1st} 🐼 수업시간에 들었다.

mmap은 유닉스 세상에서 스위스 아미 나이프이다.

자세한 건 11. memory mapping and Copy-on-write (COW) {SP}에서 확인바람.

스크린샷 2023-09-14 오후 9.19.57.png

10.5. Robust Reading and Writing with the RIO Package#

두 가지 기능을 제공한다.

  1. Unbuffered Input & output: 애플리케이션 수준의 버퍼 없이 곧장 fd를 사용해 파일을 읽거나 쓸 수 있다.

    1. ssize_t rio_readn(int fd, void *usrbuf, size_t n);
    2. ssize_t rio_writen(int fd, void *usrbuf, size_t n);
    3. Returns: number of bytes transferred if OK, 0 on EOF (rio_readn only), −1 on error
  2. Buffered Input: thread safe한 버퍼기반 입력을 통해 오버헤드를 줄인다.

    1. ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
    2. ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
    3. Returns: number of bytes read if OK, 0 on EOF, −1 on error

void rio_readinitb(rio_t *rp, int fd);: rio 객체 초기화, file descriptor 필요.

read(2)

10.6. Readig File Metadata#

stat(2) 참조

#include <unistd.h> 
#include <sys/stat.h> 
int stat(const char *filename, struct stat *buf); 
int fstat(int fd, struct stat *buf); 
// Returns: 0 if OK, −1 on error

struct stat *에 파일 메타데이터가 담긴다.

/* Metadata returned by the stat and fstat functions */ 
struct stat { 
    dev_t st_dev; /* Device */ 
    ino_t st_ino; /* inode */ 
    mode_t st_mode; /* Protection and file type */ 
    nlink_t st_nlink; /* Number of hard links */ 
    uid_t st_uid; /* User ID of owner */ 
    gid_t st_gid; /* Group ID of owner */ 
    dev_t st_rdev; /* Device type (if inode device) */ 
    off_t st_size; /* Total size, in bytes */ 
    unsigned long st_blksize; /* Block size for filesystem I/O */ 
    unsigned long st_blocks; /* Number of blocks allocated */ 
    time_t st_atime; /* Time of last access */ 
    time_t st_mtime; /* Time of last modification */ 
    time_t st_ctime; /* Time of last change */
};

[!question] 그런데, 파일 메타데이터는 사용자가 임의대로 추가할 수 있는거 아니었음?

10.7. Read Directory Contents#

#include <sys/types.h> 
#include <dirent.h> 
DIR *opendir(const char *name);
// Returns: pointer to handle if OK, NULL on error

directory stream의 첫번째 주소를 가리킨다. 해당 디렉토리 원소가 고갈될 때까지 readdir을 호출하면 다음 struct dirent * 포인터를 얻을 수 있다.

#include <dirent.h> 
struct dirent *readdir(DIR *dirp); 
// Returns: pointer to next directory entry if OK, NULL if no more entries or error
  • struct dirent
    • ino_t d_ino: inode number
    • char d_nme[256]: filename

DIR * 객체를 닫을 경우 closedir을 호출하면 된다.

10.8. Sharing Files#

  • Descriptor table: file descriptor 목록, 프로세스 독립적임.
  • File table: 모든 프로세스가 공유하는 열린 파일 목록. reference counting을 한다. 0이 될 때까지 file descriptor를 닫으면 비로소 삭제된다.
  • v-node table: 모든 프로세스가 공유하는 열린 파일 목록. File Table과 다른 점은 얘는 파일 메타데이터인 struct stat을 가지고 있다.

[!question]- 한 프로세스가 열 수 있는 파일의 개수는 file descriptor의 최대치겠네?
stack exchange 답변에 따르면, 커널이 열 수 있는 최대 파일의 개수를 /proc/sys/fs/file-max에 저장해 놓고 있다고 한다. 내 컴퓨터는 2459860개가 되는데, 정작 프로세스 당 file descriptor의 개수는 딱히 정해져 있지 않은게, file descriptor는 같은 파일(v-node)을 가리킬 수도 있고, 같은 파일 테이블(file table)을 가리킬 수도 있으며, 아예 가리키지 않을 수도 있어서란다.

I/O Redirection#

아주 중요한 개념인 dup2에 대해서 나온다. 리눅스 셸에서는 >, 2>, <가 있다. 각각

  1. lhs > rhs: lhs의 stdout 출력 결과를 rhs파일로 리디렉션 하시오.
  2. lhs 2> rhs: lhs의 stderr 출력 결과를 rhs 파일로 리디렉션 하시오.
  3. lhs < rhs: rhs파일을 lhs의 stdin으로 리디렉션 하시오.
#include <unistd.h> 
int dup2(int oldfd, int newfd); 
// Returns: nonnegative descriptor if OK, −1 on error

descriptor table의 newfd번째 인덱스의 값을 oldfd의 값으로 덮어쓴다. 만약 newfd가 열려있다면 자동으로 닫는다.