在Linux中,进程打开一个文件,返回一个整数的文件描述符,然后就可以在这个文件描述符上对该文件进行操作。那么文件描述符和文件到底是什么关系?进程使用的是虚拟地址,不同进程间是地址隔离的,如何在两个进程中传递文件描述符,然后指向同一文件(binder传递文件描述符)?

linux打开文件过程

下图是linux内核中打开文件的结构体之间的关系图(只是大概,细节可以参考各种内核书籍):

内核中每个进程都使用task_struct结构体表示,其成员files是一个files_struct结构体,表示该进程打开的所有文件相关信息; 
files_struct结构体中的fd_array数组(上层应用程序返回的文件描述符其实就是这个数组的下标),是个struct file,内核对打开的文件都用file结构体描述,而成员fdt是个struct fdtable,其成员fd代表数组fd_array的起始地址; 
struct file中的f_dentry表示文件的dentry结构,而dentry中包含了文件唯一的inode,即d_inode,d_inode唯一表示了该文件(每个具体文件只有一个inode结构,而多个dentry可以指向同一个inode,例如软链接)。

下面简要分析下代码。 
上层:应用程序调用c库中(实现依赖使用的c库)的open()函数打开一个文件,open()返回一个文件描述符; 
内核:上层open调用系统调用open函数,

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{ret = do_sys_open(AT_FDCWD, filename, flags, mode);return ret;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

实际调用的是

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{struct open_flags op;int lookup = build_open_flags(flags, mode, &op);char *tmp = getname(filename);int fd = PTR_ERR(tmp);if (!IS_ERR(tmp)) {//①//获取进程中未使用的fd,就是从0开始的整数,使用一个往上+1fd = get_unused_fd_flags(flags);if (fd >= 0) {//②//构建file结构体struct file *f = do_filp_open(dfd, tmp, &op, lookup);if (IS_ERR(f)) {put_unused_fd(fd);fd = PTR_ERR(f);} else {fsnotify_open(f);//③//将fd和file结构体建立关系fd_install(fd, f);}}putname(tmp);}return fd;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
void fd_install(unsigned int fd, struct file *file)
{//当前进程的files_struct结构struct files_struct *files = current->files;struct fdtable *fdt;spin_lock(&files->file_lock);//获取files_struct中的fdt结构体fdt = files_fdtable(files);BUG_ON(fdt->fd[fd] != NULL);//将fdt->fd[fd],也就是fd_array[fd],赋值为file,rcu_assign_pointer(fdt->fd[fd], file);spin_unlock(&files->file_lock);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

do_sys_open主要就完成了上面注释的3步: 
获取进程中未使用的fd,就是从0开始的整数,使用一个往上+1; 
根据文件的路径,构建file结构体,即构建其中的dentry等; 
将fd、file结构体同本进程task_struct结构体建立关系。 
经过上面的分析,我们可以得到一个结论: 
内核用struct file描述文件,在单个进程中,打开的文件都保存在进程结构体task_struct中files_struct结构体中的fd_array数组中,而上层返回的文件描述符就是这个数组的下标,用来和struct file保持对应关系。因此在不同的进程中打开同一文件,内核都会创建一个新的file结构用来和实际文件关联(但是同一文件在不同进程中的file结构体主要内容是相同的,都描述了所指向的实际文件),然后给上层返回不同的文件描述符。 
那么如何将不同进程中的文件描述符关联为同一文件?下面我们看看Android binder通过传递文件描述符如何实现同一文件描述符的共享。

android binder传递文件描述符

下面是内核binder_transaction函数中的一部分,

static void binder_transaction(struct binder_proc *proc,struct binder_thread *thread,struct binder_transaction_data *tr, int reply)
{
case BINDER_TYPE_FD: {int target_fd;struct file *file//通过本进程的fd,获取对应的file结构体file = fget(fp->handle);//获取目标进程未使用的文件描述符target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);//将file结构和目标进程的fd等联系起来task_fd_install(target_proc, target_fd, file);fp->handle = target_fd;} break;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

上述主要工作和前面open函数的实现类似,只不过是去获取目标进程的未使用文件描述符,然后将本进程的file结构体去和目标进程挂钩(file结构体主要内容描述的都是具体文件相关的,例如dentry,所以不同进程间可以共享,因为都是指向同一实际文件),这样binder驱动就悄无声息的帮我们在内核中在目标进程中新建了文件描述符,并将原进程的file结构与之挂钩,就像在目标进程中打开了原进程中的该文件一样,只不过返回给目标进程上层的描述符是新的target_fd。

android进程间传递文件描述符原理相关推荐

  1. Linux高级进程编程———在任意两个进程间传递文件描述符:使用 sendmsg 和 recvmsg 实现

    进程间传递打开的文件描述符,并不是传递文件描述符的值.那么在传递时究竟传递了什么?我们要先搞明白这个问题. 1.文件描述符 文件描述符的值与文件没有任何联系,只是该文件在进程中的一个标识,所以同一文件 ...

  2. Linux 进程间传递文件描述符

    文章目录 文件描述符 文件数据结构 共享文件 UNIX域socket实现传递文件描述符 进程间传递打开的文件描述符,并不是传递文件描述符的值.先说一下文件描述符. 文件描述符 对内核来说,所有打开的文 ...

  3. Linux中进程间传递文件描述符的方法

    在进行fork调用后,由于子进程会拷贝父进程的资源,所以父进程中打开的文件描述符在子进程中仍然保持着打开,我们很容易的就将父进程的描述符传递给了子进程.但是除了这种情况下,如果想将某个父进程在子进程创 ...

  4. 进程间传递文件描述符--sendmsg,recvmsg(可用)

    UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h> #include <sys/socket.h> ...

  5. 不相干进程之间传递文件描述符

    #include <sys/socket.h> #include <fcntl.h> #include <stdio.h> #include <unistd. ...

  6. 线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。 进程拥有这

    线程共享的环境: 进程代码段.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯).进程打开的文件描述符.信号的处理器.进程的当前目录和进程用户ID与进程组ID. 进程拥有这许多共性的 ...

  7. Linux的辅助数据和传递文件描述符

    简介 首先,明确传递文件描述符的意义.一般来说,在多进程网络编程中,我们设置一个主进程用于监听新来的连接,设置一个进程池,用于处理这些连接.但是,与线程池不同,进程池各个进程之间的空间是独立的,直接共 ...

  8. FORK()子进程对父进程打开的文件描述符的处理

    总的来说,子进程将复制父亲进程的数据段,BSS段,代码段,堆空间,栈空间和文件描述符.而对于文件技术符关联内核文件表项(即STRUCT FILE结构),则是采取了共享的方式. 下面代码说明. I值分离 ...

  9. fork()子进程与父进程之间的文件描述符问题

    在C程序中,文件由文件指针或者文件描述符表示.ISO C的标准I/0库函数(fopen, fclose, fread, fwrite, fscanf, fprintf等)使用文件指针,UNIX的I/O ...

最新文章

  1. R语言tidyr包spread()函数实战详解:数据裂变、从窄表到宽表
  2. 【python】一个简单的贪婪爬虫
  3. Pimple - 一个简单的 PHP 依赖注入容器
  4. 汇编语言转成c语言,如何把汇编语言转换成C语言
  5. hadoop小型集群_小型Hadoop集群的Ganglia配置和一些故障排除
  6. 埃及分数问题(带乐观估计函数的迭代加深搜索算法-IDA*)
  7. html三列布局和两列布局,CSS 常见两列布局、三列布局
  8. UVA10063 Knuth‘s Permutation【排列组合】
  9. 原生JS大揭秘—事件循环机制Event Loop
  10. OSPF协议配置命令解析
  11. Knockoutjs开发指南
  12. educator计算机技术基础答案,计算机教育技术基础,technical basis of computer education,音标,读音,翻译,英文例句,英语词典...
  13. 计算机图片组合快捷键,电脑高手常用的组合快捷键
  14. 【考研政治】1. 导论和基本哲学问题
  15. 关于统计分析软件Spss统计个案数和实际数据的个案数不一致问题
  16. 大龄程序员找工作,为什么这么难?能力与年龄不匹配
  17. 基于jsp+mysql+Spring的SSM在线蛋糕商城销售网站项目设计和实现
  18. 南极大冒险/零下八度/南极物语/8只雪橇犬
  19. 基于Java毕业设计房屋租赁平台源码+系统+mysql+lw文档+部署软件
  20. 微信小程序canvas画布新接口type为2D时drawImage方法的使用以及注意事项

热门文章

  1. Beyond Compare可以进行二进制比较
  2. 发布 学习进度条 博客要求
  3. Nuget 管理报repositories.config 访问路径被拒绝 解决办法
  4. Programmer Competency Matrix
  5. JBox - 模态窗口,工具提示和消息 jQuery 插件
  6. 《How to Reshape Input Data for Long Short-Term Memory Networks in Keras》学习笔记
  7. 【参数辨识】永磁同步电机的参数辨识
  8. mysql 联合索引 range_MySQL 联合索引使用情况
  9. oracle函数 power(x,y)
  10. linux下的文档处理及tar命令