20135306黄韧 附录A及第十章学习总结

附录A  错误处理

A.1 Unix系统中的错误处理

1.Unix风格的错误处理

if ((pid = wait(NULL)) < 0) {

  fprintf(stderr,”wait error:%s\n”,strerror(errno)));

  exit(0);

}

strerror函数返回某个errno值的文本描述。

2.Posix风格的错误处理

if ((retcode = pthread_create(&tid,NULL,thread,NULL))!= 0){

  fprintf(stderr,”pthread_create error:%s\n”,

             strerror(retcode));

  exit(0)

}

3.DNS风格的错误处理

if ((p = gethostbyname(name)== NULL){

fprintf(stderr, “gethostbyname error:%s\n:”,

           hstrerror(h_errno));

exit(0);

}

正如他们的名字表明的那样,unix_error、posix_error和dns_error函数报告Unix风格的错误、Posix风格的错误和DNX风格的错误,然后终止。包括app_error函数是为了方便报告应用错误。它只是简单地打印它的输入,然后终止。

A.2 错误处理包装函数

1Unix风格的错误处理包装函数

2Posix风格的错误处理包装函数

3DNS风格的错误处理包装函数

第十章  系统级I/O

输入/输出(I/O)是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。输入操作是从I/O设备拷贝数据到主存,而输出操作是从主存拷贝数据到I/O设备。

10.1 UnixI/O

一个Unix文件就是一个m字节的序列:

B0,B1,B2,B3,……,Bk,Bm-1

所有的I/O设备,如网络、磁盘和终端。都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口,称为UnixI/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行:

·打开文件

·改变当前的文件位置

·读写文件

·关闭文件

10.2打开和关闭文件

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

                       

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

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

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

 

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

  •  O_CREAT:如果文件不存在,就创建它的一个截断的(空)文件。
  •  O_TRUNC:      如果文件已经存在,就截断他。
  •  O_APPEN      D:在每次写操作前,设置文件位置到文件的结尾处。

 

Mode参数指定了新文件的访问权限位。

 

最后进程通过调用close函数关闭一个打开的文件。

 

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

10.3读和写文件

#include<unistd.h>

ssize_t read(int fd,void *buf,size_t n);/*返回:若成功则为读的字数,0为EOF,-1为出错*/

ssize_ write(int fd,const void *buf,size_t n);/*返回:若成功则为写的字节数,若出错则为-1*/

1.read函数

从描述符fd的当前文件位置拷贝n个字节到存储位置buf。

2.write函数

从存储器位置buf拷贝至多个n字节到描述符fd的当前文件位置

3.lseek函数

通过调用lseek函数,应用程序能够显式地修改当前文件的位置。

△不足值

read和write传送的字节比应用程序要求的要少。

出现情况如下:

  • 读时遇到EOF
  • 从终端读文本行      (返回不足值为文本行大小)
  • 读和写网络套字(socket)
    1. 内存缓冲约束和较长的网络延迟
    2. Unix管道

 

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

旁注:size_t是作为usigned int,而ssize_t是作为int。

在某些情况下,read和write传送的字节比应用程序要求的要少。出现这种情况的可能的原因有:

读时遇到EOF。假设该文件从当前文件位置开始只含有20个字节,而应用程序要求我们以50个字节的片进行读取,这样一来,这个read的返回的值是20,在此之后的read则返回0。
从终端读文本行。如果打开的文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小。
读和写socket。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会导致read和write返回不足值。

 

10. 4 用RIO包健壮地读写

RIO包会自动为你处理上文中所述的不足值。

RIO提供了两类不同的函数:

  •         无缓冲的输入输出函数。
  •         带缓冲的输入函数。

10.4.1 RIO的无缓冲的输入输出函数

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

 

 

注意:如果rio_readn和rio_writen函数被一个从应用信号处理程序的返回中断,那么每个函数都会手动地重启read或write。

 

10.4.2 rio的带缓冲的输入函数 600

  • 一个文本行就是一个由      换行符 结尾的ASCII码字符序列。在Unix系统中,换行符是‘\n’,与ASCII码换行符LF相同,数值为0x0a。假设我们要编写一个程序来计算文本文件中文本行的数量应该如何来实现呢?

一种方法是用read函数来一次一个字节地从文件传送到用户存储器,检查每个字节来查找换行符。这种方法的问题就是效率不高,每次取文件中的一个字节都要求陷入内核。
一种更好的方法是调用一个包装函数(rio_readlineb),它从一个内部缓冲区拷贝一个文本行,当缓冲区变空时,会自动的调用read系统调用来重新填满缓冲区。

  • 在带缓冲区的版本中,每打开一个描述符都会调用一次rio_readinitb函数,它将描述符fd和地址rp处的一个类型为rio_t的读缓冲区联系起来。
  • rio_readinitb函数从文件rp读取一个文本行(包括结尾的换行符),将它拷贝到存储器位置usrbuf,并且用空字符来结束这个文本行。
  • RIO读程序的核心是rio_read函数,rio_read函数可以看成是Unix read函数的带缓冲区的版本。当调用rio_read要求读取n个字节的时候,读缓冲区内有rp->rio_cnt个未读的字节。如果缓冲区为空的时候,就会调用read系统函数去填满缓冲区。这个read调用收到一个不足值的话并不是一个错误,只不过读缓冲区的是填充了一部分。
  • 一旦缓冲区非空,rio_read就从读缓冲区拷贝n和rp->rio_cnt中较小值个字节到用户缓冲区,并返回拷贝字节的数目。
  • 对于应用程序来说,rio_read和系统调用read有着相同的语义。出错时返回-1;在EOF时,返回0;如果要求的字节超过了读缓冲区内未读的字节的数目,它会返回一个不足值。rio_readlineb函数多次调用rio_read函数。每次调用都从读缓冲区返回一个字节,然后检查这个字节是否是结尾的换行符。
  • rio_readlineb函数最多读取(maxlen-1)个字节,余下的一个字节留给结尾的空字符。超过maxlen-1字节的文本行被截断,并用一个空字符结束。

10.5 读取文件元数据

应用程序能够通过调用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*/

stat以文件名作为输入,fstat以文件描述符作为输入

普通文件

某种类型的二进制或文本数据(对内核而言两者并没有什么区别)

目录文件

包含关于其他文件的信息

套接字

一种用来通过网络和其他进程通信的文件

st_size

包含了文件的字节数大小。

st_mode

编码了文件访问许可位和文件类型。
Unix提供的宏指令根据st_mode成员来确定文件的类型。

10. 6 共享文件

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

  •        描述符表。
  •        文件表。
  •        V-node表。

 

 

10.7 i/o重定向 608

  • Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。
  • I/O重定向的工作方式: 一种是使用dup2函数。
                   
  • dup2函数拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在拷贝oldfd之前关闭newfd。

 

10.8 标准I/O

标准I/O库:

打开和关闭文件的函数fopen fclose

读和写字节的函数fgets fputs

复杂格式化的I/O函数scanf printf

 

流:一个流就是一个指向FILE类型结构的指针

每个ANSI C程序开始时都有三个打开流:

stdio   标准输出

stdout  标准输入

stderr  标准错误

FILE类型流是对文件描述符流缓冲区的抽象

 

流缓冲区的目的:
使开销较高的Unix I/O系统调用的数量尽可能小。

重点

    1. 阅读教材,注意每个系统调用的参数、返回值,会查帮助文档
      完成课后练习(书中有参考答案)重点:10.1、10.2、10.3、10.4、10.5
    2. 重要命令:
      man -k key1 | grep key2| grep 2 : 根据关键字检索系统调用
      grep -nr XXX /usr/include :查找宏定义,类型定义

 

参考资料

    1. 教材:第十章,详细学习指导:http://group.cnblogs.com/topic/73069.html
    2. 课程资料:https://www.shiyanlou.com/courses/413 实验十,课程邀请码:W7FQKW4Y
    3. 教材中代码运行、思考一下,读代码的学习方法:http://www.cnblogs.com/rocedu/p/4837092.html。
posted @ 2015-11-08 19:19  黄伯伯  阅读(282)  评论(0编辑  收藏  举报