上周已完成第十章 系统I/O的学习笔记,这周对主要对附录A 错误处理进行学习,并对第十章进行进一步理解。
附录A 错误处理
基于错误处理包装函数
思想:给定的基本级系统函数foo,我们有相同参数的、函数名大写的包装函数Foo;包装函数调用基本函数并检查错误。如果发现了错误,包装函数就终止进程并返回一条信息;当基本函数无误的时候,包装函数就返回调用者。(包装函数被封装在源文件csapp.c中)
A.1 Unix系统中的错误处理
系统基函数调用时用三种不同风格的返回错误:Unix风格和Posix风格和DNS风格。
-
unix风格
例如fork函数和外套函数,返回值既包括错误代码,又包括有用的结果。
if((pid = wait(NULL))<0) { fprintf(stderr,"wait error:%s ",strerror(errno)); //将errno设置为指向错误原因的代码 exit(0); }
-
Posix风格
例如pthread,函数只返回调用成功(0)或者失败(非0),任何有用的信息都返回在通过调用引用进来的参数中。
-
DNS风格
gethostbyname和gethostbyaddr检索NDS(域名系统)库;它们在错误的时候会返回NULL,并设置全局变量h_errno。
A.2 错误处理包装函数
-
unix风格
pid_t Wait(int *status) { pid_t pid; if(pid = wait(status)<0) unix_error("wait error"); return pid; }
-
Posix风格
void Pthread_detach(pthread_t tid) { int rc; if(rc=pthread_detach(tid) != 0) posix_error(rc,"Pthread_detach error"); }
-
DNS风格
struct hostent *Gethostbyname(const char *name) { struct hostname *p; if((p = gethostbyname(name)) == NULL) dns_error("Gethostbyname error"); return p; }
重要命令:
man -k key1 | grep key2| grep 2 : 根据关键字检索系统调用
grep -nr XXX /usr/include :查找宏定义,类型定义
系统调用:文件I/O 如何用Linux学习系统编程
who
多用户系统如何知道谁在使用系统?who
-
who能做什么,查阅一下:
-
who命令的工作流程是:
打开utmp文件,针对文件,读取一条记录,显示记录,关闭文件。
-
如何实现who?
代码思想:从UTMP_FILE文件中读取想要的信息到存储器中,然后再用标准输出函数打印到屏幕上,最后关闭文件。
#include <stdio.h> #include <stdlib.h> #include <utmp.h> #include <fcntl.h> #include <unistd.h> #define SHOWHOST int show_info( struct utmp *utbufp ) { printf("%-8.8s", utbufp->ut_name); printf(" "); printf("%-8.8s", utbufp->ut_line); printf(" "); printf("%10ld", utbufp->ut_time); printf(" "); #ifdef SHOWHOST printf("(%s)", utbufp->ut_host); #endif printf(" "); return 0; } int main() { struct utmp current_record; int utmpfd; int reclen = sizeof(current_record); /*打开UTMP_FILE读取信息,如果打开失败则输出失败信息。*/ if ( (utmpfd = open(UTMP_FILE, O_RDONLY)) == -1 ){ perror( UTMP_FILE ); exit(1); } /*读取信息到存储器中,reclen就是是读的字节数,然后再调用函数打印出来。*/ while ( read(utmpfd, ¤t_record, reclen) == reclen ) { show_info(¤t_record); close(utmpfd); return 0; }
cp
——复制文件
cp src dst
cp能干什么,查阅一下:
cp
#include <stdio.h>//标准输入输出
#include <stdlib.h>//C标准函数库
#include <unistd.h>//Unix类系统定义符号常量
#include <fcntl.h>//定义了很多宏和open,fcntl函数原型
#define BUFFERSIZE 4096//定义存储器容量
#define COPYMODE 0644//定义复制的长度
void oops(char *, char *);
int main(int argc, char *argv[])
{
int in_fd, out_fd, n_chars;//三个描述符值
char buf[BUFFERSIZE];//存储器位置
/*cp的参数有两个,分别是要复制的文件,和目的目录,这样一共应该是有三个操作数
所以要先检查argc的值是否为三,如果不是,返回标准错误*/
if (argc != 3) {
fprintf(stderr, "usage: %s source destination
", *argv);
exit(1);
}
/*检查cp的第一个参数,要复制的文件,用open打开,in_fd为open返回的描述符
如果返回-1,代表打开失败,提示错误*/
if ((in_fd = open(argv[1], O_RDONLY)) == -1)
oops("Cannot open ", argv[1]);
/*检查cp的第二个参数,复制的目的地址,用create在目的地址创建新文件,out_fd为open返回的描述符
如果返回-1,代表创建失败,提示错误*/
if ((out_fd = creat(argv[2], COPYMODE)) == -1)
oops("Cannot creat", argv[2]);
/*cp指令的动作就是读取一个文件的内容到存储器,在新的地址创建空白文件,再从存储器将内容写入新文件。
这里判断复制是否成功:
如果能读取顺利,而读取的位数和写的位数不同,是写错误;
如果读取失败,是读错误。*/
while ((n_chars = read(in_fd, buf, BUFFERSIZE)) > 0)
if (write(out_fd, buf, n_chars) != n_chars)
oops("Write error to ", argv[2]);
if (n_chars == -1)
oops("Read error from ", argv[1]);
/*这里执行的是关闭文件的动作,in_fd和out_fd两个文件描述符
所指向的文件只要有一个关闭错误,就提示关闭错误。*/
if (close(in_fd) == -1 || close(out_fd) == -1)
oops("Error closing files", "");
}
/*这个是用来输出错误信息的函数*/
void oops(char *s1, char *s2)
{
fprintf(stderr, "Error: %s ", s1);
perror(s2);//用来将上一个函数发生错误的原因输出到标准设备(stderr)
exit(1);
}
ls
——读取文件名和文件属性
ls能干什么:
-
ls -l
-
ls -a
-
ls -lu:最后访问时间
-
ls -s:以块为单位的文件大小
-
ls -t:按时间排序
-
ls -F:显示文件类型
ls -l能做什么?
显示文件信息:模式(文件类型file(1),访问控制),链接数,文件所有者,组,大小,最后修改时间,文件名
ls(1)
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
void do_ls(char []);
int main(int argc, char *argv[])
{
/*如果操作数只有1个,表明ls后面没有带参数,默认为当前目录,.表示当前目录。*/
if ( argc == 1 )
do_ls( "." );
/*如果ls后面有参数,就把参数读入argv中。*/
else
while ( --argc ){
printf("%s:
", *++argv );
do_ls( *argv );
}
return 0;
}
/*因为ls和dir功能相近,用dir来实现ls*/
void do_ls( char dirname[] )
{
DIR *dir_ptr;
struct dirent *direntp;
/*如果没有指向的那个地址,报错*/
if ( ( dir_ptr = opendir( dirname ) ) == NULL )
fprintf(stderr,"ls1: cannot open %s
", dirname);
else
{
/*递归的方式来读取*/
while ( ( direntp = readdir( dir_ptr ) ) != NULL )
printf("%s
", direntp->d_name );
closedir(dir_ptr);
}
}
ls2
ls2前半部分和ls1一样,所不同的只是多出来了一部分,用来显示文件的详细信息,比如用户名,群组名,大小,创建时间,读写权限等。
参考资料:
《深入理解操作系统》
闫佳歆博客关于实践代码部分