2017-2018-1 20155315 《信息安全系统设计基础》第13周学习总结

本文深入探讨了I/O系统的原理与实现,详细介绍了输入输出的基本概念、Unix I/O的工作机制,以及文件操作的重要细节,包括文件打开、关闭、读写等关键流程。

2017-2018-1 20155315 《信息安全系统设计基础》第13周学习总结

教材学习内容总结

  • 本周老师让我们学习的是自己觉得最终要的一章,我学习的是第十章。在我看来,不论是什么产品,都存在设计端与使用端,更确切的说,正因为有使用端,才需要设计端。因此,使用端的使用感受最为重要,而作为一个对外的平台与程序,I/O系统是基础和核心,也因此我选择系统级I/O进行深入学习。
  • 主要知识点:
    • 输入是从I/O设备复制数据到主存
    • 输出是从主存复制数据到I/O设备

UnixI/O

  • 每个unix文件都是一个m字节的序列。
  • unixI/O是由内核提供的。
  • I/O设备都是文件,输入就是写文件,输出就是读文件。
  • 程序要求内核打开文件来说明它想要访问相应的I/O设备,内存返回描述符,fd = Open("文件名",flag参数,mode参数);,此时fd就是一个描述符。
  • 每个进程都有三个文件,头文件是<unistd.h>,输入返回0,输出返回1,错误返回2。
  • 文件位置k是字节偏移量,初始为0,用seek查找当前位置。
  • 读是将文件复制n个字节到内存中,文件位置增加到k+n,当n大于文件字节的时候,会返回EOF错误,表示读到文章结尾。

文件

  • 普通文件分文本文件和二进制文件,文本文件致函ASCII或Unicode字符。
  • 对内核而言,文本文件和二进制文件没有区别
  • 文本文件每一行都是字符序列
  • 目录是一组包含链接的文件。
  • 路径名是一个字符串,有绝对路径和相对路径。
打开和关闭文件
  • 打开文件
int open(char *filename, int flags, mode_t mode)
flags行为
O_RDONLY只读
O_WRONLY只写
O_RDWR可读可写
O_CREAT文件不存在即创建一个截断文件
O_TRUNC文件存在就截断
O_APPEND写之前设置是文件在文件结尾
  • 关闭文件
int close(int fd);
读写文件

1071536-20171217103719111-502552753.png

  • 读文件
ssize_t Read(int fd, void *buf, size_t count);
返回值意义
-1错误
0读到文章末尾
其他实际传送的字节数量
  • 写文件
ssize_t Write(int fd, const void *buf, size_t count);
  • 一次一个字节地从标准输入复制到标准输出
#include "csapp.h"
int main(void)
{
char c;
while(read(STDIN_FILENO,&c,1)!=0)
write(STDOUT_FILENO,&c,1);
exit(0);
}

1071536-20171217103725911-1248509126.png

  • ssize_t被定义为long,是有符号的;size_t被定义为unsigned long,是无符号数
  • 可能出现不足值
    • 读时遇到FOF
    • 从终端读文本行
    • 读和写网络套接字

RIO(Robust I/O)

1071536-20171217103733360-2095846714.png

  • 无缓冲的输入输出:直接在内存和文件之间传送数据
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
  • read遇到EOF只能返回一个不足值,write不会返回不足值。
  • 被中断时函数会重启read或write
  • 带缓冲的输入输出:数据存在缓冲区
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
  • readlineb读取一行文本到缓冲区(包括换行符),用NULL(0)结束,最多读maxlen-1个字节
  • rio_readnb读取n个字节到缓冲区
  • 一次一行地从标准输入复制一个文本文件到标准输出
#include "csapp.h"
int main(int argc,char **argv)
{
int n;
rio_t rio;
char buf[MAXLINE];
rio_readinitb(&rio,STDIN_FILENO);
while((n=rio_readlineb(&rio,buf,MAXLINE))!=0)
rio_writen(STDOUT_FILENO,buf,n);
}

1071536-20171217103749768-1436686341.png
我的理解:读文件主要有三部分。一是init函数初始化,将文件描述符与读缓存区联系在一起;二是readline或readnb将文本复制到读缓存区;三是内部的read函数,当缓冲区为空,调用read填满,非空的时候,read从读缓存区复制字节到用户缓冲区,也就是说,涉及到两个缓冲。

读取

读取元数据

1071536-20171217103756233-1288728290.png

  • stat函数以一个文件名作为输入,fstat以文件描述符作为输入
st_mode文件访问许可位和文件类型
S_ISREG(m)普通文件
S_ISDIR(m)目录文件
S_ISSOCK(m)网络套接字
  • 查询和处理一个文件的st_mode位。
#include "csapp.h"
int main(int argc,char **argv)
{
struct stat stat;
char *type,*readok;
Stat(argv[1],&stat);
if(S_ISREG(stat.st_mode))
  type = "regular";
else if(S_ISDIR(stat.st_mode))
  type = "directory";
else
  type = "other";
if((stat.st_mode & S_IRUSR))
  readok = "yes";
else
  readok = "no";
printf("type:%s,read:%s\n",type,readok);
}

1071536-20171217111022843-801888514.png

读取目录
opendir
readdir//返回inode和文件名,出错返回NULL,设置errno

