文件描述符和文件指针

来源:互联网 发布:做淘宝需要多少钱 编辑:程序博客网 时间:2024/05/30 23:04

一、什么是文件描述符?
在linux系统中,“一切皆文件”所以设备也是以文件的形式存在的。对一个设备进行操作必须先打开这个文件,而打开这个文件就会用到文件描述符。文件描述符是一个很小的正整数,每个进程在PCB中都会保存着一份文件描述符表而文件描述符就是这个表的索引,每个表项都有一个指向一个已打开文件的指针。所有执行I/O操作的系统调用都是通过文件描述符来完成的。

在linux下进程是通过进程描述符(file descriptors)简称”fd”来访问文件的,系统启动的时候会有三个默认的文件描述符,分别是:0(标准输入),1(标准输出),2(标准错误)。文件描述符是从当前未使用的最小整数处开始分配的。所以,此时再打开一个文件它的文件描述符就是3。

如果我们把文件描述符的1号关闭,然后再打开一个文件。根据我们已知的知识,1号文件描述符在系统启动的时候都会创建里面存的是标准输出的地址,而又因为我们把1号给关闭了。所以,此时再创建一个文件那它的文件描述符的索引号码就是1。那么会遇到什么问题呢?我们来看下面的栗子:
这里写图片描述
我们关闭了1号文件,然后再以写的方式创建了一个新的文件log。
这里写图片描述
代码结果:
这里写图片描述
然后这本来应该打印到屏幕上的10条“hello fd”就被写入到这个文件log中去了,它的文件描述符现在是1号。而这就是文件描述符重定向
文件描述符重定向:
把文件索引号为1里本来指向stdout的地址清空掉,此时再来创建一个文件file_struct会把1号分给它,这样输出的内容本来是要输出到屏幕上,现在会输出到这个文件里面。

二、如何创建一个文件描述符
系统获取一个文件描述符的方法就是通过open或者creat函数获取,或者就是通过父进程来继承一个。
1、从父进程来继承的话,那么子进程就可以访问父进程的文件,然而进程之间是相互独立的,父进程与子进程之间是代码共享,数据独立。如果父子进程之间进行通信的话,那么父子都可以访问的文件就成了一个突破口。
2、open/creat 获取。

文件描述符对于每一个进程都是唯一的,当我们用fork()创建一个子进程时,子进程会获取父进程的文件描述符的副本。
三、文件结构体–file struct
每一个文件描述符都与一个打开的文件相互对应,一个文件也可以有多个文件描述符。系统为每一个进程都维护了一个文件描述符的表。
系统维护了3种不同类型的文件描述符表:
1、进程级别的文件描述符表
进程级别的文件描述符表每一条都记录了单个的文件描述符相关信息,表里面存的是控制文件描述符操作的一组标志、打开文件句柄的引用。这个进程级别的文件描述符用file_struct来表示,pcb里面有这样一个类型的指针变量files。

struct files_struct {      atomic_t count;              /* 共享该表的进程数 */      rwlock_t file_lock;          /* 保护以下的所有域,以免在tsk->alloc_lock中的嵌套*/      int max_fds;                 /*当前文件对象的最大数*/      int max_fdset;               /*当前文件描述符的最大数*/      int next_fd;                 /*已分配的文件描述符加1*/      struct file ** fd;           /* 指向文件对象指针数组的指针 */      fd_set *close_on_exec;       /*指向执行exec( )时需要关闭的文件描述符*/      fd_set *open_fds;           /*指向打开文件描述符的指针*/      fd_set close_on_exec_init;          /* 执行exec( )时需要关闭的文件描述符的初 值集合*/      fd_set open_fds_init;               /*文件描述符的初值集合*/      struct file * fd_array[32];         /* 文件对象指针的初始化数组*/  }; 

