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. C# winform DataGridView操作 (转)

    C# DataGridView控件动态添加新行 DataGridView控件在实际应用中非常实用,特别需要表格显示数据时.可以静态绑定数据源,这样就自动为DataGridView控件添加相应的行.假如 ...

  2. access_token的获取方式

      获取Access Token $appid = ""; $appsecret = ""; $url = "https://api.weixin.q ...

  3. 最大乘积(Maximum Product,UVA 11059)

    Problem D - Maximum Product Time Limit: 1 second Given a sequence of integers S = {S1, S2, ..., Sn}, ...

  4. C语言学习之笔记

    第一章 概述 1. C语言的特点 ①语言简洁.紧凑,使用方便.灵活.共有32个关键字(也称保留字),9种控制语句. ②运算符丰富,共有34种运算符. ③数据结构丰富,数据类型有:整型.实型.字符型.数 ...

  5. KVO/KVC总结

    KVO/KVC总结       下面是根据网上文章的总结,方便查看. 在网上看别人的文章,了解KVC.KVO,有个kvo-kvc的例子,就是改变数组的内容(插入和删除),同步改变tableview中的 ...

  6. [151116 记录] 使用Python3.5爬取豆瓣电影Top250

    这一段时间,一直在折腾Python爬虫.已有的文件记录显示,折腾爬虫大概个把月了吧.但是断断续续,一会儿鼓捣python.一会学习sql儿.一会调试OpenCV,结果什么都没学好.前几天,终于耐下心来 ...

  7. asp.net web api long running task

    http://stackoverflow.com/questions/17577016/long-running-task-in-webapi http://blog.stephencleary.co ...

  8. MIT教授将网页开发整合为完整独立的程式语言Ur/Web

    MIT 的软体技术教授 Adam Chlipala 设计了新的 Ur/Web 程式语言,这是一个整合 HTML.CSS.XML.SQL 及 JavaScript 等网路标准的“完整独立”语言,强调快速 ...

  9. 如何把iOS代码编译为Android应用

    新闻 <iPhone 6/6 Plus中国销量曝光:单月销量650万>:据iSuppli Corp.中国研究总监王阳爆料,iPhone 6和iPhone 6 Plus在国内受欢迎的情况大大 ...

  10. java学习之数组(二)

    在上一节中我们讲到了数组的概念,定义,以及在内存当中的表现形式.那么这里我们来说一下,数组的另一种定义方式. 在上一篇当中我们规定是这个样子定义数组的, class ArrDemo { public ...