一个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参数指明了进程打算如何访问这个文件:
Flags参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:
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提供了两类不同的函数:
通过调用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成员来确定文件的类型。
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, OWRONLY|O_CREAT, fdmode); //打开文件操作 dup2(fd, STDOUTFILENO); //把输出重定向到fd标识的文件 close(fd);
}
fd2=dup(STDOUT_FILENO);说明fd2表示了标准输出 如果我们想把刚刚定向到fd的输出,再定向回标准输出,我们可以用下面的代码实现:
void resumestdout() //恢复输出,把标准输出定向到fd2,fd2代表的是标准输出 {
dup2(fd2, STDOUTFILENO);
close(fd2);
}
参考资料
[1]《深入理解操作系统》 [2]http://www.cnblogs.com/lalacindy/p/4933184.html [3]百度百科http://baike.haosou.com/doc/5948579-6161518.html