共享文件

  • 描述符表:每个进程有一个独立的描述表,相当于本地文件
  • 文件表:打开文件的集合,所有的进程共享,包括当前的文件地址、引用计数及一个指向v-node中对应项的指针,相当于总表
  • v-node表:所有进程共享,包含stat中的大多数信息,相当于详情。
  • 多个描述符可通过不同的文件表表项来引用同一个文件。
  • 多进程时,子进程继承父进程,复制父进程的描述符表,在内核删除相应表项前,父子进程都要关闭描述符。

重定向

dup2函数,复制oldfd到newfd,若newfd已经打开,dup2会先关闭它。

标准I/O

练习题

10.1

  • 代码
#include "csapp.h"
int main()
{
int fd1,fd2;
fd1=open("foo.txt",O_RDONLY,0);
close(fd1);
fd2=open("baz.txt",O_RDONLY,0);
printf("fd2=%d\n",fd2);
exit(0);
}
  • 运行结果
    1071536-20171217103713170-206295908.png
  • 答案:Unix进程生命周期开始时,打开的描述符赋给了stdin(描述符0)、stdout((描述符1)和stderr(描述符2)。open函数总是返回最低的未打开的描述符,所以第一次调用open会返回描述符3,调用close函数会释放描述符3。最后对open的调用会返回描述符3,因此程序的翰出是“fd2=3”。当文件不存在是,输出“fd2=-1”

10.2

#include "csapp.h"
int main()
{
int fd1,fd2;
char c;
fd1=open("foobar.txt",O_RDONLY,0);
fd2=open("foobar.txt",O_RDONLY,0);
read(fd1,&c,1);
read(fd2,&c,1);
printf("c=%c\n",c);
exit(0);
}
  • 运行结果
    1071536-20171217143809421-2118897606.png
  • 答案:fd2读操作读取foobar.txt的第一个字节。

10.3

#include "csapp.h"
int main()
{
int fd;
char c;
fd = open("foobar.txt",O_RDONLY,0);
if(fork()==0)
{
read(fd,&c,1);
exit(0);
}
wait(NULL);
read(fd,&c,1);
printf("c=%c\n",c);
exit(0);
}
  • 运行结果
    1071536-20171217160256530-1421950003.png
  • 答案:描述符fd在父子进程中都指向同一个打开文件表表项,当子进程读取文件的第一个字节时,文件位置加1。因此,父进程会读取第二个字节,也就是o

10.4

如何用dup2将标准输入重定向到描述符5?
回答:dup2(5,0)

10.5

#include "csapp.h"
int main()
{
int fd1,fd2;
char c;
fd1=open("foobar.txt",O_RDONLY,0);
fd2=open("foobar.txt",O_RDONLY,0);
read(fd2,&c,1);
dup2(fd2,fd1);
read(fd1,&c,1);
printf("c=%c\n",c);
exit(0);
}
  • 答案:将fd1重定向到fd2,因此第二个fd1指的是fd2+1
  • 运行结果
    1071536-20171217172927139-1056342480.png
    回答:将fdl重定向到了fd2,输出实际上是c=o

教材学习中的问题和解决过程

问题1: 使用带缓存的RIO包的时候,为什么要先设定一个缓存区,再将其中的数据转移到缓冲区呢?

解决1:

带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。

也就是说,如果直接将数据写入缓存区就系统调用,与不带缓存的RIO是一样的,设定双重缓冲是为保证算法的效率,减少系统调用的次数。

问题2: 为什么使用一个static缓冲区就不是线程安全的?什么是线程安全?

解决2:

非线程安全是指多线程操作同一个对象可能会出现问题。而线程安全则是多线程操作同一个对象不会有问题。

我的理解:static缓冲区是静态的,就好像是静态变量一样,本地操作即单一线程调用的时候是不会出错的,但它不是可以共享的,也就是说其他线程调用的时候就可能出现问题。

代码学习中的问题和解决过程

问题1: 编译书p629代码时
1071536-20171217103741003-1372367729.png
解决1: 将csapp.c、csapp.h和cpfile.c编译在一起gcc csapp.h csapp.c cpfile.c -o cpfile -lpthread

问题2: 编译书p634代码时
1071536-20171217113902858-488112315.png
解决2: 使用man readdir发现有一个系统调用版本和c版本,使用man 3 readdir发现在csapp.h中缺少dirent.h头文件,加上之后编译通过,运行成功
1071536-20171217115009843-1152141276.png

代码托管

(statistics.sh脚本的运行结果截图)
1071536-20171119184821437-720283434.png

结对及互评

其他(感悟、思考等,可选)

认真学习I/O相关知识后,知道了输入输出的更多知识,也了解到系统在处理输入输出的时候会操作什么改变什么,希望对今后的学习有所帮助。

学习进度条

代码行数(新增/累积)博客量(新增/累积)学习时间(新增/累积)重要成长
目标5000行30篇400小时
第一周5/51/125/25
第二周236/2413/430/55
第三周169/4102/630/85
第四周169/4102/850/135
第五周1177/15872/1030/165
第六周1826/34132/1230/195
第七周977/43903/1530/225
第八周977/43902/1730/255
第九周977/43902/1930/285
第十周977/43900/1530/315
第十一周977/43902/2125/335
第十二周977/43900/2125/360
第十三周977/43902/2330/390
  • 计划学习时间:20小时
  • 实际学习时间:30小时

(有空多看看现代软件工程 课件
软件工程师能力自我评价表
)

参考资料

转载于:https://www.cnblogs.com/-zyl/p/8052702.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值