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的区别见这里

转载于:https://www.cnblogs.com/qiangxia/p/4243769.html

[转] linux系统文件流、文件描述符与进程间关系详解相关推荐

  1. Linux C:文件描述符、IO重定向、恢复标准输入输出

    目录 一.文件描述符 二.IO重定向 三.重定向回终端.伪终端 四.恢复标准输入输出 一.文件描述符 在Linux中,文件描述符是一个非负整数的数据类型.是FILE结构体中的一个成员属性. 每打开或者 ...

  2. linux文件描述符有什么用,linux上的文件描述符3有什么特别之处?

    我的工作,那将在Linux和Mac OS X上运行的服务器应用程序它是这样的:linux上的文件描述符3有什么特别之处? 启动主要应用 控制器进程的叉 调用lock_down()在控制过程中 再次叉终 ...

  3. Linux下利用文件描述符恢复的成功失败实验

    数据误删除是作为初级运维人员常常遇到的"低级错误",一些有经验的老手有时也在疲劳.不冷静的情况下"马失前蹄".一旦误删除数据文件,尽快采用影响最小.最迅速的手段 ...

  4. 文件描述符与inode的关系

    文件描述符 Linux中,文件描述符(File descriptor,fd),是表示指向文件的引用的抽象化概念,在形式上是一个非负整数,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符 ...

  5. linux下grep文件内容搜索工具及基本正则表达式详解

    linux下grep文件内容搜索工具及基本正则表达式详解 grep命令: 根据模式(文本字符和基本正则表达式的元字符组合而成之匹配条件)搜索文本, 并将符合模式的文本行显示出来. 格式:grep [选 ...

  6. linux复制文件scp命令,Linux 中的文件复制cp命令和scp命令详解

    Linux 中的文件复制cp命令和scp命令详解 在使用操作系统的使用过程中,常常需要复制文件到本地或者传输文件到其他电脑上,这时候用到两个命令cp和scp. cp命令用来复制文件或者目录.scp是s ...

  7. 【Linux篇】第九篇——基础IO(系统文件IO+文件描述符+重定向+文件系统+软硬链接)

    ⭐️这篇博客就要开始聊一聊Linux中基础IO相关知识,IO相信大家都不陌生,我们在C/C++中对文件进行读写的操作,也就是文件IO,这篇博客我也会带大家回顾一下.这篇博客还会介绍系统中的文件IO调用 ...

  8. Linux网络编程--文件描述符

    文件描述符 在Unix和Unix-like操作系统中,文件描述符(file descriptor, FD)是一个文件或者像pipe或者network socket等之类的输入/输出源的唯一标识. 文件 ...

  9. Linux——什么是文件描述符

    目录 前文 一,为什么有文件描述符 二,什么是文件描述符 2.1 文件操作接口 2.2 文件描述符 三,文件描述符的原理 四,文件描述符的分配规则 前文 本文主要是详解一下文件描述符,我们从1.为什么 ...

最新文章

  1. 【Linux 内核】Linux 操作系统结构 ( Linux 内核在操作系统中的层级 | Linux 内核子系统及关系 | 进程调度 | 内存管理 | 虚拟文件系统 | 网络管理 | 进程间通信 )
  2. Redis系列五:redis键管理和redis数据库管理
  3. DBI(i80)/DPI(RGB)/DSI【转】
  4. python怎么输入列表元素每个元素占一行_如何对列表的每个元素执行操作并将结果放在Python的新列表中?...
  5. Testing for SSL renegotiation
  6. 为什么shell脚本第一行要#!/bin/sh
  7. 堆的应用--并查集解决“擒贼先擒王”问题(JAVA)
  8. mysql sql 0填充_sql - MySQL - 如何用“0”填充前面的邮政编码?
  9. ORBSLAM论文翻译
  10. 背负着24-70之名 佳能新老镜皇横向对比(MTF对比说明)
  11. Python3中_和__的用途和区别
  12. py-faster-rcnn 中 shell脚本解读:./experiments/scripts/faster_rcnn_alt_opt.sh
  13. 在JSP中,点击网页某个按钮或者超链接执行某些需要确认的命令时,弹出确认框如下效果: 如果点击确定,则执行要操作的命令。如果点击取消则不执行。 实现步骤: 1:在jsp,或者html
  14. iPhone的解锁、越狱、激活、固件等等是什么意思,有什么分别?(转)
  15. 环信 “和未来有约”移动IM新时代分享沙龙——做最开源的即时通讯云平台
  16. java线程切换消耗时间_cpu性能消耗分析
  17. Meta Correction: Domain-aware Meta Loss Correction for Unsupervised Domain Adaptation in Semantic Se
  18. excel 进行二叉树_基础扩展 | 20. 建立二叉树
  19. 学习STM32 Flash存储 W25Q64 SPI总线存储模块进行读写数据
  20. Android大杂烩上篇

热门文章

  1. python实现yolo目标检测_从零开始PyTorch项目:YOLO v3目标检测实现
  2. c语言子程序return,c语言return返回到哪
  3. qt 多个模型如何显示在表格中_Qt MOOC系列教程 第五章第四节:QML中的C++模型
  4. iOS一些实用的技巧
  5. JPPhotoBrowserDemo--微信朋友圈浏览图片
  6. iOS - 实现映客首页 TabBar 和滑动隐藏 NavBar 和 TabBar
  7. HTML5跳转页面并传值以及localStorage的用法
  8. [python][jupyter notebook]之菜鸟安装[pyecharts]中Geo或Map显示问题
  9. Yii2.0 RESTful API 之版本控制
  10. MySQL练习题:常用函数