使用读写缓冲
进程缓冲区
我们慢慢来说明为什么要使用缓冲:
- 只有工作在管理员模式下,即只有内核代码才可以访问磁盘、终端、打印机等设备,还可以访问全部的内存。而用户模式下只访问特定区域的内存空间。
- 系统调用就是要从用户空间陷入内核空间,进入管理员模式。系统调用是很花时间的,原因:(1)执行权从用户代码转移到内核代码本身要进行数据传输;(2)管理员模式对应特殊的堆栈和内存环境,系统调用前必须建立好,系统调用后又要把堆栈和内存环境恢复成用户程序运行时的状态,这种切换需要消耗很多时间。
- read()和write()要访问磁盘,它们是系统调用,为节约时间应尽量减少read()和write()的调用次数,为达到此目的就要增大文件缓冲。
下面的代码是自己实现使用缓冲区的who命令实现。
#include<stdio.h> #include<utmp.h> #include<string.h> #include<stdlib.h> #include<time.h> #include<sys/types.h> #include<fcntl.h> #define BUFSIZE 16 //缓冲区中存放16条记录 #define NULLUT ((struct utmp*)NULL) //定义一个空指针,不指向任何实际的对象或函数 #define UTSIZE (sizeof(struct utmp)) #define SHOWHOST int my_utmp_open(char *filename); struct utmp* my_utmp_next(); int my_utmp_reload(); void my_utmp_close(); void show_info(struct utmp*); static char utmpbuf[BUFSIZE*UTSIZE]; //缓冲区大小 static int num_recs; //缓冲区中的数据个数 static int cur_rec; //缓冲区中已经使用的数据个数 static int fd_utmp=-1; //utmp文件的描述符 int my_utmp_open(char *filename){ fd_utmp=open(filename,O_RDONLY); cur_rec=num_recs=0; return fd_utmp; } struct utmp* my_utmp_next(){ struct utmp *result; if(fd_utmp==-1) //文件打开失败 return NULLUT; if(cur_rec==num_recs && my_utmp_reload()==0) //缓冲区已读完,就从文件向缓冲区reload新内容。如果从文件中已读不出更多的内容,则返回空指针 return NULLUT; result=(struct utmp*)&utmpbuf[cur_rec*UTSIZE]; cur_rec++; return result; } int my_utmp_reload(){ int num=read(fd_utmp,utmpbuf,BUFSIZE*UTSIZE); //最多读取16条记录 num_recs=num/UTSIZE; //实际读取的记录数 cur_rec=0; return num_recs; } void my_utmp_close(){ if(fd_utmp!=-1) close(fd_utmp); } int main(){ struct utmp *record; if(my_utmp_open("/var/run/utmp")==-1){ //打开文件 perror("fopen"); exit(0); } while((record=my_utmp_next())!=NULLUT){ //循环读取记录(结构体) show_info(record); } my_utmp_close(); //关闭文件 return 0; } void show_info(struct utmp *buf){ if(buf->ut_type!=USER_PROCESS) return; printf("%-8s ",buf->ut_name); //输出用户名 printf("%-12s ",buf->ut_line); //终端设备名 time_t timelong=buf->ut_time; struct tm *localnow=localtime(&timelong); printf("%d-%d-%d %d:%d ",localnow->tm_year+1990,localnow->tm_mon+1,localnow->tm_mday,localnow->tm_hour,localnow->tm_min); //登录时间 #ifdef SHOWHOST printf("(%s)",buf->ut_host); #endif printf("\n"); }
内核缓冲区
管理员模式和用户模式之间切换需要时间,相比之下,磁盘I/O消耗的时间更多,因此内核也利用缓冲技术来提高对磁盘的访问速度。
因此read函数并不是把数据直接从磁盘读到进程缓冲区,而是从内核缓冲区读到进程缓冲区;同样write也不会都导致内核的写磁盘操作,只有当内核缓冲区积累到一定量时才一次写入磁盘,有时突然断电,则内核缓冲区的数据会丢失。
当进程要求的数据块不在内核缓冲区时,内核把相应的数据加到请求数据列表中,然后挂起该进程,为其他进程服务。一段时间后(很短),内核把相应数据从磁盘读到内核缓冲区,然后再把数据复制到进程缓冲区,最后唤起被挂起的进程。
本文来自博客园,作者:张朝阳,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/2294326.html