第十章 系统级I/O
第九周(11.02-11.08):
估算学习时间:共8小时
读书:4
代码:1
作业:1
博客:2
实际学习时间:共12小时
读书:4
代码:2
作业:2
博客:4
一、学习目标
- 掌握系统编程和系统调用的概念
- 掌握系统编程错误处理的方式
- 掌握Unix/Linux系统级I/O:open close read write seek stat
- 掌握RIO
- 掌握I/O重定向的方法
二、学习资源 - 教材:附录A,第10章《系统级I/O》
- 课程资料:https://www.shiyanlou.com/courses/413 实验11,课程邀请码:W7FQKW4Y
- 教材中代码运行、思考一下,读代码的学习方法见这。
三、学习方法
- 进度很重要:必须跟上每周的进度,阅读,练习,问答,项目。我会认真对待每一位同学,请你不要因为困难半途而废。
- 问答很重要:遇到知识难点请多多提问,这是你的权利更是您对自己负责的义务。问答到博客园讨论小组:http://group.cnblogs.com/103791/
- 实践很重要:解决书中习题,实践书中实例,完成每周项目,才算真的消化了这本好书。通过实验楼环境或自己安装的虚拟机在实践中进行学习
- 实验报告很重要:详细记录你完成项目任务的思路,获得老师点评和帮助自己复习。学习完成后在博客园中(http://www.cnblogs.com/)把学习过程通过博客发表,博客标题“信息安全系统设计基础第九周学习总结”
四、学习任务
- 阅读教材,注意每个系统调用的参数、返回值,会查帮助文档
完成课后练习(书中有参考答案)重点: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是在主存和外部设备(如磁盘,网络和终端)之间拷贝数据的过程。
- 输入就是从I/O设备拷贝数据到主存,而输出就是从主存拷贝数据到I/O设备。
- 所有语言的运行时系统都提供执行I/O的较高级别的工具。例如,ANSI C提供标准I/O库,包含像printf和scanf这样执行带缓冲区的I/O函数。C++语言用它的重载操作符<<(输出)和>>(输入)提供了类似的功能。在UNIX系统中,是通过使用由内核提供的系统级UnixI/O函数来实现这些比较高级的I/O函数的。
- 学习Unix I/O原因:
- 了解Unix I/O将帮助你理解其他的系统概念;
- 有时你除了使用Unix I/O外别无选择。
10.1 unix i/o
所有的I/O设备,如网络、磁盘和终端,都被模型化为文件,而所有的输入和输出都被当做对相应的文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Unix内核引出一个简单、低级的的应用接口,称为Unⅸ I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行:
- 打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。Unⅸ外壳创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2)。头文件可用来代替显式的描述符值。
- 改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显式地设置文件的当前位置为k。
- 读写文件。一个读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。给定一个大小为m字节的文件,当k>=m时执行读操作会触发―个称为end-of-file(EOF)的条件,应用程序能检测到这个条件。在文件结尾处处并没有明确的“EOF”符号。
- 关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的存储器资源。
10.2 打开和关闭文件
进程是通过调用open函数来打开一个已存在的文件或者创建一个新文件
- flags参数表示进程打算如何访问这个文件,它的值包括:
O_RDONLY
O_WRONLY
O_RDWR
- flags参数也可以是一个或者更多位掩码的或,提供一些额外的指示:
O_CREAT
O_TRUNC:如果文件已经存在,就截断它。
O_APPEND
- mode参数指定了新文件的访问权限位。符号名字如下图。作为上下文的一部分,每个进程都有一个umask它是通过调用umask函数来设置的。当进程通过带某个mode参数的open函数调用来创建一个新文件时,文件的访问权限位被设置为mode&umask。
10.3 读和写文件
应用程序是通过分别调用系统函数 read和write函数来执行输入和输出的。
在某些情况下,read和write传送的字节比应用程序要求的要少。出现这种情况的可能的原因有:
读时遇到EOF。假设该文件从当前文件位置开始只含有20个字节,而应用程序要求我们以50个字节的片进行读取,这样一来,这个read的返回的值是20,在此之后的read则返回0。
从终端读文本行。如果打开的文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小。
读和写socket。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会导致read和write返回不足值。
10.4 用rio包健壮地读写
RIO提供了两类不同的函数:
- 无缓冲的输入输出函数
这些函数直接 在存储器和文件之间传送数据,没有应用级缓冲。 - 带缓冲的输入函数
内容缓存在应用级缓冲区内。
10.4.1 rio的无缓冲的输入输出函数
rio_readn函数从描述符fd的当前文件位置最多传送n个字节到存储器位置usrbuf。类似的rio_writen函数从位置usrbuf传送n个字节到描述符fd。rio_readn函数在遇到EOF时只能返回一个不足值。rio_writen函数绝不会返回不足值。
注意:如果rio_readn和rio_writen函数被一个从应用信号处理程序的返回中断,那么每个函数都会手动地重启read或write。
10.4.2 rio的带缓冲的输入函数
一个文本行就是一个由 换行符 结尾的ASCII码字符序列。在Unix系统中,换行符是‘ ’,与ASCII码换行符LF相同,数值为0x0a。假设我们要编写一个程序来计算文本文件中文本行的数量应该如何来实现呢?
一种方法是用read函数来一次一个字节地从文件传送到用户存储器,检查每个字节来查找换行符。这种方法的问题就是效率不高,每次取文件中的一个字节都要求陷入内核。
一种更好的方法是调用一个包装函数(rio_readlineb),它从一个内部缓冲区拷贝一个文本行,当缓冲区变空时,会自动的调用read系统调用来重新填满缓冲区。
10.5 读取文件元数据
应用程序能够通过调用stat和fstat函数,检索到关于文件的信息。
stat函数结构
st_size成员包含了文件的字节数大小。st_mode成员则编码了文件访问许可位和文件类型。Unix识别大量不同的文件类型。普通文件包括某种类型的二进制或文本数据。对于内核而言,文本文件和二进制文件毫无区别。
目录文件包含关于其他文件的信息。套接字是一种用来通过网络与其他进程通信的文件。Unix提供的宏指令根据st_mode成员来确定文件的类型。
10.6 共享文件
内核用三个相关数据结构来表示打开的文件
描述符表
文件表
v-node表
10.7 i/o重定向
Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。
10.8 标准i/o
ANSI C定义了一组高级输入输出函数,成为标准I/O库,为程序员提供了Unix I/O的较高级别的替代。这个库(libc)提供了打开和关闭文件的函数(fopen和fclose)、读和写字节的函数(fread和fwrite)、读和写字符串的函数(fgets和fputs)、以及复杂的格式化I/O函数(printf和scanf)。
标准I/O库将一个打开的文件模型化为一个流。对于程序员而言,一个流就是一个指向FILE类型的结构的指针。每个ANSI C程序开始时都有三个打开的流stdin、stdout和stderr,分别对应于标准输入、标准输出和标准错误:
#include<stdio.h>
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
10.9 综合:我该使用哪些i/o函数
这一章讨论过的各种I/O包
标准I/O流,从某种意义上来说是全双工的,因为程序能够在同一个流上执行输入和输出。
建议在网络套接字上不要使用标准I/O函数来进行输入和输出。而要使用健壮的RIO函数。
10.10 小结
- Unix提供了少量的系统级函数,它们允许应用程序打开、关闭、读和写文件,提取文件的元数据,以及执行I/O重定向。Unix的读和写操作会出现不足值,应用程序必须能正确地预计和处理这种情况。应用程序不应直接调用unⅸ I/O函数,而应该使用RIO包,RIO包通过反复执行读写操作,直到传送完所有的请求数据,自动处理不足值。
- Unix内核使用三个相关的数据结构来表示打开的文件。描述符表中的表项指向打开文件中的表项,而打开文件表中的表项又指向v-node表中的表项,每个进程都有它自己单独的描述符表,而所有的进程共享同一个打开文件表和v-node表,理解这些结构的一般组成就能使我们清楚地理解文件共享和I/O重定向。
- 标准I/O库是基于Unix I/O实现的,并提供了一组强大的高级I/O例程,对于大多数应用程序而言,标准I/O更简单,是优于Unix I/O的选择。然而,因为对标准I/O和网络文件的一些相互不兼容的限制,Unix I/O比标准I/O更适用于网络应用程序。
七、遇到的问题及解决
八、其他
怎样学习系统编程(利用Linux学习Linux编程)
1 分析实用程序
/bin, /usr/bin, /usr/local/bin
学习使用工具,分析工具,了解功能和原理
2 学习系统调用
函数和系统调用本质上都是函数。不同的库,不同的头文件
分析需要哪些系统调用,学习系统调用的使用方法:参数?返回值?
3 编程实现
利用上面的原理和一组协同工作的系统调用,自己编程实现使用程序的功能
或积极主动抄代码,学习那些系统调用是协同工作的
C语言的学习可以参考这个思路,实现标准库
伴随我们学习经常要问的三个问题:
能做什么?
如何实现?
自己如何编写?
比如说Linux操作系统
能做什么?
登录-运行程序-注销
如何登录?如何获取程序名?如何运行程序?
目录操作:目录树:ls,cd,pwd,mkdir,rmdir
目录树如何组织的?目录存在哪?什么是当前目录?
文件操作:cat, more/less/pg, cp, mv, lpr
文件数据如何存储?如何复制,移动,改名?文件名存在哪?
文件访问控制:ugo
如何设置?
从OS的角度看: 网络游戏
通信
协作
网络访问
bc/dc---->网络 B/S
标准I/O 学习示例:more
能做什么?用一下
使用方法
more filename
more < filename
command | more
如何实现?
伪代码:
如果用户没有输入文件名
输出键盘输入内容
对整个文件
显示24行
提示用户选择 空格 回车 q
如果用户选择空格
显示下24行
如果用户选择回车
显示下一行
如果用户选择 q
退出
函数调用:
fopen/fclose
stdin stdout stderr
fgets/fputs
getchar/putchar
printf(" 33[7m more? 33[m")
自己编写:
如何判断读到文件尾部了?
feof
fgets的返回值是0
fseek 与文件读写位置
问题:
选择都要回车
文字和反白more一起上滚
百分比
适应窗口
重定向: who | more, ls |more
/dev/tty(流与文件)
系统调用:文件I/O 如何用Linux学习系统编程
多用户系统如何知道谁在使用系统?who
who能做什么?使用一下
man who (info who; who --help)
如何实现who?
阅读ManPages
man man
搜索ManPages
man -k
man -k XXX | grep -i YYY
阅读头文件
参阅see also
man who
man -k utmp
man 5 utmp
utmp.h
grep -i XXX -nr /usr/include
UTMP_FILE
struct utmp
define ut_name ut_user
ut_line
define ut_time ut_tv.tv_sec
伪代码:
打开utmp文件
针对文件
读取一条记录
显示记录
关闭文件
哪些系统调用?
man -k file | grep -i read
see also
open/read/close
struct utmp s;
read( fd, &s, sizeof(s));
man
cf fopen fread fclose
自己编写who:
read: return value
/var/run/utmp
var/run/unp
问题:空白记录
ut_type
时间转换
man -k time | grep -i transform (trans)
asctime(3)
ctime(3)
localtime(3)
man -k time | grep -i convert
无
查找关键字:至少汉语有想法
转换
时间格式
man -k time | grep -i format
strftime(3)
如何复制文件?cp
cp能干什么?
cp src dst
如何实现cp?
open/close
open(fff, O_WRONLY|O_CREAT,0644)
creat(fff, 0644);
n = read(fd, buf, BUFSIZE);//#define BUFSIZE 4096
write(fd, buf, n);
伪代码:
打开源文件
创建目标文件
针对源文件
把源文件读入缓冲区
把缓冲区内容写入目标文件
关闭源文件和目标文件
自己编写cp:
系统调用错误处理:
错误种类:errno
显示错误消息:perror(3)
错误处理封装函数
fork()
if((pid = fork()) < 0)
unix_error("fork error");
return pid;
学有余力者:
ac last cat head tail od dd
read/write可以读取文件内容,如何读取文件名和文件属性?ls
ls能干什么?
ls
ls -l
ls -a
ls -lu:最后访问时间
ls -s:以块为单位的文件大小
ls -t:按时间排序
ls -F:显示文件类型
列出文件目录
显示文件信息
如何列出文件目录?
如何显示文件属性?
如何判断一个名字是文件还是目录?
文件树
文件和目录被组织成目录树(tree),节点是目录或者文件
目录是一种特殊文件,文件内容就是目录和文件的名字,与utmp类似
与文件不同,目录不会为空
如何实现ls?
man -k direct
man -k direct | grep -i read
man -k direct | grep -i entry
readdir
see also: opendir closedir
总结:代码模式
fopen fread/fwrite fclose
open read/write close
opendir readdir closedir
伪代码:
打开目录文件
针对目录文件
读取目录条目
显示文件名
关闭文件目录文件
自己编写ls:
传入参数,显示任意目录: ls /tmp; ls /; ls /dev
分栏
.开头隐含文件:-a
排序:man -k sort
代码库:DRY
xxx_uitl.h xxx_util.c
出错处理,参数处理,常用结构...
xxx_datastru.h xxx_datastru.c
链表,双向链表
树,图。。。
xxx_algorith.h xxx_algorthm.c
ls -l能做什么?
显示文件信息:模式(文件类型file(1),访问控制),链接数,文件所有者,组,大小,最后修改时间,文件名
如何实现ls -l?
man -k file | grep -i infomation (status, property, attribute)
stat(1) stat(2)
struct stat
最后修改时间:st_mtime ctime
模式:st_mode
type ugs rwx rwx rwx
八进制掩码
struct stat info;
if ((info.st_mode & 0170000) == 0040000)
printf("这是一个目录
");
文件类型宏
define S_ISDIR(m) (((m)&0170000)) == (0040000))
struct stat info;
if (S_ISDIR(info.st_mode))
printf("这是一个目录
");
访问控制属性?比如说本人能不能写?如何定义宏?
S_ISREG(mod) '-'
S_ISDIR(mode) 'd'
S_ISCHR(mode) 'c'
S_ISBLK(mode)) 'b'
UID/GID
/etc/passwd
getpwuid(3)
struct passwd
/etc/group
getgrgid(3)
struct group
自己编写ls -l: ls2.c
stat(2) struct stat
getpwuid(3) struct passwd
getgrgid(3) struct group
记录计数
按文件名排序 qsort
文件属性修改与open/creat
open/creat umask(1) umask(2)
chmod(1) chmod(2)
chown(1) chgrp(1) chown(2)
touch(1) utime(2)
mv(1) rename(2)
学有余力:tree chmod file chown chgrp finger touch
tree ls-R
文件包含数据,目录是文件列表,目录构成目录树。
文件在目录中什么意思?
用户的主目录(home)是什么意思?
文件系统(解决的问题参考教材)
路径:绝对路径,相对路径。pwd
用户眼中的文件系统
目录和文件:构成目录树
目录命令:
. .. /
mkdir
rmdir
-p
mv
cd
pwd
...
练习:构建一棵目录树
文件命令:
cp
-r
cat
mv
rm
-r
ln
ls
...
目录树命令
tree
ls -R
chmod -R
du
find:注意与grep的区别
...
目录树的深度??
思考:
文件系统形成目录树,操作系统提供相应的操作命令来协同工作
目录是什么?
如何知道文件所处的目录?
切换目录(cd)是什么意思?
pwd如何自知道你处的目录?