Try   HackMD

CS:APP Section 10 System-Level I/O

contributed by < YLowy >

10.1 Unix I/O

  1. file descriptor
    標準輸入輸出錯誤(0,1,2)
    <unistd.h>
STDIN_FILENO STDOUT_FILENO STDERR_FILENO
  1. file position
    每個 file descriptor 存在 file position (offset) ,由 kernel 管理
    seek 管理 file position

  2. Read & Write
    Read:
    從 file position 後讀取 k 個 bytes 或者讀取到 EOF , 更新 file position
    Write:
    寫入 k 個 bytes, 更新 file position

  3. close
    close fd 後 fd 會被回收並且釋放其資源

10.2 open/close files

int open (char *filename, int flags, mode_t mode)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Practice Problem 10.1

10.3 read/write

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

read write 的 short count 原因

  1. EOF on read
  2. Read text line from a terminal
  3. r/w socket
    寫伺服器要注意

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Practice Problem 10.2

size_t vs ssize_t 差別

size_t 為無號數而 ssize_t 為有號數
負數率為了判斷 w/r 有無成功
也因為如此一次 w/r 最大的資料量非為 4GB 而為 2GB

10.4 Robust Reading and Writing with RIO Packet

unbuffered IO : no application-level buffer, 適合用在 網路 socket

buffered IO : efficiently read data line

----- -rio_t -------
rio_read

rio_readlineb

rio_readnb
----- no rio_t ------
rio_readn

rio_writen

若 rio 建立在 heap 中或者為 static 要考慮 multi-thread 的 安全性。

10.5 Reading File Metadata

information about a file.

int stat(const char *filename, struct stat *buf)
int fstat(int fd, struct stat *buf)

狀態

struct stat * status;

給定一文件透過 gdb 觀察 :

(gdb) p stat1
$1 = {st_dev = 2053, st_ino = 2104329, st_nlink = 1, st_mode = 33204, 
  st_uid = 1000, st_gid = 1000, __pad0 = 0, st_rdev = 0, st_size = 27, 
  st_blksize = 4096, st_blocks = 8, st_atim = {tv_sec = 1613892772, 
    tv_nsec = 447094144}, st_mtim = {tv_sec = 1613726300, 
    tv_nsec = 252177149}, st_ctim = {tv_sec = 1613726300, 
    tv_nsec = 252177149}, __glibc_reserved = {0, 0, 0}}

再透過 Mask Marco 判斷

S_ISREG(stat1.st_mode)

10.6 Sharing Files

Kernel 使用 3 種資料結構實現 open file

  1. Descriptor table
    每一個 process 存在一張 Descriptor table 紀錄 fd 與 file table

  2. File Table
    所有 process 共享 file table ,而 file table 紀錄 set of file

存在 pointer to an entry in the v-node table
file position
refcnt 有多少數量指向該 file ,可以說是有多少 process open 該 file, close 就 -1,直到其變為 0 Kernel 才會回收該空間

  1. v-node table
    所有 process 共享
    metadata 所提供的 stat struct 也是紀錄在這

下圖為其最基礎概念

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

下圖考慮一個 process 開啟同一份檔案(因為每個 open file struct 存在各自的 file position 所以可以讀到同檔案不同位置的內容)
不同 entry of open file table 卻會指向相同的 entry of v-node table
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

下圖考慮 fork 之後 不同的 process 對於 fd 之關係(Decriptor table copied)

參考 fcntl()

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

10.7 I/O Redirection

shell 的 IO 功能
簡單而言

$ ls -al > foo.txt
$ cat foo.txt
total 136
drwxrwxr-x 2 yyc yyc  4096  二  21 16:35 .
drwxrwxr-x 4 yyc yyc  4096  二  21 15:24 ..
-rw-rw-r-- 1 yyc yyc     0  二  21 16:36 foo.txt
-rw-rw-r-- 1 yyc yyc     0  二  21 16:35 Foo.txt
-rw-rw-r-- 1 yyc yyc     0  二  21 16:36 ls
-rwxrwxr-x 1 yyc yyc 20384  二  21 15:46 metadata
-rwxrwxr-x 1 yyc yyc 16968  二  21 16:32 pp3
-rwxrwxr-x 1 yyc yyc 16784  二  19 15:20 systemi01
-rwxrwxr-x 1 yyc yyc 16880  二  19 15:30 systemi02
-rwxrwxr-x 1 yyc yyc 22280  二  19 18:28 systemio
-rw-rw-r-- 1 yyc yyc   470  二  19 15:20 systemio1.c
-rw-rw-r-- 1 yyc yyc   243  二  19 15:30 systemio2.c
-rw-rw-r-- 1 yyc yyc   334  二  21 15:45 systemio3_Metadata.c
-rw-rw-r-- 1 yyc yyc  3159  二  19 18:28 systemio.c
-rw-rw-r-- 1 yyc yyc   338  二  21 16:21 systemio_pp2.c
-rw-rw-r-- 1 yyc yyc   368  二  21 16:32 systemio_pp3.c

如何做到的?

-> dup2

int dup2(int old fd, int newfd);

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

10.8 Standard I/O

lib for fread, fwrite, fclose, fopen , printf, scanf also.

stdio.h 將 open file 視作 stream,也就是一個 指向 FILE 的 pointer。

stream 可想成 fd + buffer(原因如同 rio,避免過多次的 system call)

10.9 Putting it together

絕大多數情況可以使用 stdio.h 提供 API -> 全雙工介面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

