1. 掌握系统编程和系统调用的概念
2. 掌握系统编程错误处理的方式
3. 掌握Unix/Linux系统级I/O:open close read write seek stat
4. 掌握RIO
5. 掌握I/O重定向的方法
1. 教材:附录A,第10章《系统级I/O》
2. 课程资料:https://www.shiyanlou.com/courses/413 实验11,课程邀请码:W7FQKW4Y
3. 教材中代码运行、思考一下,读代码的学习方法见这。
1. 进度很重要:必须跟上每周的进度,阅读,练习,问答,项目。我会认真对待每一位同学,请你不要因为困难半途而废。
2. 问答很重要:遇到知识难点请多多提问,这是你的权利更是您对自己负责的义务。问答到博客园讨论小组:http://group.cnblogs.com/103791/
3. 实践很重要:解决书中习题,实践书中实例,完成每周项目,才算真的消化了这本好书。通过实验楼环境或自己安装的虚拟机在实践中进行学习
4. 实验报告很重要:详细记录你完成项目任务的思路,获得老师点评和帮助自己复习。学习完成后在博客园中(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 :查找宏定义,类型定义
3. 考核:教材内容,练习题把数据变换一下
4. 实验:需要动手的到实验楼中练习一下
(可选):教材第八章《异常控制流》
输入/输出(I/O)是在主存和外部设备之间拷贝数据的过程。
这一节涉及到操作系统的基本抽象之一——文件。也就是说,所有的I/O设备都被模型化为文件,而所有的输入输出都被当做对相应文件的读/写。相关的执行动作如下:
应用程序向内核发出请求→要求内核打开相应的文件→内核返回文件描述符
文件描述符:一个小的非负整数,用来在后续对此文件的所有操作中标识这个文件。有三个已经被指定了的如下:
标准输入——0(STDIN_FILENO)
标准输出——1(STDOUT_FILENO)
标准错误——2(STDERR_FILENO)
括号中是常量表示形式,使用时需要加头文件
也就是说,在Unix生命周期一开始,0、1、2就被占用,以后的open只能从3开始——习题10.1.
在UNIX下还有stdin,stdout,stderr表示同样的含义。
二者的主要区别为:
1.数据类型不同,前者为int类型,后者为FILE*;
2.STDIN_FILENO主要用在read(),write()等中,后者主要用在fread(),fwrite()以f开头。
通常,读,写操作都从当前文件偏移量处开始(也就是文件位置),并使偏移量增加所读写的字节数,可以理解为光标所在的位置。
当打开一个文件的最初时候文件的偏移量为0.
通过seek操作,可以显示的设置文件的当前位置为k。
读操作就是从文件拷贝n>0个字节到存储器,并且改变文件当前位置。(如果当前位置是k,则改变为k+n)
这里有一个一直以来的理解上的误区:文件结尾处没有明确的EOF信号,是当文件当前位置的数值超过了文件大小时,会处罚一个称为end-of-file的条件,能够被应用程序检测到,这就是所谓的EOF信号。
写操作是从存储器拷贝n>0个字节到一个文件,然后更新当前文件位置。
应用通知内核关闭文件→内核释放文件打开时的数据结构→恢复描述符→释放存储器资源。
(1)函数定义:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(char *filename, int flags, mode_t mode);
(2)参数解析:
flags:指明进程打算如何访问这个文件,可以取的值见下:
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:文件不存在,就创建新文件
O_TRUNC:如果文件存在,就截断它
O_APPEND:写操作前设置文件位置到结尾处
这些值可以用或连接起来。mode:指定了新文件的访问权限位,符号名称如下:
(1)函数定义:
#include <unistd.h>
int close(int fd);
(2)参数解析:
关闭一个已经关闭的描述符会出错
(1)函数原型:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t n);
(2)参数解析:
(1)函数原型:
#include <unistd.h>
ssize_t write(int fd, void *buf, size_t n);
(2)参数解析:
需要注意的是,read和write在正常情况下返回值是实际传送的字节数量。
不足值指在某些情况下,read和write传送的字节比应用程序要求的要少,原因如下:
RIO,Robust I/O,针对的出现不足值的问题。
这些函数的作用是直接在存储器和文件之间传送数据,常适用于网络和二进制数据之间。
#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);
参数:
返回值:
rio_readn成功则返回传送的字节数,EOF为0(一个不足值),出错为-1
rio_writen成功则返回传送的字节数,出错为-1,没有不足值。
可以高效的从文件中读取文本行和二进制数据。
一个概念:一个文本行就是一个由换行符结尾的ASCII码字符序列。
范例:如何统计文本文件中文本行的数量——通过计算换行符。需要用到的函数:
#include "csapp.h"
void rio_readinitb(rio_t *rp, int fd);//将描述符fd和地址rp处的一个类型为rio_t的读缓存区联系起来。
ssize_t rio_readlineb(rio_t *rp,void *usrbuf, size_t maxlen);//从文件rp中读出一个文本行,包括换行符,拷贝到存储器位置usrbuf,并用空字符结束这个文本行。最多赌到maxlen-1个字节,最后一个给结尾的空字符。
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);//从文件rp中读取最多n个字符到存储器位置usrbuf中。
成功则返回传送的字节数,EOF为0,出错为-1。
【课本代码】
#include "csapp.h"
int main(int argc, char **argv)
{
int n;
rio_t rio;
char buf[MAXLINE];
Rio_readinitb(&rio, STDIN_FILENO);//连接标准输入和rio地址
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) //当成功返回时,将rio中的内容拷贝到存储器位置buf中,最多读maxline-1
Rio_writen(STDOUT_FILENO, buf, n);//把存储器位置中的数据拷贝到标注输出中。
exit(0);
}
先连接标准输入和地址rio,再根据返回值判断是否成功将rio中的一行内容拷贝到了buf中,如果是再把这一行拷贝到标准输出中,即可实现一次一行的从标准输入拷贝一个文本文件到标准输出。
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
}
由代码可以看出,rio_t数据结构的组成部分有文件描述符,缓存区中还没有读过的数值,下一个需要读的字节,文本行。在rio_readinitb函数中,创建了一个读缓存区,把文件描述符赋值,还没有读过的数值是0,下一个要读的字节就是文本行的起始,这代表这个读缓存区是空的。
RIO读程序的核心是rio_read函数
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
int cnt;
while (rp->rio_cnt <= 0) { /* 如果缓存区为空,调用read填满它 */
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf));
if (rp->rio_cnt < 0) {
if (errno != EINTR) /* 出错返回-1*/
return -1;
}
else if (rp->rio_cnt == 0) /* EOF返回0 */
return 0;
else
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
}
/* 一旦缓存区非空,就从读缓存区拷贝n和rp->rio_cnt中较小值个字节到用户缓存区,并且返回拷贝的字节数 */
cnt = n;
if (rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf, rp->rio_bufptr, cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* 调用read填充 */
else
return -1; /* 错误,返回-1 */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* 返回成功传送的字节数*/
}
原文:http://www.cnblogs.com/20135213lhj/p/4948500.html