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设备。
一个Unix文件就是一个m字节的序列:
B0,B1,B2,B3,……,Bk,Bm-1
所有的I/O设备,如网络、磁盘和终端。都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口,称为UnixI/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行:
·打开文件
·改变当前的文件位置
·读写文件
·关闭文件
进程通过调用open函数来打开一个已存在的文件或者创建一个新文件的:R
open函数将filename转换成一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中没有打开的最小描述符。
Flags参数指明了进程打算如何访问这个文件:
Flags参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:
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传送的字节比应用程序要求的要少。
出现情况如下:
应用程序是通过分别调用系统函数 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
一种方法是用read函数来一次一个字节地从文件传送到用户存储器,检查每个字节来查找换行符。这种方法的问题就是效率不高,每次取文件中的一个字节都要求陷入内核。
一种更好的方法是调用一个包装函数(rio_readlineb),它从一个内部缓冲区拷贝一个文本行,当缓冲区变空时,会自动的调用read系统调用来重新填满缓冲区。
应用程序能够通过调用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 共享文件
内核用三个相关的数据结构来表示打开的文件:
10.7 i/o重定向 608
10.8 标准I/O
标准I/O库:
打开和关闭文件的函数fopen fclose
读和写字节的函数fgets fputs
复杂格式化的I/O函数scanf printf
流:一个流就是一个指向FILE类型结构的指针
每个ANSI C程序开始时都有三个打开流:
stdio 标准输出
stdout 标准输入
stderr 标准错误
FILE类型流是对文件描述符和流缓冲区的抽象
流缓冲区的目的:
使开销较高的Unix I/O系统调用的数量尽可能小。