10系统级I/O
10.1 Unix I/O
这一节涉及到操作系统的基本抽象之一——文件。也就是说,所有的I/O设备都被模型化为文件,而所有的输入输出都被当做对相应文件的读/写。相关的执行动作如下:
打开文件:
应用程序向内核发出请求→要求内核打开相应的文件→内核返回文件描述符
文件描述符:
标准错误——2(STDERR_FILENO)
改变当前的文件位置
通常,读,写操作都从当前文件偏移量处开始(也就是文件位置),并使偏移量增加所读写的字节数,可以理解为光标所在的位置。
当打开一个文件的最初时候文件的偏移量为0.
通过seek操作,可以显示的设置文件的当前位置为k。
读写文件
读
读操作就是从文件拷贝n>0个字节到存储器,并且改变文件当前位置。(如果当前位置是k,则改变为k+n)
写
写操作是从存储器拷贝n>0个字节到一个文件,然后更新当前文件位置。
关闭文件
应用通知内核关闭文件→内核释放文件打开时的数据结构→恢复描述符→释放存储器资源。
10.2打开和关闭文件
1.open函数
(1)函数定义:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
intopen(char *filename, int flags, mode_t mode);
(2)参数解析:
O_APPEND:写操作前设置文件位置到结尾处
这些值可以用或连接起来。
mode:指定了新文件的访问权限位
2.close函数
(1)函数定义:
#include<unistd.h>
intclose(intfd);
(2)参数解析:
10.3读和写文件
1.读 read
(1)函数原型:
#include<unistd.h>
ssize_tread(intfd, void *buf, size_t n);
(2)参数解析:
2.写 write
(1)函数原型:
#include<unistd.h>
ssize_twrite(intfd, void *buf, size_t n);
(2)参数解析:
3.通过lseek函数可以显式的修改当前文件的位置
4.不足值
不足值指在某些情况下,read和write传送的字节比应用程序要求的要少,原因如下:
10.4 RIO包健壮的读写
1.RIO的无缓冲的输入输出函数。
作用是直接在存储器和文件之间传送数据,常适用于网络和二进制数据之间。
rio_readn函数和rio_writen定义:
#include "csapp.h"
ssize_trio_readn(intfd, void *usrbuf, size_t n);
ssize_trio_writen(intfd, void *usrbuf, size_t n);
参数:
返回值:
rio_writen成功则返回传送的字节数,出错为-1,没有不足值。
2.RIO的带缓冲的输入函数
可高效的从文件中读取文本行和二进制数据。
一个文本行就是一个由换行符结尾的ASCII码字符序列。
rio_readnb函数
ssize_trio_readnb(rio_t *rp, void *usrbuf, size_tn)
{
size_tnleft = n;
ssize_tnread;
char *bufp = usrbuf;
while (nleft> 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* 调用read填充 */
else
return -1; /* 错误,返回-1 */
}
elseif (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* 返回成功传送的字节数*/
}
rio_readlineb函数
ssize_trio_readlineb(rio_t *rp, void *usrbuf, size_tmaxlen)
{
intn, rc;
char c, *bufp = usrbuf;
for (n = 1; n<maxlen; n++) { //最多是maxlen-1个
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n')//找到换行符,就退出
break;
} elseif (rc == 0) {
if (n == 1)
return 0; /* EOF,并且没有读到数据 */
else
break; /* EOF,有数据,出现不足值 */
} else
return -1; /* 错误,返回-1 */
}
*bufp = 0;
returnn;//返回成功传送的字节数
}
10.5读取文件元数据
元数据即文件信息,需要用到的函数是stat和fstat。定义如下:
#include<unistd.h>
#include<sys/stat.h>
intstat(constchar *filename, struct stat *buf);
intfstat(intfd,struct stat *buf);
返回值:成功为0,错误为-1
参数:
stat需要输入文件名,而fstat需要输入的是文件描述符。
关于stat数据结构如下图:
st_mode:包编码文件访问许可位和文件类型。许可位在第一节提到了,Unix文件类型如下,并有对应的宏指令,含义均为“是xx吗”,这些宏在sys/stat.h中定义:
套接字 通过网络与其他进程通信的文件 S_ISSOCK()
10.6共享文件
内核用三个相关的数据结构来表示打开的文件:
每个描述符都有它自己的文件位置,所以对不同描述符的读操作可以从文件的不同位置获取数据。
子进程继承父进程的打开文件:
参考资料
1.2035202闫佳歆与20135302魏静静
2.《深入理解计算机系统》
3.百度百科知识
stdio.h 标准输入输出
stdlib.h C标准函数库
unistd.h Unix类系统定义符号常量
fcntl.h 定义了很多宏和open,fcntl函数原型
sys/types.h 基本系统数据类型
dirent.h unix类目录操作的头文件,包含了许多UNIX系统服务的函数原型,例如opendir函数、readdir函数。
termios.h 在Posix规范中定义的标准接口
学习总结 遇到的问题:
预计学习时间:6h实际学习时间:10h
在编译代码时,与小组同学们遇到了一样的问题,发现不能运行csapp这个头文件,推理这个应该是自己编的。发现有两个解决办法,一个是老师之前在班群里的代码,一个是同学修改了头文件,是函数正常输出。她的博客写了解决方法(20135302)。另外,本章内容少,但是有些代码还是不太懂,有时候要靠一句句地百度理解。虽然老师上课,有给我们介绍一点知识,但是本章的学习还是要靠自学,研究函数的返回值与参数等,学了一个函数,掌握其方法,其他就可以举一反三啦。
实践代码:下面四图各为老师给的代码运行的结果。cp1,cp2,who1,who2