使用读写缓冲

进程缓冲区

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

  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 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也不会都导致内核的写磁盘操作,只有当内核缓冲区积累到一定量时才一次写入磁盘,有时突然断电,则内核缓冲区的数据会丢失。

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

posted @ 2011-12-20 10:58  张朝阳  阅读(1018)  评论(0编辑  收藏  举报