使用读写缓冲

来源:互联网 发布:mac梦幻西游文件夹 编辑:程序博客网 时间:2024/05/18 20:50

进程缓冲区

我们慢慢来说明为什么要使用缓冲:

  1. 只有工作在管理员模式下,即只有内核代码才可以访问磁盘、终端、打印机等设备,还可以访问全部的内存。而用户模式下只访问特定区域的内存空间。
  2. 系统调用就是要从用户空间陷入内核空间,进入管理员模式。系统调用是很花时间的,原因:(1)执行权从用户代码转移到内核代码本身要进行数据传输;(2)管理员模式对应特殊的堆栈和内存环境,系统调用前必须建立好,系统调用后又要把堆栈和内存环境恢复成用户程序运行时的状态,这种切换需要消耗很多时间。
  3. 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 SHOWHOSTint 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也不会都导致内核的写磁盘操作,只有当内核缓冲区积累到一定量时才一次写入磁盘,有时突然断电,则内核缓冲区的数据会丢失。

当进程要求的数据块不在内核缓冲区时,内核把相应的数据加到请求数据列表中,然后挂起该进程,为其他进程服务。一段时间后(很短),内核把相应数据从磁盘读到内核缓冲区,然后再把数据复制到进程缓冲区,最后唤起被挂起的进程。