10. System-Level IO {CSAPP}
- 0015.1 CSAPP Third Edition Bryant, Randal E. O'Hallaron, David. 💻
- 07. Input Output {SP} | file descriptor, standard io,
read
,write
,open
,close
등에 대한 내용. - 11. memory mapping and Copy-on-write (COW) {SP} |
fork
와execve
, shared library를 여러 프로세스가 공유하는 기법인 Copy on Write 기법, 대망의mmap
- 11. Network Programming {CSAPP} 챕터에서
Tiny
웹 서버를 만들 때 필요한RIO
패키지가 이 챕터에 들어있음.
README#
이 파일은 사실 11. Network Programming {CSAPP}, 웹 서버 만드는 데 필요한 RIO
패키지 때문에 넘어왔다. 파일 디스크립터와 입력출력에 대한 내용을 빠르게 짚고 바로 RIO
패키지 구현체로 넘어갈 것. 대충 봤는데 버퍼가 없어 빠르다고?
유닉스 세상에서 모든 것은 파일이다.
대학시절 교수님이 귀에 딱지가 앉도록 말씀하셨다. 그래서 파일을 어떻게 다루는지, file descriptor가 무엇인지, 파일 메타데이터를 어떻게 쓰는지 등등에 대한 내용을 0015.2 Systems Programming {ssu2021-1st} 🐼 수업시간에 들었다.
mmap
은 유닉스 세상에서 스위스 아미 나이프이다.
자세한 건 11. memory mapping and Copy-on-write (COW) {SP}에서 확인바람.
10.5. Robust Reading and Writing with the RIO
Package#
두 가지 기능을 제공한다.
-
Unbuffered Input & output: 애플리케이션 수준의 버퍼 없이 곧장 fd를 사용해 파일을 읽거나 쓸 수 있다.
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
- Returns: number of bytes transferred if OK, 0 on EOF (rio_readn only), −1 on error
-
Buffered Input: thread safe한 버퍼기반 입력을 통해 오버헤드를 줄인다.
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
- Returns: number of bytes read if OK, 0 on EOF, −1 on error
void rio_readinitb(rio_t *rp, int fd);
: rio 객체 초기화, file descriptor 필요.
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 *
에 파일 메타데이터가 담긴다.
- Access permission bits defined in sys stat.h
- file types
S_ISREG(m)
: Is this a regular file?S_ISDIR(m)
: Is this a directory?S_ISSOCK(m)
: Is this a network socket?
/* 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 numberchar 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>
, <
가 있다. 각각
lhs > rhs
:lhs
의 stdout 출력 결과를rhs
파일로 리디렉션 하시오.lhs 2> rhs
:lhs
의 stderr 출력 결과를rhs
파일로 리디렉션 하시오.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
가 열려있다면 자동으로 닫는다.