20135304刘世鹏——信息安全系统设计基础第九周总结

第十章、系统I/O

一个Unix文件就是一个m个字节的序列:B0,B1,…,BK,…,Bm-1

一、unix i/o 596

Unix I/O:一种将设备优雅地映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口,这使得所有的输入输出都能以一种统一且一致的方式来执行:

**1.打开文件:** - 内核返回一个小的非负整数,叫做描述符。 -Unix外壳创建的每个进程开始时都有三个打开的文件:标准输入(描述符 为0)、标准输出(描述符为1)、标准错误(描述符为2)。 

2.改变当前文件位置:

对于每个打开的文件,内核保持着一个文件位置K,初始为0。 这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显示地设置文件当前位置为K。 

3.读写文件:

读操作:从文件拷贝n>0个字节到存储器,从当前文件位置K开始,然后将K增加到k+n。 写操作:从存储器拷贝n>0个字节到一个文件,从当前文件位置K开始然后更新K 。 

4.关闭文件:

二、打开和关闭文件

open函数将filename转换成一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中没有打开的最小描述符。

Flags参数指明了进程打算如何访问这个文件:

  • O_RDONLY:只读
  • O_WRONLY:只写
  • O_RDWR:可读可写

Flags参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:

  • O_CREAT:如果文件不存在,就创建它的一个截断的(空)文件。
  • O_TRUNC: 如果文件已经存在,就截断他。
  • O_APPEN D:在每次写操作前,设置文件位置到文件的结尾处。

Mode参数指定了新文件的访问权限位。

 

三、读和写文件

read (int fd, void *buf, size_t n)(返回:若成功,则为读的字节数,若EOF则为0,若出错则为-1)  write(int fd, const void *buf, size_t n)(返回:若成功则为写的字节数,若出错则为-1) 

旁注:sizet是作为usigned int,而ssizet是作为int。

在某些情况下,read和write传送的字节比应用程序要求的要少。出现这种情况的可能的原因有:

读时遇到EOF。假设该文件从当前文件位置开始只含有20个字节,而应用程序要求我们以50个字节的片进行读取,这样一来,这个read的返回的值是20,在此之后的read则返回0。 从终端读文本行。如果打开的文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小。 读和写socket。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会导致read和write返回不足值。

四、用rio包健壮地读写

RIO包会自动为你处理上文中所述的不足值。

RIO提供了两类不同的函数:

  • 无缓冲的输入输出函数。
  • 带缓冲的输入函数。 10.4.1 RIO的无缓冲的输入输出函数

通过调用rioreadn和riowriten函数,应用程序可以在存储器和文件之间直接传送数据。

 

五、读取文件元数据

文件的元数据,就是关于文件的信息。

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

讨论Web服务器时,会需要stat数据结构中的stmode和stsize成员。st_mode则编码了文件访问许可位和文件类型。

Unix提供的宏指令根据st_mode成员来确定文件的类型。

  • S_ISREG() 普通文件
  • S_ISDIR() 目录文件
  • S_ISSOCK() 套接字

stat数据结构中的成员。重点掌握stmode,stsize.

     st_size:文件字节数大小       st_mode:文件访问许可位和文件类型。(普通文件:二进制文件和文本文件。目录文件:其他文件信息。套接字:通过网络与其他进程通信的文件。) 

六、共享文件

用三个相关的数据结构表示打开的文件:

               描述符表:每个打开的描述符表项指向文件表中的一个表项。                 文件表:打开的文件的集合是由一张文件表表示的,所有的进程共享这张表。包括文件位置、引用计数(当前指向该表项的描述符表项数),指向v-node表的指针。                 v-node表:包含stat结构中大多数信息。 

打开文件的内核数据结构 

 

文件共享。共享了同一个磁盘文件 

 

子父进程共享文件。子进程有一个父进程描述表符副本,所以他们共享打开文件的集合。注意,在内核删除相应文件表表项之前,子父进程必须都关闭他们的描述符。

七、I/O重定向

方法一:使用dup2函数 dup2函数拷贝了描述符表表项oldfd到描述符表项newfd,覆盖描述符表项newfd的内容。若果newfd已经打开了,dup2会拷贝oldfd之前关闭newfd.

标准的I/O 标准I/O库将一个打开的文件模型化为一个流,一个流就是一个指向FILE类型的结构指针,。stdin\stdout\stdeer分别对应标准输入、标准输出、标准错误。

extern FLIE *stdin extern FILE *stdout extern FILE *stderr 

类型为FILE的流是对文件描述符和流缓冲区的抽象。 

 

问题及解决

1、不理解dup2函数的工作原理

通过搜索资料:

定义这个函数的头文件是unistd.h,有兴趣的可以自己看看这个头文件包含的内容

  要提的是这个头文件同时定义了下面三个常量 

STDERRFILENO = 2 标准错误输出 STDINFILENO = 0 标准输入 STDOUT_FILENO = 1 标准输出 兄弟们学习网络编程用0,1,2这些参数的时候也得知道代表的意思

  要说这两个函数的意思,还是看一段具体的代码 

int fd, fd2;
modet fdmode = SIRUSR|SIWUSR|SIRGRP|SIROTH;

void redir_stdout(const char *filename)
{
fd2=dup(STDOUTFILENO);
fd = open(filename, O
WRONLY|O_CREAT, fdmode); //打开文件操作 dup2(fd, STDOUTFILENO); //把输出重定向到fd标识的文件 close(fd);
}

  fd2=dup(STDOUT_FILENO);说明fd2表示了标准输出   如果我们想把刚刚定向到fd的输出,再定向回标准输出,我们可以用下面的代码实现: 

void resumestdout() //恢复输出,把标准输出定向到fd2,fd2代表的是标准输出 {
dup2(fd2, STDOUT
FILENO); 
close(fd2);
}

参考资料

[1]《深入理解操作系统》 [2]http://www.cnblogs.com/lalacindy/p/4933184.html [3]百度百科http://baike.haosou.com/doc/5948579-6161518.html

posted @ 2015-11-08 23:33  刘世鹏  阅读(222)  评论(0编辑  收藏  举报