一、学习目的##

  1. 掌握系统编程和系统调用的概念
  2. 掌握系统编程错误处理的方式
  3. 掌握Unix/Linux系统级I/O:open close read write seek stat 4. 掌握RIO
  4. 掌握I/O重定向的方法

二、学习资源

  1. 教材:附录A,第10章《系统级I/O》
  2. 课程资料:https://www.shiyanlou.com/courses/413 实验11,课程邀请码:W7FQKW4Y
  3. 教材中代码运行、思考一下,读代码的学习方法见这。
    三、学习方法
  4. 进度很重要:必须跟上每周的进度,阅读,练习,问答,项目。我会认真对待每一位同学,请你不要因为困难半途而废。
  5. 问答很重要:遇到知识难点请多多提问,这是你的权利更是您对自己负责的义务。问答到博客园讨论小组:http://group.cnblogs.com/103791/
  6. 实践很重要:解决书中习题,实践书中实例,完成每周项目,才算真的消化了这本好书。通过实验楼环境或自己安装的虚拟机在实践中进行学习
  7. 实验报告很重要:详细记录你完成项目任务的思路,获得老师点评和帮助自己复习。学习完成后在博客园中(http://www.cnblogs.com/)把学习过程通过博客发表,博客标题“信息安全系统设计基础第九周学习总结”

三、学习任务

1 .阅读教材,注意每个系统调用的参数、返回值,会查帮助文档
完成课后练习(书中有参考答案)重点:10.1、10.2、10.3、10.4、10.5

2 .重要命令:
man -k key1 | grep key2| grep 2 : 根据关键字检索系统调用
grep -nr xxx /usr/include :查找宏定义,类型定义。

四、学习过程

第十章 系统级I/O

10.1 Unix I/O

1.打开文件

一个应用程序通过要求内核打开相应文件,来宣告它想要访问一个I/O设备。

Unix外壳创建时都有三个打开的文件

标准输入、标准输出、标准错误。

头文件<unistd.h> 定义了常量:
STDIN_FILENO 
STDOUT_FILENO
STDERR_FILENO

2.改变当前的文件位置

对于每个打开的文件,内核保持着一个文件位置k,初始为0。应用程序通过执行seek操作,显示的设置文件的当前位置为k.

3.读写文件

一个读操作就是从文件拷贝n>0个字节到储存器,从当前文件位置k开始,让后将k增加到k+n.

4.关闭文件

当应用完成了对文件的访问之后,它就通知内核关闭这个文件。

10.2打开和关闭文件

1、打开文件:

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

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

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

  • O_RDONLY: Reading only

  • O_WRONLY: Writing only

  • O_RDWR: Reading and writing

flags参数也可以是一个或者更多位掩码的或,提供一些额外的指示:

  • O_CREAT: 如果文件不存在,就创建它的一个截断的(空)文件。

  • O_TRUNC:如果文件已经存在,就截断它。

  • O_APPEND:再每次写操作前,设置文件位置到文件的结尾处。

2、关闭文件

#include <unistd.h> 
int close(int fd);

10.3 读和写文件

3、读文件

  • 应用程序是通过分别调用read和write函数来执行输入和输出的。

  • read函数从描述符为fd的当前文件位置拷贝最多n个字节到储存器位置buf。

  • 返回值-1表示一个错误,而返回值0表示EOF。否则,返回值表示的时实际传送的字节数量。

      include <unistd.h>
      ssize_t read(int fd, void *buf, size_t n);
      				//返回:若成功则为0,若出错则为-1.
    

4、写文件

write函数从储存器位置buf拷贝至多n个字节到描述符fd到当前位置。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t n);
					//返回:若成功则为写的字节数,若出错则为-1.
  • 读时遇到EOF

假设该文件从当前文件位置开始只含有20个字节,而应用程序要求我们以50个字节的片进行读取,这样一来,这个read的返回的值是20,在此之后的read则返回0.

  • 从终端读文本行

如果打开的文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小。(具体的含义可看我以前的文章,关于缓冲区的)

  • 读和写网络套接字(socket)

如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会导致read和write返回不足值。

10.4 用Rio包健壮地读写

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的带缓冲的输入函数

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

void rio_readinitb(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_n);
(若成功则返回传送成功的字节数,若EOF则为0,若出错 则为-1)
  • 打开每一个描述符都会调用一次rio _ readinitb函数,他将描述符fd和地址rp处的一个类型为rio _ t的读缓冲区联系起来。

  • 带缓冲的函数的调用不应该和无缓冲的rio _ readn交叉使用。

10.5 读取文件元数据

应用程序能通过调用stat和fstat函数,检索到关于文件的信息(元数据)。stat函数以一个文件名作为输入,fstat函数以文件描述符作为输入。

#include <unistd.h>
include <sys/stat.h>

int stat(const char *filename,struct stat *buf);
int fstat(int fd,struct stat *buf);
  • st _ size成员包含了文件的字节数大小。st _ mode成员则编码了文件访问许可位和文件类型。

  • 普通文件包括某种类型的二进制或文本数据。

  • 目录文件包含关于其他文件的信息。

  • 套接字是一种用来通过网络与其他进程通信的文件。

10.6 共享文件

  • 内核用三个相关的数据结构来表示其打开的文件。

  • 描述符表:表项由进程打开的文件描述符来索引的,每个打开的描述符表指向文件表中的一个表项,每个进程有其独立的描述符表。

  • 文件表:打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。包括:当前的文件位置、引用计数、以及一个指向v-node表中对应表项的指针。

  • v-node表:每个表项包含stat结构中的大多数信息,;包括st_mode和st_size成员,所有进程共享。

10.7 I/O重定向

I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。

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

#include <unistd.h>

int dup2(int oldfd,int newfd);

10.8 标准I/O

标准I/O库将一个打开的文件模型化为一个流,也就是一个指向FILE类型的结构的指针。

#include <stdio.h>
extern FILE *stdin;  /*标准输入,文件描述符为0*/
extern FILE *stdout;  /*标准输出,文件描述符为1*/
extern FILE *stderr;  /*标准错误,文件描述符为2*/

类型为file的流是对文件描述符和流缓冲区的抽象,目的是使开销较高的Unix I/O系统调用的数量尽可能小。

遇到问题

  • 第一次尝试使用Markdown不是很熟练,但是感觉很强大
  • 所谓"截断"是什么意思
  • csapp.h 不识别