2、系统级别文件描述符
内核对多有打开的文件有一个系统级别的文件描述符,也称打开文件描述符表,表中各条目称为打开文件句柄,一个打开文件句柄有这个打开文件的全部相关信息。
包含为:
1、当前文件偏移量
2、打开文件所用到的文件标识(open中的flags标识)
3、文件访问模式(只读、只写、读写)
4、信号驱动先关的设置
5、对文件i-node的引用
6、文件类型和访问权限
7、一个指针、指向这个文件所持有的所列表
8、文件的各种属性

struct file{struct list_head f_list; /*所有打开的文件形成一个链表*/struct dentry *f_dentry; /*指向相关目录项的指针*/struct vfsmount *f_vfsmnt; /*指向VFS安装点的指针*/struct file_operations *f_op; /*指向文件操作表的指针*/mode_t f_mode; /*文件的打开模式*/loff_t f_pos; /*文件的当前位置*/unsigned short f_flags; /*打开文件时所指定的标志*/unsigned short f_count; /*使用该结构的进程数*/unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;/*预读标志、要预读的最多页面数、上次预读后的文件指针、预读的字节数以及预读的页面数*/int f_owner; /* 通过信号进行异步I/O数据的传送*/unsigned int f_uid, f_gid; /*用户的UID和GID*/int f_error; /*网络写操作的错误码*/unsigned long f_version; /*版本号*/void *private_data; /* tty驱动程序所需 */};

3、文件系统的i-node表
三种类型的文件描述符关系如下:
这里写图片描述
四、与文件描述符相关的输入输出函数

open:   以指定方式打开一个文件,调用成功后返回一个文件描述符。   creat:  打开一个文件,如果文件不存在则创建它,成功后返回一个文件描述符。   close:  关闭文件,进程对文件加锁全部被释放。   read:   从文件描述符对应的文件中读取文件,成功后返回读出的字节数。   write:  向文件描述符对应的文件中写入数据,成功后返回写入的字节数。   ftruncate:  把文件描述符对应的文件缩短到指定的长度。   lseek:  把文件指针设置到指定的位置,相当于库函数中的fseek。   fsync:  将已经写入到文件的数据写入到磁盘或其他下层设备中,成功返回0fstat:  返回文件描述符所对应文件的相关信息,把结果保存在struct stat中,成功返回0fchown: 修改文件描述符对应的文件的文件所有者和文件所有者组的信息。   fchmod: 修改文件描述符对应的文件的权限。   flock:  对文件施加建议性锁,成功返回0fcntl:  技能施加建议性锁也能施加强制性锁,能建立记录锁、读取锁,写入锁,成功返回0   dup:    复制文件描述符,返回没有使用的文件描述符中的最小编号。   dup2:   由用户指定返回的文件描述符的值,用来重新打开或重定向一个文件描述符。   select: 同时从多个文件描述符读取数据或向多个文件描述符写入数据。  

五、文件指针
文件指针:
C语言中使用的是文件指针而不是文件描述符作为I/O的句柄。文件指针指向进程用户区中一个被称为FILE结构的数据结构,FILE结构包括一个缓冲区和一个文件描述符。文件描述符是文件描述符表的一个索引,某种意义来说文件指针就是句柄的句柄(windows下文件描述符被称为文件句柄)。FILE*中除了包含fd的信息,还包含I/O缓冲是C标准形式的,所以说FILE*比fd更好用。所以还是多用fopen少用open吧。

六、文件指针和文件描述符的区别
文件描述符:在linux系统中打开文件就会获得文件描述符,它是一个很小的整数。每个进程控制块(PCB)中保存着一份文件描述符表,文件描述符就是文件描述符表的索引,每个表项都有一个指向打开文件的文件指针,这个文件指针指向进程用户区中的一个被称为FILE的数据结构。FILE结构包含一个缓冲区和一个文件描述符。而文件描述符是文件描述符表的一个索引,因此从某些意义上来说,文件指针就是文件描述符的句柄。

0 0
原创粉丝点击