linux系统文件流、缓冲及描述符与进程间关系详解

bo.cui 2009-03-09 06:49:00
linux系统文件流、缓冲及描述符与进程间关系详解



linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题。

包括:

1、linux多/单进程与多/单文件对于文件流和描述符在使用时的关联情况及一些需要注意的问题。

2、fork,vfork流缓冲等对文件操作的影响。



1、linux文件系统结构

首先补充一点基础知识,了解一下linux文件系统。如下图所示:





图1 磁盘,分区和文件系统

应该明白,上图所示结构是硬盘中文件存放方式的一种逻辑表现形式,与进程无关。对于其中一些术语,见下面的解释。

i节点:包含文件/目录的几乎全部-适用于放置在硬盘上的,需要长久保存的信息。

例如:文件所有者,文件类型,i节点号(存放在目录块中),主次设备号,连接计数,访问/修改时间,IO块长,文件字节数等等。

可以用stat函数(#include <sys/stat.h> )获取相关i节点信息信息。





2、简单的进程与文件关系



下面,我们了解一下单进程多文件以及多进程单文件间的关系,在不考虑fork(父子进程)的情况下,除了赋值语句给我们带来一些小麻烦以外,这个问题还是相当容易的。



2.1、一个进程同时打开多个文件:




图2 一个进程同时打开2个不同文件时内核数据结构

其中,v节点我们几乎已经介绍过了,因为它除了包含i节点之外,自身的内容实在是不怎么多,重点看一下文件表吧。

对于文件表,要注意,它并不是从在于硬盘中的东西,可以说,他是进程的一部分(可能是由操作系统内核负责维护,本人未考证,因为它到底是进程的还是内核的,对于我们要探讨的问题无关紧要)。文件表包括:

文件状态标志:包含读,写,添写,同步和非阻塞等各种文件打开/当前状态。

V节点:文件类型和对此文件进行各种操作的函数的指针,这些信息都是在打开文件时候由磁盘读入内存的。

I节点:同上节所述。

可用fcntl函数(#include <fcntl.h> )修改文件表内容。





2.2、多个无关联进程同时打开一个文件:






图3 两个进程同时打开同一个文件时内核数据结构

此时,2个文件分别使用不同的文件表,这说明不同进程间文件状态标志,文件偏移量等都是独立的。但他们共用同一个v节点表。对于这种结构的特性,理解起来因该是个轻松的事情。



3、文件描述符或流的复制

对于文件描述符或流的复制,很多情况我们会采用赋值语句,下面了解一个赋值和dup的不同之处,dup函数复制文件描述符后的内核数据结构:






图4 执行dup函数(#include <unistd.h> )复制文件描述符后,内核数据结构。

此时,2个fd文件标志同时使用同一文件表。

3.1、dup与赋值语句用于文件描述符的区别

为了了解dup与赋值语句用于文件描述符的区别,请看如下程序。

程序描述:

打开一个文件描述符,分别适用dup和赋值语句进行复制,复制之后,打印原始和被复制的文件描述符id,看看是否具有相同的值,然后关闭文件,测试关闭是否成功。

程序示例:

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>



int sys_err(char *str)

{

puts(str);

exit(0);

}



int main(void)

{

int p,q;



if((p=open( "c_fid.c ", O_RDONLY)) == -1)

sys_err( "open error ");

q = dup(p);

puts( "dup: ");

printf( "file p,q fd is:%d %d\n ", q, p);

printf( "close file p ok?: %d\n ", close(p));

printf( "close file q ok?: %d\n ", close(q));



if((p=open( "c_fid.c ", O_RDONLY)) == -1)

sys_err( "open error ");

q = p;

puts( "=: ");

printf( "file p,q fd is:%d %d\n ", q, p);

printf( "close file p ok?: %d\n ", close(p));

printf( "close file q ok?: %d\n ", close(q));



return 0;

}



程序运行结果:

dup:

file p,q fd is:4 3 //文件p,q使用不同的文件描述符

close file p ok?: 0

close file q ok?: 0 //文件关闭成功

=:

file p,q fd is:3 3 //简单复制

close file p ok?: 0

close file q ok?: -1//关闭失败,原因是此描述符已经被关闭了



由此证明,dup是产生一个新的文件描述符id和指针,但是他们共用文件表,效果如图4,这时,关闭一个文件描述符,另外一个仍旧可用,文件表并不会被释放。而赋值语句不同,它只是简单的在另外一个变量中记录原始文件指针等,2个变量的文件描述符相同,进程表项中并不产生新的项目。



3.2、赋值语句复制标准流。

例:

#include <stdio.h>

#include <stdlib.h>



int sys_err(char *str)

{

puts(str);

exit(0);

}



int main(void)

{

FILE *p,*q;



if((p=fopen( "c_fid.c ", "r ")) == NULL)

sys_err( "open error ");

q = p;

printf( "FILE p,q fd is:%d %d\n ", fileno(q),fileno(p));

printf( "close file p ok?: %d\n ", fclose(p));

printf( "close file q ok?: %d\n ", fclose(q));



return 0;

}



程序执行结果:

FILE p,q fd is:3 3 //2个流共用同一个文件描述符

close file p ok?: 0

*** glibc detected ***//2次关闭引起错误,造成程序崩溃。

…………





4、 引入fork后进程与文件关系以及流缓冲对文件操作的影响

4.1 fork





图5 使用fork之后,父进程、子进程之间对打开文件的共享

使用fork后,子进程会继承父进程所打开的文件表,此时,父子进程使用同一个文件表(可见,上面猜测文件表由内核维护因该是正确的,因为文件表并没有被复制),这说明2个进程将共用文件状态,文件偏移量等信息。如果使用close关闭一个进程中的文件描述符,那么另一个进程中,此描述符仍然有效,相应文件表也不会被释放。

需要注意的是,在使用c标准io进行文件读写时,先结束的进程会将缓冲区内数据也计入文件偏移量的长度,对于相应文件缓冲区类型和长度,可以使用setbuf,setvbuf(#include <stdio.h> )设置。

程序示例:

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <time.h>



int main(void)

{

int pid;

FILE *p;

char buff[20]={0};

int test=-2;



if((p=fopen( "/media/lin_space/soft/netbeans-6.5-ml-cpp-linux.sh ", "r ")) == NULL)

{//文件大于4096字节

puts( "open error. ");exit(0);

}



if((pid=fork()) < 0)

{

puts( "fork error ");

exit(0);

}

else if(pid == 0)

{

sleep(2);

test = ftell(p);//返回当前偏移量

printf( "\nchild - ftell is: %d\n ", test);

if((buff[0] = fgetc(p)) != EOF)

printf( "child - fgetc is: %c\n ", buff[0]);

else

puts( "child - fgetc error\n ");

test = ftell(p);

printf( "child - ftell is: %d\n ", test);

}

else

{

test = ftell(p);

printf( "\nparent - ftell is: %d\n ", test);

if((buff[0] = fgetc(p)) != EOF)

printf( "parent - fgetc is: %c\n ", buff[0]);

else

puts( "parent - fgetc error\n ");

test = ftell(p);

printf( "parent - ftell is: %d\n ", test);

}



printf( "parent and child - close file ok?: %d\n ", fclose(p));

return 0;

}



程序执行结果:

parent - ftell is: 0

parent - fgetc is: #

parent - ftell is: 1

parent and child - close file ok?: 0

freec@freec-laptop:/media/lin_space/summa/apue/unit8$ //父进程结束

child - ftell is: 4096 //子进程文件偏移量为4096,原因是文件缓冲类型为全缓冲,缓冲区大小为4096

child - fgetc is: a

child - ftell is: 4097

parent and child - close file ok?: 0 //文件关闭成功



4.2 vfork

而对于vfork,虽然子进程运行于父进程的空间,但是子进程却拥有自己的进程表项(包含进程pid,fd标志,文件指针等),所以,在其中一个进程结束后,另外一个进程的文件描述符依旧有效,子进程得到的是父进程文件描述符的副本。

但是vfork对于标准流,情况则不同,一个进程结束后(如果不是使用_exit()结束进程),则此进程结束时可能会冲洗流缓冲区,并且关闭流,对于是否这样做,要取决于具体实现。
...全文
641 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
f_x_p0324 2011-08-31
  • 打赏
  • 举报
回复
写的比较详细。不错
sailorone 2009-04-10
  • 打赏
  • 举报
回复
飘过,带走一点知识。谢谢
bo.cui 2009-03-11
  • 打赏
  • 举报
回复
.... csdn人少了??
whuyotc 2009-03-11
  • 打赏
  • 举报
回复
先mark下~
沙漠里的海豚 2009-03-11
  • 打赏
  • 举报
回复
好贴 支持
xuguod20042576 2009-03-11
  • 打赏
  • 举报
回复
mark
pengzhixi 2009-03-11
  • 打赏
  • 举报
回复
mark
bo.cui 2009-03-11
  • 打赏
  • 举报
回复
。。。。。 没人关注~~~

5555555
其实这个看似和unix高级环境编成差不多~~
但是,整个文章都是对那本书这个部分的扩展~~~
yutaooo 2009-03-10
  • 打赏
  • 举报
回复

8错。留念。
lengsehuoyan 2009-03-10
  • 打赏
  • 举报
回复
回来顶贴。
我这网络不延时,哈哈。
wangsnow 2009-03-10
  • 打赏
  • 举报
回复
标记
largep 2009-03-10
  • 打赏
  • 举报
回复
友情up
dongpy 2009-03-09
  • 打赏
  • 举报
回复
支持
fuleicf 2009-03-09
  • 打赏
  • 举报
回复
写的不错,写的很好。(虽然本人看不懂。。。汗)!努力吧,朋友!
lengsehuoyan 2009-03-09
  • 打赏
  • 举报
回复
有人的人都这样。留名,咱屋网速卡。真是不行啊。
Sco_field 2009-03-09
  • 打赏
  • 举报
回复
先up再看- -
bo.cui 2009-03-09
  • 打赏
  • 举报
回复
联系:
freecunix@gmail.com
yangch_nhcmo 2009-03-09
  • 打赏
  • 举报
回复
mark
bo.cui 2009-03-09
  • 打赏
  • 举报
回复
声明:
1、此文章绝对原创,望转载著名。
2、本人刚刚毕业,5年c编成经验,代码量20W+,求c语言程序员(linux/unix平台)工作(北京地区)。
bo.cui 2009-03-09
  • 打赏
  • 举报
回复
声明:
1、此文章绝对原创,望转载著名。
2、本人刚刚毕业,5年c编成经验,代码量20W+,求c语言程序员(linux/unix平台)工作(北京地区)。
加载更多回复(5)
Re: 《 Linux磁盘与文件系统管理命令 》   ---------------------------------------内容提要: 01/16)命令fdisk           :磁盘分区工具02/16)命令partprobe  :更新内核的硬盘分区表信息(即分区即刻生效)03/16)命令 tune2fs     :调整 ext2/ext3/ext4 文件系统参数04/16)命令 parted       :磁盘分区工具(大小通吃)05/16)命令 mkfs          :创建Linux文件系统06/16)命令 dumpe2fs :导出ext2/ext3/ext4文件系统信息07/16)命令 resize2fs   :调整ext2/ext3/ext4文件系统大小08/16)命令 fsck           :检查并修复Linux文件系统09/16)命令 dd             :转换或复制文件10/16)命令 mount       :挂载文件系统11/16)命令 umount     :卸载文件系统12/16)命令 df              :报告文件系统磁盘空的使用情况13/16)命令 mkswap    :创建交换分区14/16)命令 swapon     :激活交换分区15/16)命令 swapoff     :关闭交换分区16/16)命令 sync           :刷新文件系统缓冲区17/17)附录                   :NFS 网络文件服务器到安装;客户端的挂载 -t nfs;及新分区的权限测试  本人在教学和实战过程中发现,即便是有一定运维经验的人,可能已经能够搭建一定复杂度的Linux架构,但是在来来回回的具体操作中,还是体现出CLI(命令界面)功底不够扎实,甚至操作的非常‘拙’、处处露‘怯’。 对一个士兵来说,枪就是他的武器,对于一个程序员来说,各种library(工具库)就是他的武器;而对于Linux运维人员来说,无疑命令行工具CLI(命令界面)就是他们的武器;高手和小白之的差距往往就体现在对于这些“武器”的掌握和熟练程度上。有时候一个参数就能够解决的事情,小白们可能要写一个复杂的Shell脚本才能搞定,这就是对CLI(命令界面)没有理解参悟透彻导致。 研磨每一个命令就是擦拭手中的作战武器,平时不保养不理解,等到作战的时候,一定不能够将手中的武器发挥到最好,所以我们要平心、静气和专注,甘坐冷板凳一段时,才能练就一身非凡的内功! 本教程从实战出发,结合当下行或最新的Linux(v6/7/8 版本)同时演示,将命令行结合到解决企业实战问题中来,体现出教学注重实战的务实精神,希望从事或未来从事运维的同学,能够认真仔细的学完Linux核心命令的整套课程。 本课程系列将逐步推出,看看我教学的进度和您学习的步伐,孰占鳌头! 注:关于教学环境搭建,可以参考本人其它课程系列,本教学中就不再赘述! 《参透 VMware 桌面级虚拟化》 《在虚拟机中安装模版机(包括应用软件等)》 《SecureCRT 连接 GNS3/Linux 的安全精密工具》 

69,380

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