csapp Unix I/O
第十章 系统级IO
- I/O是在主存和外部设置(磁盘驱动器、终端和网络)之间拷贝数据的过程。
- 输入操作是从I/O设备拷贝数据到主存。
- 输出操作是从主存拷贝数据到I/O设备。
Unix I/O
- 所有的I/O设备,如磁盘、网络和终端,都被模型化为文件的方式,都被当做对相应的文件读写来进行。
- 打开文件,若返回成功,则为新文件描述符,出错为-1。
C #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(char *filename, int flags, mode_t mode); //O_RDONLY:只读;O_WRONLY:只写;O_RDWR:可读写。 fd = open("foo.txt", O_RDONLY, 0); //O_CREAT:文件不存在,创建一个空文件;O_TRUNC:文件存在,则截断它;O_APPEND:在每次写之前,设置文件位置到文件的末尾处。 fd = open("foo.txt", O_WRONLY|O_APPEND, 0);
- 打开文件的mode表示新文件的访问位权限,采用S_IRUSR,S_IWUSR,S_IXUSR 使用者的读、写、执行;S_IRGRP,S_IWGRP,S_IXGRP分组拥有者的读写执行;S_IROTH,S_IWOTH,S_IXOTH其他(任何人)能够读、写、执行。
- 文件的访问权限被置为mode & ~umask
C umask(DEF_UMASK); fd = open("foo.txt", O_CREAT|O_TRUNC|O_WRONLY, DEF_MODE);
- 文件的访问权限被置为mode & ~umask
- 关闭文件,关闭一个已经关闭的文件会出错.
C #include <unistd.h> int close(int fd);
- 读和写文件,调用read和write,如:
C #include <unistd.h> //若成功则返回为读的字节数,若EOF则为0,若出错为-1。 ssize_t read(int fd, void *buf, size_t n); //若成功则返回为写的字节数,否则出错返回-1。 ssize_t write(int fd, const void *buf, size_t n);
RIO
- RIO表示健壮的读写,包括无缓冲I/O函数和带缓冲I/O函数。
RIO的无缓冲输入输出函数
- 调用rio_readn和rio_writen,可以在存储器和文件之间传输数据。
#include <csapp.h> //从fd的当前文件位置最多传n个字节到存储器位置usrbuf ssize_t rio_readn(int fd, void *usrbuf, size_t n); //从usrbuf传送n个字节到描述符fd。 ssize_t rio_writen(int fd, void *usrbuf, size_t n);
RIO带缓冲的输入输出函数
//读一个文本行,并且拷贝到存储器位置usrbuf
void rio_readinitb(rio_t *rp, int fd);
//最多读maxlen
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
//从文件rp读n个字节到存储器位置usrbuf
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readn(int fd, void *usrbuf, size_t n) {
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) {
nread = 0; /* and call read() again*/
} else {
reutrn -1; /* errno set by read()*/
}
} else if (nread == 0) {
break;
}
nleft -= nread;
bufp += nread;
}
return (n - nleft);
}
ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) < 0) {
if (errno == EINTR) {
nwritten = 0; /* and call read() again*/
} else {
reutrn -1; /* errno set by read()*/
}
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
-
rio_read 和 Unix read 有一样的语义。在出错时,它返回值-1, 并且适当地设置errno。在EOF时,返回0。
C #define RIO_BUFSIZE 8192 typedef struct { int rio_fd; /* Descriptor for this internal buf */ int rio_cnt; /* Unread bytes in internal buf */ char *rio_bufptr; /* Next unread byte in internal buf */ char rio_buf[RIO_BUFSIZE]; /* Internal buffer */ } riot; static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { int cnt; while (rp -> rio_cnt <= 0) { rp -> rio_cnt = read(rp -> rio_fd, rp -> rio_buf, sizeof(rp -> rio_buf)); if (rp -> rio_cnt < 0) { if (errno != EINTR) { return -1; } } else if (rp -> rio_cnt == 0) { return 0; } else { rp -> rio_bufptr = rp -> rio_buf; } } cnt = n; if (rp -> rio_cnt < 0) { cnt = rp -> rio_cnt; } memcpy (usrbuf, rp -> rio_bufptr, cnt); rp -> rio_bufptr += cnt; rp -> rio_cnt -= cnt; return cnt; }
```C ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { size_t nleft = n; ssize_t nread; char *bufp = usrbuf; while (nleft > 0) { if ((nread = rio_read(rp, bufp, nleft)) < 0) {if (errno == EINTR) { return 0; /*Call read() again*/ } else { return -1; /* errno set by read() */ } } else if (nread == 0) { break; /* EOF */ } nleft -= nread; bufp += nread; } return (n - nleft); } ``` ```C ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { int n, rc; char c, *bufp = usrbuf; for (n = 1; n < maxlen; ++n) { if ((rc = rio_read(rp, &c, 1)) == 1) { *bufp++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) { return 0; /* EOF, no data read */ } else { break; /* EOF, some data read */ } } else { return -1; } } *bufp = 0; /*设置行结尾ASCII = '\0'*/ return n; } ```
共享文件
- 数据结构
- 描述符表(descriptor table):每个进程一张表,表项是进程打开的文件索引
- 文件表(file table):所有进程共享,包括当前文件的位置,引用计数,以及指向v-node表的指针。
- 文件表(v-node table):所有进程共享,st_mode和st_size,st_type成员。
- 多个描述符可通过不同的文件表表项来引用同一个文件。