zoukankan      html  css  js  c++  java
  • 信息安全系统设计基础第九周学习总结

    第十章 系统级I/O

    一、知识点总结

    (一)Unix I/O

    Unix I/O:简单低级的应用接口,使所有输入输出都以统一的方式执行。

    • 打开文件。一个应用程序通过要求内核打开相应文件,来宣告它想要访问一个I/O设备,内核返回描述符(小的非负整数)。每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)、标准错误(描述符为2)。
    • 改变当前的文件位置。每个打开的文件内核保持着一个文件位置k,初始为0。
    • 读写文件。读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始将k增加到k+n。
    • 关闭文件。将描述符恢复到可用的描述池中。

    (二)打开和关闭文件

    1. open:进程通过调用open函数来打开一个已存在的文件或创建一个新文件。

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    
    int open(char *filename, int flags, mode_t mode);
    返回:若成功则为新文件描述符,若出错则为-1。
    

    open函数将filename转换为一个文件描述符,并返回描述符数字。返回的描述符是在进程中当前没有打开的最小描述符。

    flags参数指明进程打算如何访问这个文件:

    • O_RDONLY:只读
    • O_WRONLY:只写
    • O_RDWR:可读可写

    以只读的方式打开一个已存在的文件:

    fd = Open("foo.txt, O_RDONLY, 0");
    

    打开一个已存在的文件,并在后面添加一些数据:

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

    2. close:进程通过调用close函数关闭一个打开的文件。

    #include<unistd.h>
    int close(int fd);
    返回:若成功则为0,若出错则为-1。
    

    关闭一个已关闭的描述符会出错。

    (三)读和写文件

    1. read和write函数:应用程序通过分别调用read和write函数来执行输入和输出。

    #include<unistd.h>
    ssize_t read(int fd, void *buf, size_t n);
    返回:若成功则为读的字节数,若EOF则为0,若出错则为-1。
    ssize_t write(int fd, const void *buf, size_t n);
    返回:若成功则为写的字节数,若出错则为-1。
    
    • read函数从描述符为fd的当前位置拷贝最多n个字节到存储器位置buf。
    • write函数从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置。

    注:"csapp.h"是《深入理解计算机系统》这本书写的头文件,运行代码时需要将该头文件下载并移到/usr/include中。

    2. lseek函数:应用程序调用lseek函数能显式地修改当前文件的位置。

    3. ssize_ t 和 size_t 的区别

    • size_t:read函数的输入参数,定义为unsigned int
    • ssize_t:read函数的返回值,定义为int。因为出错时必须返回-1。

    4. 不足值:read和write传送的字节比应用程序要求的少。出现原因如下:

    • 读取时遇到EOF
    • 从终端读取文本行
    • 读和写网络套接字

    实际上除了EOF,读取磁盘文件时不会遇到不足值,而且在写磁盘文件时也不会遇到不足值。

    (四)用RIO包健壮地读写

    1. RIO包:自动处理不足值,提供两类不同的函数

    • 无缓冲的输入输出函数:直接在存储器和文件之间传送数据。
    • 带缓冲的输入函数:高效地从文件中读取文本行和二进制数据。

    2. RIO的无缓冲的输入输出函数

    应用程序通过调用rio_ readn和rio_ writen函数可以在存储器和文件之间直接传送数据。

    #include"csapp.h"
    ssize_ t rio_ readn(int fd, void *usrbuf, size_t n);
    ssize_ t rio_ writen(int fd, void *usrbuf, size_t n);
    返回:若成功则为传送的字节数,若EOF则为0(只对rio_ readn而言),若出错则为-1。
    

    rio_ readn函数从描述符fd的当前文件位置最多传送n个字节到存储器位置usrbuf。rio_ writen函数从存储器位置usrbuf传送n个字节到描述符fd。

    3. RIO的带缓冲的输入函数

    一个文本行就是一个由换行符结尾的ASCII码字符序列。

    调用包装函数rio_ readlineb,从一个内部读缓冲区拷贝一个文本行,当缓冲区变空时会自动调用read重新填满缓冲区。

    #include"csapp.h"
    void rio_ readlineb(rio_t *rp, int fd);
    ssize_ t rio_ readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
    ssize_ t rio_ readnb(rio_t *rp, void *usrbuf, size_t n);
    返回:若成功则为读的字节数,若EOF则为0,若出错则为-1。
    

    • rio_ readlineb函数从文件rp读出一个文本行(包括结尾的换行符),将它拷贝到存储器位置usrbuf,并用空(零)字符结束这个文本行。rio_ readlineb函数最多读maxlen-1个字节,余下一个字符留给结尾的空字符。
    • rio_ readnb函数从文件rp最多读n个字节到存储器位置usrbuf。对同一描述符,对rio_ readlineb和rio_ readnb的调用可以任意交叉进行。

    rio_ readlineb和rio_ readnb函数:

    (五)读取文件元数据

    1. 应用程序能通过调用stat和fstat函数检索到关于文件的信息(元数据)。

    #include<unistd.h>
    #include<sys/stat.h>
    int  stat(const char *filename, struct stat *buf);
    int fstat(int fd, struct stat *buf);
    返回:若成功则为0,若出错则为-1。
    

    2. stat数据结构成员:st_ mode、st_ size……

    st_ size成员包含了文件的字节数大小,st_ mode成员编码了文件访问许可位和文件类型。

    (六)共享文件

    1. 内核用三个相关的数据结构来表示打开的文件:

    • 描述符表
    • 文件表
    • v-node表

    多个描述符可以通过不同的文件表表项来引用同一个文件。每个描述符都有它自己的文件位置,所以对不同描述符的读操作可以从文件的不同位置获取数据。

    调用fork后,子进程有一个父进程描述符表的副本,共享相同的文件位置。在内核删除相应文件表项之前,父子进程必须都关闭它们的描述符。

    (七)I/O重定向

    I/O重定向操作符:允许用户将磁盘文件和标准输入输出联系起来。I/O重定向可使用dup2函数工作。

    #include<unistd.h>
    int dup2(int oldfd, int newfd);
    返回:若成功则为非负的描述符,若出错则为-1。
    

    dup2函数拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在拷贝oldfd之前关闭newfd。

    (八)标准I/O

    标准I/O库提供打开和关闭文件的函数(fopen和fclose)、读和写字节的函数(fread和fwrite)、读和写字符串的函数(fgets和fputs),以及复杂的格式化的I/O函数(scanf和printf)。

    (九)综合

    1. 标准I/O流某种意义上而言是全双工的,因为程序能够在同一个流上执行输入和输出。

    2. 对流的限制和对套接字的限制:

    • 跟在输出函数之后的输入函数。如果中间没有插入对fflush、fseek、fsetpos或者rewind的调用,一个输入函数不能跟在一个输出函数之后。
    • 跟在输入函数之后的输出函数。如果中间没有插入对fseek、fsetpos或者rewind的调用,一个输出函数不能跟在一个输入函数之后,除非该输入函数遇到了一个EOF。

    • Unix I/O、标准I/O和RIO之间的关系

    二、参考资料

    • 《深入理解计算机系统》
    • 《嵌入式Linux应用程序开发标准教程》

    三、心得体会

      通过对本章系统级I/O的学习,我了解到Unix内核引出的应用接口I/O的基本功能和使用方法,它允许应用程序打开、关闭、读和写文件、提取文件的元数据,以及执行I/O重定向。同时,这一章还为我们随后学习网络编程和并发性奠定坚实的基础。不过我在运行本章代码时遇到了无法编译的情况,是因为“csapp.h”这个头文是教材编写的,C语言库中没有。我在博客园小组中也看到了这个提问,但在引入了头文件csapp.h后我仍是遇到了可以编译但是无法运行出结果这个问题,希望老师能为我们进一步解答一下。谢谢老师!

  • 相关阅读:
    Linux 多线程编程 实例 1
    面试题-链表反转c实现
    information_schema.TABLES
    mongodb遇到的错误
    MySQL优化的奇技淫巧之STRAIGHT_JOIN
    mongodb安装
    XtraBackup安装
    提高mysql千万级大数据SQL查询优化30条经验(Mysql索引优化注意)
    我用 TypeScript 语言的七个月
    Grunt之添加文件监视:Grunt-watch (已备份)
  • 原文地址:https://www.cnblogs.com/20135228guoyao/p/4947369.html
Copyright © 2011-2022 走看看