首页 > 代码库 > 第3章 文件I/O

第3章 文件I/O

概述

       本章主要讨论两个内容:1,不带缓冲的I/O;2,多进程共享文件

       Unix环境下,I/O设备抽象成了文件,所以将这两个内容放在一起讲解。让我们带着问题去学习,效果可能更好。

一 什么是文件描述符?

       从OS的角度,我们知道,I/O设备是共享设备,然而共享设备会带来进程之间的竞争,即临界资源的问题。比如,一台打印机,它是一个共享设备,在进程A进行打印的过程中,不能直接打印进程B想要打印的内容。怎么管理这个临界资源呢?Unix系统采用内核进行管理。如下图1:


                                                                                                                                                     图1 内核和进程

       对于内核而言,它用文件描述符引用一个打开的文件,并向对应的进程返回这个文件描述符。以后对于该文件的操作,均针对这个文件描述符进行。形象一点讲,文件相当于学生自己,文件描述符就相当于学号,显然学号比姓名更加易于操作和管理。

二 什么是缓冲?

        因为还没有讲标准I/O(带缓冲的I/O),所以这里就概要地讲解缓冲。

        提到缓冲,往往指的是应用级缓冲,而非内核当中的缓冲,因为不管是不带缓冲的I/O,还是标准I/O,在内核当中均有I/O缓冲区。如下图:


图2 缓冲

三 一个简单的不带缓冲I/O实例

 int readT(unsigned char *&T,const string name)
 {
	int fd = open(name.c_str(),O_RDWR);//打开文件

	if(fd == -1)//判断打开是否成功
	{
		fprintf(stderr,"error open text");
		return -1;
	}

	int n = lseek(fd,0,SEEK_END);//得出文件长度

	if(n == -1)//是否成功
	{
		fprintf(stderr,"error seek text");
		return -1;
	}


	T = new unsigned char[n];//为存储文件内容分配缓冲空间
	if(T == NULL)//空间分配是否成功
 	{
		fprintf(stderr,"error molloc space");
		return -1;
	} 
	lseek(fd,0,SEEK_SET);//定位到文件开始位置
	int cur = 0;
	int num = 0;
 	while(1)
 	{
		num = read(fd,T+cur,n-cur);//尽最大的努力读入文件
		if(num == -1)
		{
			fprintf(stderr,"error read text 43");
			return -1;
		}
		else if(num == 0)
			break;
		cur += num;
	}

	if(cur != n)
	{
		fprintf(stderr,"error in read text,cur is %d,n is %d\n",cur,n);
		return -1;
	} 

	close(fd);//关闭打开的文件
	return n;
 }
       这是我写的一个应用实例,从文件name中读入数据。其中
num = read(fd,T+cur,n-cur);//尽最大的努力读入文件
       这一行是非常好的一种方法。

四 怎么实现文件共享

4.1 内核打开文件数据结构

       存在这种情况1:进程A正在以读方式打开文件1,同时它又想用读的方式打开文件2。形象一点:我看一会儿《apue》,又想看《Tcp/Ip》,那我就同时把两本书放在手边,想看哪个看哪个,不会干扰到,计算机怎么实现呢?如图:


图3 打开文件的内核数据结构

4.2 两个进程共享同一文件

       存在这种情况2:进程A正在以读方式打开文件1,进程B也在以读方式打开文件1,而且两个进程互不干扰,怎样才能做到呢?举个形象的例子:小玉在看招聘海报,小伟也在看同一张招聘海报,显然他俩没必要争抢打架,各看各的就行了。那么操作系统内核是怎么处理这种情况的呢?也是类似的,如图:


图4 进程共享文件

        明白了文件共享的内核数据结构,那么就能清楚的知道文件重定向等是怎么实现的,具体的函数细节可以就是api的学习了,运用几次就会熟悉。

五 修改文件状态标志

        由四,我们可以看出,进程是通过文件表访问文件的,文件表中一个重要的项是文件状态标识,那么是不是只能在打开文件的时候设置它的值呢?显然不是,那就用到了fcntl函数了。

六 小结

        通过本章的学习,就能编写正确的不带缓冲的I/O。了解了文件共享实现的核心机制。当然,里面还涉及各个api的参数等等,很多细节没有讲到,但是我们了解了它的整个的设计思想,这些细节再加以练习就能很好掌握了。