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

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

[email protected]:/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的区别见这里

时间: 2024-10-12 19:09:23

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

【Linux驱动】文件描述符以及相关知识

1.文件描述符 Linux操作系统中,几乎所有的设备都被抽象成为设备文件.因此,当我们想对设备进行操作的时候可以直接去操作其相应的设备文件.设备文件即是文件,要想对文件进行操作,无非就是:打开文件.关闭文件.写入数据.读出数据等,它们分别对应的函数有open(),close(),write(),read(),就以其中的open()函数做一个分析.open函数的作用是打开一个文件. (1)它的定义:int open( const char * pathname, int flags); int o

【翻译自mos文章】在unix/linux中使用文件描述符(File Descriptors)来找回被删掉的文件(数据文件or redo log)

在unix/linux中使用文件描述符(File Descriptors)来找回被删掉的文件(数据文件or redo log) 参考原文: Retrieve deleted files on Unix / Linux using File Descriptors (Doc ID 444749.1) 适用于: Oracle Database - Enterprise Edition - Version 8.1.7.0 to 11.2.0.3 [Release 8.1.7 to 11.2] Linu

Linux下基于文件描述符的文件操作(非缓冲)

1 文件描述符 内核为每个进程维护一个已打开文件的记录表(实现为结构体数组),文件描述符是一个较小的正整数(0-1023)(结构体数组下标),它代表记录表的一项,通过文件描述符和一组基于文件描述符的文件操作函数,就可以实现对文件的读.写.创建.删除等操作. 常用基于文件描述符的函数有open(打开).creat(创建).close(关闭).read(读取).write(写入).ftruncate(改变文件大小).lseek(定位).fsync(同步).fstat(获取文件状态).fchmod(权

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

1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件.链接文件和设备文件.文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符.程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误.如果此时去打开一个新的文件,它的文件描述符会是3.POSIX标准要求每次打开文件时(含socket)必须使用当前进程中最小可用的文件描述符号

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

转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件.链接文件和设备文件.文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符.程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误.如果此时去打开一个

(转)Linux中的文件描述符

本文转自:http://blog.csdn.net/cywosp/article/details/38965239 作者:cywosp 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件.链接文件和设备文件.文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符.程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误.如果

linux c 通过文件描述符获取文件名

在linux中每个被打开的文件都会在/proc/self/fd/目录中有记录,其中(/proc/self/fd/文件描述符号:这个文件是符号文件)的文件就是文件描述符所对应的文件. 而readlink可以取得符号连接所指的文件 函数原型: ssize_t readlink(const char *path, char *buf, size_t bufsiz); 函数说明: readlink()会将参数path的符号连接内容存到参数buf所指的内存空间,返回的内容不是以NULL作字符串结尾,但会将

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

最近在看<精通unix shell脚本编程>时,看到exec<$1 exec 1>$OUTFILE,一下看的我就蒙了.网上看了大半天,终于搞定,记录如下.对于 Linux 而言,所有对设备和文件的操作都使用文件描述符来进行的.文件描述符是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表.当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数.通常,一个进程启动时,都会打开 3 个文件:标

Linux中对文件描述符的操作(FD_ZERO、FD_SET、FD_CLR、FD_ISSET

在Linux中,内核利用文件描述符(File Descriptor)即文件句柄,来访问文件.文件描述符是非负整数.打开现存文件或新建文件时,内核会返回一个文件描述符.读写文件也需要使用文件描述符来指定待读写的文件.宏FD_ZERO.FD_SET.FD_CLR.FD_ISSET中"FD"即为file descriptor的缩写,下面来一一进行介绍. 首先介绍一个重要的结构体:fd_set,它会作为下面某些函数的参数而多次用到,fd_set可以理解为一个集合,这个集合中存放的是文件描述符(