01 June 2016

第十章 系统级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);
  • 关闭文件,关闭一个已经关闭的文件会出错. 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成员。
  • 多个描述符可通过不同的文件表表项来引用同一个文件。 image