20145212 《信息安全系统设计基础》第9周学习总结
20145212 《信息安全系统设计基础》第9周学习总结
教材学习内容总结
UnixI/O
1.输入/输出是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。输入操作时从I/O设备拷贝数据到主存,而输出操作时从主存拷贝数据到I/O设备
2.所有的I/O设备,如网络、磁盘盒终端,都被模型化为文件,而所有的输入和输出都被当做对相应的文件的读和写来执行。这是一种应用接口,称为Unix I/O
3.每个unix文件都是一个m字节的序列;所有I/O设备如网络、磁盘和终端都被模型化为文件,而输入和输出就是对这些文件的读写操作。
4.unix系统中输入输出的操作:
打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符。unix系统创建每个进程的时候都有三个打开的文件:标准输入;标准输出,标准错误。
改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k(从文件开头起始的字节偏移量)。
读写文件。读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。
关闭文件。应用通知内核关闭这个文件;作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池当中。
把I/O抽象成文件,其实是把系统内的一切操作都变成对文件(字节序列)的操作;这样极大地简洁了各类动作。上述对文件的描述,其实是输入输出类型的“文件操作”,分别对应的是进行I/O、读写操作。
打开和关闭文件
- 打开文件:
fd = Open("文件名",flag参数,mode参数);
- mode参数指定新文件的访问权限位。作为上下文的一部分,每个进程都有一个umask;当进程通过带某个带mode参数的open函数用来创建一个新文件的时候,文件的访问权限位被设置为mode & ~umask。
- fd是返回的文件描述符(数字),总是返回在进程中当前没有打开的最小描述符。
- open函数将filename转换成一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。
- flag参数:
O_RDONLY:只读。
O_WRONLY:只写。
O_RDWR:可读可写。
//一位或者多位掩码的或
O_CREAT,表示如果文件不存在,就创建它的一个截断的文件。
O_TRUNC:如果文件已经存在,就截断它。
O_APPEND:在每次写操作前,设置文件位置到文件的结尾处。
- 关闭文件:
int close(int fd);//若成功则返回0,不成功则为-1
读写文件
应用程序是通过分别调用read和write函数来执行输入和输出的。
read函数:
ssize_t read(int fd,void *buf,size_t n);//成功则返回n;EOF返回0;出错返回-1
从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf。返回值:-1:一个错误;0:EOF;否则,返回值:实际传送的字节数量。
write函数:
ssize_t write(int fd,const void *buf,size_t n);
从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置。
不足值:在某些情况下,read和write传送的字节比应用程序要求的要少
原因:
1.读时遇到EOF
2.从终端读文本行
3.读和写网络套接字
用RIO包健壮地读写
- 实质:I/O包
- 两种函数:无缓冲的输入输出函数、带缓冲的输入函数(线程安全)
- RIO的无缓冲的输入输出函数
- 应用程序通过调用rioreadn和riowritten函数可以在存储器和文件之间直接传送数据。
ssize_t rio_readn(int fd,void *usrbuf,size_t n);
ssize_t rio_writen(int fd,void *usrbuf,size_t n);
//rio_ readn函数在遇到EOF时,只能返回一个不足值;
//rio_ writen函数绝不会返回不足值。
RIO读程序的核心——rio_read函数
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));//调用read函数填满缓冲区
if(rp->rio_cnt<0)//排除文件读不出数据的情况
{
if(error != EINTR)
{
return -1;
}
}
else if(rp->rio_cnt=0)
return 0;
else
rp->rio_bufptr = rp->rio_buf;//更新现在读到的位置
}
cnt=n;
if(rp->rio_cnt<n)
cnt=rp->rio_cnt;//以上三步,将n与rp->rio_cnt中较小的值赋给cnt
memcpy(usrbuf,rp->rio_bufptr,cnt);把读缓冲区的内容拷贝到用户缓冲区
rp->rio_bufptr+=cnt;
rp->rio_cnt-=cnt;
return cnt;
}
读取文件元数据
应用程序能够通过调用stat和fstat函数,检索到关于文件的信息(元数据)。
函数格式:
#include <unistd.h>
#include <sys/stat.h>
int stat(cost char *filename,struc sta *buf);
int fstat(int fd,struct stat *buf);
st_size成员包含了文件的字节数大小
st_mode成员编码了文件访问许可位和文件类型
Unix提供的宏指令根据st_mode成员来确定文件的类型
宏指令:S_ISREG() 普通文件?二进制或文本数据
宏指令:S_ISDIR() 目录文件?包含其他文件的信息
宏指令:S_ISSOCK() 网络套接字?通过网络和其他进程通信的文件
共享文件
内核用三个相关的数据结构来表示打开的文件:
- 描述符表:每个打开的描述符表项指向文件表中的一个表项
- 文件表:所有进程共享这张表,每个表项包括文件位置,引用计数,以及一个指向v-node表对应表项的指针
- v-node表:所有进程共享这张表,包含stat结构中的大多数信息
三种打开文件的类型:
- 典型:描述符各自引用不同的文件,没有共享
- 共享:多个描述符通过不同的文件表表项引用同一个文件。(关键思想:每个描述符都有自己的文件位置,对不同描述符的读操作可以从文件的不同位置获取数据)
- 继承:子进程继承父进程打开文件。调用fork后,子进程有一个父进程描述符表的副本,父子进程共享相同的打开文件表集合,因此共享相同的文件位置
标准I/O和I/O函数
- 应用程序可以通过open、close、lseek、read、write和stat这样的函数来访问Unix I/O。
- RIO函数:read和write的健壮的包装函数,自动处理不足值,为读文本行提供一种高效的带缓冲的方法。
- 标准I/O函数:提供了Unix I/O函数的一个更加完整的带缓冲的替代品,包括格式化的I/O例程。是磁盘和终端设备I/O之选。
- 套接字描述符:Unix对网络的抽象是一种称为套接字的文件类型,被称为套接字描述符。应用进程通过读写套接字描述符来与运行在其他计算机上的进程通信。
- 对流I/O限制:
跟在输出函数之后的输入函数,必须在其中间插入fflush、fseek、fsetpos或者rewind函数,后三个函数使用Unix I/O中的lseek函数来重置当前的文件位置。
跟在输入函数之后的输出函数,必须在中间插入fseek、fsetpos或者rewind的调用,一个输出函数不能跟随在一个输入函数之后,除非该输入函数遇到了一个EOF。
- 解决对流I/O限制的方法是:
采用在每个输入操作前刷新缓存区这样的规则来满足。
对同一个打开的套接字描述符打开两个流,一个用来读,一个用来写。
遇到的问题及解决
-
书上练习题10.1代码编译时头文件出错
-
原因是
csapp.h
不是计算机自带的,但是可以从网上下载 -
open函数总是返回最低的未打开的描述符。
-
程序的输出是
fd2 = 3
本周代码托管
- 链接
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/2 | 10/10 | 使用虚拟机安装linux系统 |
第二周 | 341/341 | 1/3 | 20/30 | 掌握核心的linux命令 |
第三周 | 177/518 | 2/5 | 16/46 | 学会了虚拟机上的VC编程 |
第五周 | 161/679 | 1/6 | 15/61 | |
第六周 | 73/752 | 1/7 | 15/76 | 安装了Y86处理器 |
第七周 | 134/886 | 1/8 | 12/88 | 建立了项目结构 |
第八周 | 0/886 | 2/10 | 12/100 | 进行了系统的复习 |
第九周 | 61/947 | 1/11 | 10/110 | 学习Linux操作系统的基本I/O服务 |