網路通訊(socket)則不行,由於以下兩限制

  1. input functions following output function
  2. output function following input function

在socket 中無法使用 lseek()
在限制一中,可以透過flush buffer 再input
但是在限制二中,只能透過兩個 fd 實現(一個寫一個讀)但是也延伸其他問題,此必須同時close(fd) 以避免 memmory leak。
double close fd may be desaster in thread program such as server.

socket 可以使用 rio 進行讀寫

10.10 Summary

Practice Problem

Practice Problem 10.1

#include <stdio.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> int main(){ int fd1 = open("Foo.txt",O_RDWR, 0); int fd2 = open("Foo.txt",O_RDWR, 0); int fd3 = open("Foo.txt",O_RDWR, 0); int fd4 = open("Foo.txt",O_RDWR, 0); int fd5 = open("Foo.txt",O_RDWR, 0); int fd6 = open("Foo.txt",O_RDWR, 0); //close(fd); printf("%d\n%d\n%d\n%d\n%d\n%d\n",fd1,fd2,fd3,fd4,fd5,fd6); return 0; }
$ ./systemi01 
3
4
5
6
7
8
#include <stdio.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> int main(){ int fd1 = open("Foo.txt",O_RDWR, 0); int fd2 = open("Foo.txt",O_RDWR, 0); close(fd1); close(fd2); int fd3 = open("Foo.txt",O_RDWR, 0); int fd4 = open("Foo.txt",O_RDWR, 0); int fd5 = open("Foo.txt",O_RDWR, 0); int fd6 = open("Foo.txt",O_RDWR, 0); //close(fd); printf("%d\n%d\n%d\n%d\n%d\n%d\n",fd1,fd2,fd3,fd4,fd5,fd6); return 0; }
$ ./systemi01 
3
4
3
4
5
6
#include <stdio.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> int main(){ int fd1 = open("Foo.txt",O_RDWR, 0); char c; while(read(fd1,&c,1)!=0) write(STDOUT_FILENO,&c,1); close(fd1); return 0; }
$ ./systemi02
123123

Practice Problem 10.2

open file table 存在各自的 file position

Foo.txt -> hi

int main(int argc, char **argv) { char c; int fd1, fd2; fd1 = open("Foo.txt", O_RDWR, 0); fd2 = open("Foo.txt", O_RDWR, 0); read(fd1, &c, 1); printf("%c\n", c); read(fd2, &c, 1); printf("%c\n", c); return 0; }
$ ./pp2
h
h

Practice Problem 10.3

fork 則繼承各自的 open file table (refcnt+1)
Foo.txt -> hi

int main(int argc, char **argv) { char c; int fd1; fd1 = open("Foo.txt", O_RDWR, 0); if(fork()==0){ read(fd1, &c,1); exit(0); } wait(NULL); read(fd1, &c,1); printf("%c\n",c); return 0; }
./pp3
i

Practice Problem 10.4

dup2 -> make the new fd point to the entry of open file table pointed by old fd.

int main(int argc, char **argv) { char c; int fd1; fd1 = open("Foo.txt", O_RDWR, 0); int fd2=4; if(dup2(STDOUT_FILENO,fd2)) printf("fd2 = %d\n",fd2); while(read(STDIN_FILENO,&c,1)){ write(fd2,&c,1); } return 0; }

Practice Problem 10.5

Foo.txt ->hi~

int main(int argc, char **argv) { char c; int fd1; fd1 = open("Foo.txt", O_RDWR, 0); int fd2= open("Foo.txt", O_RDWR, 0); read(fd2,&c,1); dup2(fd2,fd1); read(fd1,&c,1); printf("%c\n",c); return 0; }
./pp5
i

Practice Problem 10.6

int main(int argc, char **argv) { char c; int fd1; fd1 = open("Foo.txt", O_RDWR, 0); int fd2= open("foo.txt", O_RDWR, 0); close(fd2); fd2= open("foo.txt", O_RDWR, 0); printf("%d\n",fd2); return 0; }
$ ./pp6
4

Practice Problem 10.7

int main(){ printf("IO\n"); struct stat * status; rio_t rp; int n; char *buf = malloc(RIO_BUFSIZE*sizeof(char)); rio_readinitb(&rp,STDIN_FILENO); while((n = rio_readnb(&rp,buf,MAX_LINE))!=0) rio_writen(STDOUT_FILENO,buf,n); return 0; }

Practice Problem 10.8

int main(int argc, char **argv) { struct stat stat1, stat2; char *type1, *type2, *readok; int fd = atoi(argv[1]); int fd1, fd2; fd1 = open("foo.txt", O_RDONLY, 0); fd2 = open("file", O_RDONLY, 0); fstat(fd, &stat1); if (S_ISREG(stat1.st_mode)) { type1 = "reg"; } else if (S_ISDIR(stat1.st_mode)) { type1 = "dir"; } else { type1 = "oth"; } //check access readok = (stat1.st_mode & S_IRUSR) ? "yes" : "no"; printf("fd : %s , type : %s read : %s\n", argv[1], type1, readok); return 0; }
$ ./pp8 0
fd : 0 , type : oth read : yes
$ ./pp8 1
fd : 1 , type : oth read : yes
$ ./pp8 2
fd : 2 , type : oth read : yes
$ ./pp8 3
fd : 3 , type : reg read : yes
$ ./pp8 4
fd : 4 , type : dir read : yes


Practice Problem 10.9

Practice Problem 10.10