http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html

linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题。
包括:
    1、linux多/单进程与多/单文件对于文件流和描述符在使用时的关联情况及一些需要注意的问题。
    2、fork,vfork流缓冲等对文件操作的影响。
 
1、linux文件系统结构
首先补充一点基础知识,了解一下linux文件系统。如下图所示:
 
 
                                                    图1 磁盘,分区和文件系统
 应该明白,上图所示结构是硬盘中文件存放方式的一种逻辑表现形式,与进程无关。对于其中一些术语,见下面的解释。
i节点:包含文件/目录的几乎全部-适用于放置在硬盘上的,需要长久保存的信息。
例如:文件所有者,文件类型,i节点号(存放在目录块中),主次设备号,连接计数,访问/修改时间,IO块长,文件字节数等等。
可以用stat函数(#include )获取相关i节点信息信息。
 
 
2、简单的进程与文件关系
 
下面,我们了解一下单进程多文件以及多进程单文件间的关系,在不考虑fork(父子进程)的情况下,除了赋值语句给我们带来一些小麻烦以外,这个问题还是相当容易的。
 
2.1、一个进程同时打开多个文件:
 
                               图2 一个进程同时打开2个不同文件时内核数据结构
      其中,v节点我们几乎已经介绍过了,因为它除了包含i节点之外,自身的内容实在是不怎么多,重点看一下文件表吧。
      对于文件表,要注意,它并不是从在于硬盘中的东西,可以说,他是进程的一部分(可能是由操作系统内核负责维护,本人未考证(确实是由内核负责维护的,参见APUE->Section3.12),因为它到底是进程的还是内核的,对于我们要探讨的问题无关紧要)。文件表包括:
  文件状态标志:包含读,写,添写,同步和非阻塞等各种文件打开/当前状态。
  当前文件偏移量:记录文件指针位置
   V节点/I节点:文件类型和对此文件进行各种操作的函数的指针,这些信息都是在打开文件时候由磁盘读入内存的。
可用fcntl函数(#include )修改文件表内容。
 
 
2.2、多个无关联进程同时打开一个文件:
 
                    
                图3 两个进程同时打开同一个文件时内核数据结构
    此时,2个文件分别使用不同的文件表,这说明不同进程间文件状态标志,文件偏移量等都是独立的。但他们共用同一个v节点表。对于这种结构的特性,理解起来因该是个轻松的事情。
 
3、文件描述符或流的复制
对于文件描述符或流的复制,很多情况我们会采用赋值语句,下面了解一个赋值和dup的不同之处,dup函数复制文件描述符后的内核数据结构:
( 文件流和文件描述符的转换见这里
 
       
          图4 执行dup函数(#include )复制文件描述符后,内核数据结构。
    此时,2个fd文件标志同时使用同一文件表。
 
 
3.1dup与赋值语句用于文件描述符的区别
 为了了解dup与赋值语句用于文件描述符的区别,请看如下程序。  
程序描述:
    打开一个文件描述符,分别适用dup和赋值语句进行复制,复制之后,打印原始和被复制的文件描述符id,看看是否具有相同的值,然后关闭文件,测试关闭是否成功。
程序示例:
#include
#include
#include
#include
 
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
#include
 
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 )设置。
程序示例:
#include
#include
#include
#include
#include
 
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()结束进程),则此进程结束时可能会冲洗流缓冲区,并且关闭流,对于是否这样做,要取决于具体实现
  fork与vfork的区别见这里

[转] linux系统文件流、文件描述符与进程间关系详解的更多相关文章

  1. Linux中的文件描述符与打开文件之间的关系

    Linux中的文件描述符与打开文件之间的关系 导读 内核(kernel)利用文件描述符(file descriptor)来访问文件.文件描述符是非负整数.打开现存文件或新建文件时,内核会返回一个文件描 ...

  2. Linux exec与文件描述符

    看到好几篇文章讲述exec都是一知半解,所以我尽量说的清楚明白一些.本文首先讲述Linux文件描述符,然后是exec,最后举例说明exec I/O重定向及其用法. 概念:exec命令用于调用并执行指令 ...

  3. Linux Linux下最大文件描述符设置

    Linux下最大文件描述符设置 by:授客 QQ:1033553122 1.   系统可打开最大文件描述符设置 查看系统可打开最大文件描述符 # cat /proc/sys/fs/file-max 6 ...

  4. [svc]linux中的文件描述符(file descriptor)和文件

    linux中的文件描述符(file descriptor)和文件 linux为了实现一切皆文件的设计哲学,不仅将数据抽象成了文件,也将一切操作和资源抽象成了文件,比如说硬件设备,socket,磁盘,进 ...

  5. linux fcntl 对文件描述符控制

    linux fcntl 对文件描述符控制 linux fcntl 对文件描述符控制 linux fcntl 对文件描述符控制

  6. Linux中的文件描述符与打开文件之间的关系------------每天进步一点点系列

    http://blog.csdn.net/cywosp/article/details/38965239 1. 概述     在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件. ...

  7. (转)Linux中的文件描述符

    本文转自:http://blog.csdn.net/cywosp/article/details/38965239 作者:cywosp 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为 ...

  8. linux exec和文件描述符妙用技巧(转)

    最近在看<精通unix shell脚本编程>时,看到exec<$1 exec 1>$OUTFILE,一下看的我就蒙了.网上看了大半天,终于搞定,记录如下.对于 Linux 而言 ...

  9. Linux下的文件描述符

    文件描述符是一个简单的整数,用以标明每一个被进程所打开的文件和socket.第一个打开的文件是0,第二个是1,依此类推.Unix 操作系统通常给每个进程能打开的文件数量强加一个限制.更甚的是,unix ...

随机推荐

  1. WPF自定义DataGrid分页控件

    新建Custom Control,名:PagingDataGrid 打开工程下面的Themes\Generic.xaml xaml里面代码替换如下 <Style x:Key="{x:T ...

  2. Chrome rem bug

    遇到一个bug,发现chrome在初始化页面的时候,会错误的渲染rem单位,导致字体过大. 比如: 正常的应该是这样的: 原因是,为了使用rem单位,我们常常将 html 的font-size设置为6 ...

  3. QTP插入Output Value和插入CheckPoint,注意点

    1. 必须打开程序才能进行Output value和CheckPoint的插入. 2. 也有可能是对象获取不到,从新加载对象库. 提示如下图:

  4. Js冒泡事件和捕获事件

    js中冒泡事件和捕获事件: 冒泡事件:冒泡事件是从里向外,即是从被绑定元素开始一直向外到达页面的所有祖先元素都会被触发,这 一过程被称为事件冒泡.这个事件从原始元素开始一直冒泡到DOM树的最上层 捕获 ...

  5. div浮动框居于浏览器窗口中间

    代码先贴这里,随后再改 <script language="JavaScript"> document.getElementById('divCenter').styl ...

  6. MBR

    Mbr位于磁盘的0柱面,0磁头,1扇区. MBR       有三部分构成,主引导程序,硬盘分区表DPT和,硬盘的有效标志55AA.在512个字节的主引导扇区里. 主引导程序占446个字节,dpt占6 ...

  7. Django Admin 简单部署上线

    前言 打算为公司弄一个管理公用密码的平台,由于比较懒,就选择使用Django admin,默认的admin并不漂亮,于是我使用了这个django-suit插件来美化 如图: 是不是比原来的漂亮多了. ...

  8. vs2013update4 vs-mda-remote cordova真机测试ios 解决里面一个坑

    sudo npm install -g vs-mda-remote --user=你的用户名 此步骤为安装vs-mda-remote,如果安装成功 执行vs-mda-remote –secure fa ...

  9. NSString 练习

        //将“⽂文艺⻘青年”改成“213⻘青年”.   NSString *str = @"文艺青年";   NSString *str1 = [str stringByRepl ...

  10. Adobe Photoshop CS或者CC卸载不了怎么办?

    有木有没有遇到这个问题的同学?使用Adobe Creative Suite CleanerToo工具下载就好了~ 下载地址:http://pan.baidu.com/s/1pJ3aBsn