android进程间传递文件描述符原理

转自:https://blog.csdn.net/lewif/article/details/50668783

参考:

https://blog.csdn.net/yejing_utopia/article/details/43276451 //进程间文件描述符传送

https://blog.csdn.net/vector03/article/details/51034021 //binder传递fd的机制

https://blog.csdn.net/fyfcauc/article/details/50765372 //Android Binder 透传 Fd流程

https://blog.csdn.net/new_abc/article/details/8168064  //binder ---传递文件描述符

https://blog.csdn.net/yejing_utopia/article/details/43276451 进程间文件描述符传送

https://blog.csdn.net/Aquester/article/details/23658051 进程间传递文件描述符fd


在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;
}

实际调用的是

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开始的整数,使用一个往上+1
        fd = 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;
}


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);
}

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;

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

--------------------- 
作者:lyf5231 
来源:CSDN 
原文:https://blog.csdn.net/lewif/article/details/50668783 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值