Linux内核笔记--深入理解文件描述符
来源:互联网 发布:java linklist方法 编辑:程序博客网 时间:2024/06/06 03:53
内核版本:linux-2.6.11
文件描述符(file descriptor)在Linux编程里随处可见,设备读写、网络通信、进程通信,fd可谓是关键中的关键。
深入理解可以增加我们使用它的信心。
该篇笔记主要解释了文件描述符底层的多态实现和文件描述符的生命周期。希望对自己和大家有所帮助。
先看三段简化后的内核代码
sys_open
fd = get_unused_fd();if (fd >= 0) { struct file *f = filp_open(tmp, flags, mode); fd_install(fd, f);}
sys_socket
fd = get_unused_fd();if (fd >= 0) { struct file *file = get_empty_filp(); fd_install(fd, file)}
do_pipe
struct file *f1 = get_empty_filp();struct file *f2 = get_emtpy_filp();i = get_unused_fd();j = get_unused_fd();fd_install(i, f1);fd_install(j,f2);fd[0] = i;fd[1] = j;
这三段代码分别是三个POSIX标准系统调用open
,socket
,pipe
的内核态例程,
简化后的代码可以清楚看到,文件描述符的获取和安装在不同模块中都有着几乎相同的套路。
get_unused_fd
顾名思义,它从当前进程描述符中的打开文件数组(current->files)里取得一个空闲的项,然后返回其数组下标。filp_open
最终将调用get_empty_filp
,因此三段代码都使用get_empty_filp
分配了一个新的文件对象(struct file)。fd_install
将上一个步骤创建的文件对象指针存到该进程的打开文件数组中。
至此,文件描述符安装完毕,返回的fd即为该文件对象在当前进程的task_struct中的files数组中的下标,也就是所谓的文件描述符,但究其本质,我们对文件描述符的所有使用其实就是在操作file对象。
接下来就是程序员最拿手的read,write,以及各种IO控制了。
file作为一个通用的结构体,在不同模块表示的对象是不同的,在文件系统中,它表示为一个文件,在进程通信中,它表示为一个管道,在网络通信中,它又表示为一个套接字。
那么问题来了,作为一个通用的结构体,在表示为不同的通信对象的时候,操作函数肯定是不同的,举个例子来说,write一个文件和write一个socket肯定是不一样的,那么同一个函数入口怎么做到多态的实现呢?
我们同样用write来解释,write()
->sys_write()
->vfs_write()
->file->f_op->write()
,重点在write调用流程的最后一步file->f_op->write()
,从这里开始,针对不同的file对象将进入不同的write实现。这是因为不同file里注册的各类操作函数是不同的。
struct file中的f_op指针,指向一系列函数指针的集合,这个结构名叫file_operation
,这一系列函数指针都指向着当前这个file对象(设备也好,管道也好,套接字也好)对应的各种操作函数的具体实现。
在2.6.11的内核里看起来如下:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); ...}
以ext3文件系统为例:
struct file_operations ext3_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .write = do_sync_write, .ioctl = ext3_ioctl, .mmap = generic_file_mmap, .open = generic_file_open, ...};
这里使用了C99的语法,始化了一个file_operations然后用自己的实现去填充一个个的函数指针,在open一个ext3文件系统中的文件时,会将这个file_operations注册到新建的这个file对象里。
同样,在pipe.c、socket.c里也可以看到类似的注册file_operations的行为。
所以,一个file是文件、是设备、是管道还是套接字,就是根据其中存放的file_operations来区分,
在创建file的时候就会针对这个file的类型注册不同的操作函数,这就解释了同一个通用函数的多态实现。
文件描述符的生命周期:
- 创建file对象
- 根据IO对象的类型注册各个操作函数(注册file_operations)
- 将file对象的指针注册到进程描述符的files数组里的fd下标处
- read、write等等IO操作
- 调用close(fd)释放file对象
- Linux内核笔记--深入理解文件描述符
- 《深入理解Linux内核3rd》学习笔记——进程描述符
- 《深入理解Linux内核》-3.2. 进程描述符
- 《深入理解linx内核》学习笔记3---子进程共享父进程打开的文件描述符
- 《深入理解linux内核》中hlist描述的疑问
- 《深入理解Linux内核》学习笔记-第一章
- 深入理解LINUX内核学习笔记01
- 深入理解LINUX内核---学习笔记02
- 深入理解LINUX内核--学习笔记
- 《深入理解Linux内核》笔记--内存寻址
- 《深入理解linux内核》笔记(一)
- 《深入理解LINUX内核》笔记(二)
- 《深入理解LINUX内核》笔记(三)
- 《深入理解LINUX内核》笔记(四)
- 《深入理解LINUX内核》笔记(五)
- <深入理解linux内核》笔记六
- 《深入理解linux内核》笔记八
- 《深入理解Linux内核》笔记九
- 释疑:SI-RNTI,C-RNTI,P-RNTI,RA-RNTI,SPS-RNTI
- Android打包APK时-->指定.so库
- 深入理解Scala的隐式转换系统
- C++ 学习之pair操作
- 51nod 1473-等幂映射(循环节)
- Linux内核笔记--深入理解文件描述符
- 你真应该再多了解些Handler机制
- jQuery 扩展实现
- Leetcode 从易入难 485.Max Consecutive Ones
- unity内存优化总结
- (UVA
- 【浅析】Python的内存管理机制
- UNIX环境高级编程习题——第六章
- Java并发编程:Callable、Future和FutureTask