深入理解计算机系统_第10章 系统级I/O

区分系统级unix I/O函数高级的标准I/O函数
有时候必须使用系统级unix I/O 函数, 例如, 标准I/O库没有提供读取文件元数据的方式,所以必须使用系统unix I/O函数(stat); 访问网络文件socket的时候, 使用标准IO函数访问存在不足值得风险;

10.1 Unix I/O

将所有的I/O 设备抽象问文件, 将所有的输入、输出动作抽象为读、写操作;因此, linux内核只需要提供一个低层次的接口——unix I/O, 就可以对所有的设备以统一的格式进行访问;
1) 打开文件
应用程序陷入到内核中打开文件,内核创建相应的数据结构(三个表:描述符表, 文件表, vnode表)打开指定的文件, 内核返回一个整型数值(文件描述符)给应用程序。应用程序通过文件描述符可以访问到内核创建的三张表,从而得到文件的所有信息;
2) 每个进程都打开三个文件:STDIN_FILENO,, STDOUT_FILENO, STDERR_FILENO;
3) 改变当前的文件位置。 对于每一个文件, 内核的文件表中记录着文件位置k,k初始值为0; 文件位置是从文件开头的字节偏移量。 应用程序通过执行seek可以设置文件的当前位置k;
4) 读写文件, 当读取的字节数超过文件的大小边界的时候, 会触发EOF符号;
5) 关闭文件, 关闭文件只是将文件的引用计数减1, 当引用计数为0 的时候, 内核会释放与这个文件相关的三个数据结构, 并且将文件描述符回收到描述符池中;

10.2 文件

1) 普通文件。
一般分为文本文件和二进制文件;
内核的文本文件包含了一个文本行序列, 其中每一个文本行都是一个字符序列,以“\n" 结束;
2) 目录
目录包含一组链接的文件, 每一个链接都是将一个文件名映射到一个文件;
每个目录至少包括两个条目: … 和. (父目录和当前目录);
3) 套接字,进程之间的跨网络通信的文件;
4) 其他文件类型
命名管道, 符号链接, 字符设备, 块设备;
每一个进程都有一个进程自己的当前目录;
绝对路径和相对路径;

10.3 打开和关闭文件

1) 打开文件

int open(char *filename, int flag, mode_t mode);

open函数打开文件, 会在内核中创建三个表,这三个表可以包含文件的所有信息,然后将一个文件描述符返回给进程;
flag表示以何种方式打开文件,如读,写等等;
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:读写
O_CREAT:如果文件不存在, 就创建一个截断的文件(截断表示空的意思);
O_TRUNC:如果文件已经存在,就截断这个文件;
O_APPEND:每次写操作之前, 设置文件位置到文件的结尾处;
例如:

fd = open("foo.txt", O_WRONLY | O_APPEND, 0);

mode参数指定了新文件的访问权限位:
当进程通过带某个mode参数的open函数调用来创建一个新文件的时候,文件访问权限被设置为mode & ~umask.
umask的默认值是通过umask函数设置的

#define DEF_MODE    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
#define DEF_UMASK	S_IWGRP | S_IWOTH
unask(DEF_UMASK);
fd = open("foo.txt", O_CREAT | O_TRUNC | O_WRONLY, DEF_MODE);

在这里插入图片描述
2) 关闭文件

int close(int fd);

关闭文件就是将文件描述符的引用计数减1, 如果引用计数为0, 就将这个文件在内核中的数据结构释放, 并将文件描述符回收到文件描述符池中;

10.4 读和写文件

1) read和write函数:

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

read 返回值是读取到的字节数, 返回值为0 表示EOF, 返回值为-1表示出错;
一次一个字节地从标准输入复制到标准输出:

int main(void)
{
   
	char c;
	while(read(STDIN_FILENO, &c, 1) > 0)
		write(STDOUT_FILENO, &c, 1);
	exit(0);
}

2) lseek函数可以修改文件的偏移位置;
3) ssize_t 和 size_t 有哪些区别
size_t 被定义为unsigned long,即无符号的;
ssize_t 被定义为long, 是有符号的;
某些情况下, read函数和write函数返回值会比参数n的小,这些不足值不表示有错误,出现不足值的情况是:

  1. 读的时候遇到EOF;
  2. 从终端中读取文本行,那么每一个read函数只能读取一个文本行,返回的不足值等于文本行字节大小;如果从文件中读取,不会一次只读取一个文本行,而是读取指定大小的字节数;
  3. 读写socket, 打开的文件是网络套接字, 那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值;对linux 管道使用read, write函数, 也可能出现不足值。

读取磁盘文件时, 将不会遇到不足值,只会遇到EOF。
但是读取网络文件或是管道文件时候,会遇到不足值,需要对不足值进行处理,否则部分数据将丢失;
读取终端文件的时候,一次只能读取一个文本行,read和write 返回的也是不足值;

10.5 用RIO包健壮地读写

引入RIO包的原因是:
1) 处理不足值;
2) 由于处理了不足值问题,就可以对同一个文件描述符反复地调用rio_readn, rio_writen, 因此其是线程安全的;
3) rio_writen 是不会返回不足值的;

RIO包会自动为你处理不足值。 应当使用RIO包处理网络文件。
RIO提供了两类不同的函数:
1) 无缓冲的输入输出函数。
直接在内存和文件之间传送数据,内核中有缓冲,但是应用程序中没有缓冲;
无缓冲的输入输出函数特别适用于二进制数据在内存和网络文件之间传输;
2)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值