Linux文件描述符与C FILE之间的关系
2015-11-28 15:24
686 查看
1. linux文件描述符
对于linux而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。
通常,一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这3个文件分别对应文件描述符为0、1和2(宏STD_FILENO、STDOUT_FILENO和STDERR_FILENO)。
例如下面的程序,打开/home/shenlan/hello.c文件,如果此目录下没有hello.c文件,程序自动创建,程序中返回的文件描述符为3。因为进程启动时,打开了标准输入、标准输出和标准出错处理三个文件,因此返回的文件描述符为3。
2. 内核中的实现细节:
linux用一个数组来管理进程打开的文件的file对象,数组中的每一个元素都存放了一个指向进程所打开的文件的file对象。数组的下标用来访问文件。linux把数组元素的下标叫做该数组元素所对应文件的文件描述符,这个描述符就是系统对文件的标识,也叫做文件描述符数组。(注:内核可以通过系统调用函数dup(
)、dup2( )和fctl(
)将数组中的多个元素指向同一个文件的file对象。即同一个文件可以有多个文件描述符。)文件描述符表在files_struct结构中定义,为structfile*fd_array[NR_OPEN_DEFAULT];进程与文件系统及其所打开文件的关系如图所示:
3.进程打开一个文件的具体流程
进程通过系统调用open( )来打开一个文件,实质上是获得一个文件描述符,以便进程通过文件描述符为连接对文件进行其他操作。进程打开文件时,会为该文件创建一个file对象,并把该file对象存入进程打开文件表中(文件描述符数组),进而确定了所打开文件的文件描述符。
open( )操作在内核里通过sys_open(
)实现的,sys_open( )将创建文件的dentry、inode和file对象,并在file_struct结构体的进程打开文件表fd_array[NR_OPEN_DEFAULT]中寻找一个空闲表项,然后返回这个表项的下标(索引),即文件描述符。创建文件的file对象时,将file对象的f_op指向了所属文件系统的操作函数集file_operations,而该函数集又来自具体文件的i节点,于是虚拟文件系统就与实际文件系统的操作衔接起来了。
4. C标准库中的FILE结构和文件描述符、files_struct和file结构之间的关系
早期的C标准库中,FILE在stdio.h中定义;Turbo
C中,参见谭浩强的《C程序设计》,FILE结构体中包含成员fd,即文件描述符。在glibc-2.9中,stdio.h中定义FILE为_IO_FILE类型(typedef
struct _IO_FILE FILE;),而_IO_FILE在/glibc-2.9/libio/libio.h中定义,如下所示,亦可以在安装的Ubuntu系统的/usr/include/stdio.h中找到:
当然,上面的结构体太复杂,我们只关心需要的部分-文件描述符,但是在上面的结构体中,我们并没有发现与文件描述符相关的诸如fd成员变量。此时,类型为int的_fileno结构体成员引起了我们的注意,但是不能确定其为文件描述符。因此写个程序测试是最好的办法,可以用以下的代码测试:
因此,_fileno成员即为操作系统打开文件返回的句柄(windows系统)或文件描述符。深入学习可以阅读人民邮电出版社《C标准库》。当然还可以阅读/glibc-2.9/manual/io.txti文件。Linux中,文件的描述符分配是从小到大逐个查询文件描述符是否已经使用,然后再分配,也可以写程序测试。
file在/include/linux/fs.h中定义,从进程的角度来看,文件就是一个file结构的实例,也常常叫file对象。file对象是进程调用open打开一个文件,linux虚拟文件系统根据进程请求创建的一个file对象。进程通过该文件的file结构了解和操作文件。
NR_OPEN_DEFAULTBITS_PER_LONG。以x86体系为例,BITS_PER_LONG 在/include/asm-x86/types.h中定义,32位CPU为32,64位CPU为64
对于linux而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。
通常,一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这3个文件分别对应文件描述符为0、1和2(宏STD_FILENO、STDOUT_FILENO和STDERR_FILENO)。
例如下面的程序,打开/home/shenlan/hello.c文件,如果此目录下没有hello.c文件,程序自动创建,程序中返回的文件描述符为3。因为进程启动时,打开了标准输入、标准输出和标准出错处理三个文件,因此返回的文件描述符为3。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> int main() { int fd; if((fd = open("/home/shenlan/hello.c", O_CREAT|O_WRONLY|O_TRUNC, 0611)) < 0){ perror("openfile hello.c error!\n"); exit(1); } else { printf("openfile hello.c success:%d\n",fd); } if(close(fd) < 0){ perror("closefile hello.c error!\n"); exit(1); } else printf("closefile hello.c success!\n"); exit(0); }执行结果如下图所示:
2. 内核中的实现细节:
linux用一个数组来管理进程打开的文件的file对象,数组中的每一个元素都存放了一个指向进程所打开的文件的file对象。数组的下标用来访问文件。linux把数组元素的下标叫做该数组元素所对应文件的文件描述符,这个描述符就是系统对文件的标识,也叫做文件描述符数组。(注:内核可以通过系统调用函数dup(
)、dup2( )和fctl(
)将数组中的多个元素指向同一个文件的file对象。即同一个文件可以有多个文件描述符。)文件描述符表在files_struct结构中定义,为structfile*fd_array[NR_OPEN_DEFAULT];进程与文件系统及其所打开文件的关系如图所示:
3.进程打开一个文件的具体流程
进程通过系统调用open( )来打开一个文件,实质上是获得一个文件描述符,以便进程通过文件描述符为连接对文件进行其他操作。进程打开文件时,会为该文件创建一个file对象,并把该file对象存入进程打开文件表中(文件描述符数组),进而确定了所打开文件的文件描述符。
open( )操作在内核里通过sys_open(
)实现的,sys_open( )将创建文件的dentry、inode和file对象,并在file_struct结构体的进程打开文件表fd_array[NR_OPEN_DEFAULT]中寻找一个空闲表项,然后返回这个表项的下标(索引),即文件描述符。创建文件的file对象时,将file对象的f_op指向了所属文件系统的操作函数集file_operations,而该函数集又来自具体文件的i节点,于是虚拟文件系统就与实际文件系统的操作衔接起来了。
4. C标准库中的FILE结构和文件描述符、files_struct和file结构之间的关系
早期的C标准库中,FILE在stdio.h中定义;Turbo
C中,参见谭浩强的《C程序设计》,FILE结构体中包含成员fd,即文件描述符。在glibc-2.9中,stdio.h中定义FILE为_IO_FILE类型(typedef
struct _IO_FILE FILE;),而_IO_FILE在/glibc-2.9/libio/libio.h中定义,如下所示,亦可以在安装的Ubuntu系统的/usr/include/stdio.h中找到:
struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; restis flags. */ #define _IO_file_flags _flags /* The following pointerscorrespond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_endfields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields areused to support backing up and undo. */ char *_IO_save_base; /*Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backuparea */ char *_IO_save_end; /*Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /*This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number ofpbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
当然,上面的结构体太复杂,我们只关心需要的部分-文件描述符,但是在上面的结构体中,我们并没有发现与文件描述符相关的诸如fd成员变量。此时,类型为int的_fileno结构体成员引起了我们的注意,但是不能确定其为文件描述符。因此写个程序测试是最好的办法,可以用以下的代码测试:
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main( ) { char buf[50] = {"ILOVE this game!"}; FILE *myfile; myfile = fopen("2.txt","w+"); if(!myfile){ printf("error:openfile failed!\n"); } printf("The openedfile's descriptor is %d\n",myfile->_fileno); if(write(myfile->_fileno,buf,50) < 0){ perror("error:writefile failed!\n"); exit(1); }else{ printf("writefile successed!\n"); } exit(0); }程序中,使用fopen函数以读写打开2.txt文件,如果不存在2.txt文件,则创建此文件。并将其返回的FILE指针myfile。使用printf向标准终端打印出myfile->_fileno的值,并将myfile->_fileno作为文件描述符传递给write系统调用,向打开的文件写入缓冲区数据。然后使用cat命令查看2.txt的内容。执行的结果如图所示。_fileno的值为3,因为标准输入、输出、出错为0、1、2
因此,_fileno成员即为操作系统打开文件返回的句柄(windows系统)或文件描述符。深入学习可以阅读人民邮电出版社《C标准库》。当然还可以阅读/glibc-2.9/manual/io.txti文件。Linux中,文件的描述符分配是从小到大逐个查询文件描述符是否已经使用,然后再分配,也可以写程序测试。
file在/include/linux/fs.h中定义,从进程的角度来看,文件就是一个file结构的实例,也常常叫file对象。file对象是进程调用open打开一个文件,linux虚拟文件系统根据进程请求创建的一个file对象。进程通过该文件的file结构了解和操作文件。
struct file { /* * fu_list becomes invalid after file_free iscalled and queued via * fu_rcuhead for RCU freeing */ union{ struct list_head fu_list; struct rcu_head fu_rcuhead; }f_u; struct path f_path; #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt const struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; struct file_ra_state f_ra; u64 f_version; #ifdef CONFIG_SECURITY void *f_security; #endif /*needed for tty driver, and maybe others */ void *private_data; #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all thehooks to this file */ struct list_head f_ep_links; spinlock_t f_ep_lock; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; #ifdef CONFIG_DEBUG_WRITECOUNT unsigned long f_mnt_write_state; #endif };文件描述符数组中存放了一个进程所打开的所有文件。文件描述符数组包含在进程打开的文件表files_struct结构中。在/include/linux/fdtable.h中定义,为一个指向file类型的指针数组---fd_array[NR_OPEN_DEFAULT],其中NR_OPEN_DEFAULT也在fdtable.h中定义,这是一个和具体的CPU体系结构有关的变量,#define
NR_OPEN_DEFAULTBITS_PER_LONG。以x86体系为例,BITS_PER_LONG 在/include/asm-x86/types.h中定义,32位CPU为32,64位CPU为64
struct files_struct { /* * read mostly part */ atomic_t count; struct fdtable *fdt; struct fdtable fdtab; /* * written part on aseparate cache line in SMP */ spinlock_t file_lock____cacheline_aligned_in_smp; int next_fd; struct embedded_fd_setclose_on_exec_init; struct embedded_fd_setopen_fds_init; struct file * fd_array[NR_OPEN_DEFAULT]; };FILE结构和文件描述符、file结构之间的关系可以用下图来表示:
相关文章推荐
- 第十二天-linux系统文件属性知识详解
- virtual box vbox centos linux 学习记录
- Linux下C语言来检测USB设备以及自动区分U盘和硬盘并自动挂载
- Linux下常用的工具
- Linux制作补丁与打补丁简单用法
- Linux系统中.bz2与.gz文件的区别
- 实验1 熟悉Linux开发环境 实验报告
- Linux NFS服务器的安装与配置
- linux 系统睡眠.休眠命令
- CentOS 6.5 安装使用iSCSI-Target
- 警告:MySQL-server-5.5.46-1.linux2.6.x86_64.rpm: 头V3 DSA/SHA1 Signature, 密钥 ID 5072e1f5: NOKEY
- Linux下DIR,dirent,stat等结构体详解
- 第十五天-linux系统文件权限详细讲解
- linux 普通用户添加sudo权限
- linux 中curl命令详解
- linux 下的 .a 文件 .o 文件 含义
- VMware虚拟机的CentOS无法上网的解决方法(原先可以上网忽然不能了的解决)
- linux centos 网卡错误 Device eth0 does not seem to
- linux压缩和解压缩命令大全
- CentOS安装scp命令的软件包openssh-clients