[038]Binder传输fd细节

前言

最近在研究Linux IO相关的知识,突然想起来Binder机制可以传递fd,但是没有仔细考虑过下面这个问题。

Client端fd和Server端fd,内核中指向两个的file结构体还是指向同一个file结构体?

一、两者的区别

1.1 有人可能会问:两者有什么区别?

区别大了,如果指向同一个file结构体,就意味的两个进程共享同一个f_pos读写指针。

1.2 f_pos读写指针是什么?

f_pos读写指针是用于记录当前文件读写的位置

举个例子:
假设一个文件1.txt的内容是"helloworld"。
进程A和进程B对应的fd指向同一个file,就会共享f_pos。
进程A先读5个字节,就会读到"hello"
进程B再读5个字节,就会读到"world"

二、Binder驱动源码

以下代码运行在Client端的线程,并且都在内核中

2.1 binder_translate_fd

static int binder_translate_fd(int fd,
                   struct binder_transaction *t,
                   struct binder_thread *thread,
                   struct binder_transaction *in_reply_to)
{
    //获取Server端的binder_proc
    struct binder_proc *target_proc = t->to_proc;
    int target_fd;
    struct file *file;
    //获得Client端中fd对应的file结构体
    file = fget(fd);
    //获取Server端的一个空闲的target_fd
    target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
    //将target_fd和file绑定
    task_fd_install(target_proc, target_fd, file);//跳转2.2
    //返回server端的fd,也就是target_fd
    return target_fd;
}

2.2 task_fd_install

static void task_fd_install(
    struct binder_proc *proc, unsigned int fd, struct file *file)
{
    mutex_lock(&proc->files_lock);
    if (proc->files)
        __fd_install(proc->files, fd, file);//跳转2.3
    mutex_unlock(&proc->files_lock);
}

2.3 __fd_install

void __fd_install(struct files_struct *files, unsigned int fd,
              struct file *file)
{
    //每一个进程关联着一个files_struct结构体
    //files_struct结构体中有一个fdtable结构体
    struct fdtable *fdt;
    //获取files_struct中的fdtable
    fdt = rcu_dereference_sched(files->fdt);
    //fdtable保存了一个file指针数组fd
    //将fd[fd]指向file结构体,这两个fd不同,前者表示指针数组,后者表示形参中int fd
    rcu_assign_pointer(fdt->fd[fd], file);
}

整个关系如下图:


2.4 小结

从源码来看binder传输fd,指向同一个file结构体,共享同一个f_pos读写指针


三、写的Demo

3.1 Client端

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private IBinder mSendFd;
    private TextView mTxtSend;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTxtSend = findViewById(R.id.txt_send);
        mTxtSend.setOnClickListener(this);
        ping();
        writeFile();

    }

    //绑定Binder
    private void ping() {
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mSendFd = service;
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, Context.BIND_AUTO_CREATE);
    }

    //新建一个文件
    private void writeFile() {
        try {
            String content = "helloworld";
            File file = new File(this.getFilesDir().getPath() + "/" + "1.txt");
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file.getAbsoluteFile());
            BufferedWriter bw = new BufferedWriter(fileWriter);
            bw.write(content);
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        try {
            FileDescriptor fd = readFile();
            Parcel data = Parcel.obtain();
            data.writeFileDescriptor(fd);
            mSendFd.transact(1, data, null, 0);
        } catch (Exception e) {

        }
    }

    //读取5个字符
    private FileDescriptor readFile() {
        try {
            FileDescriptor fd = Os.open(this.getFilesDir().getPath() + "/" + "1.txt", OsConstants.O_RDONLY, 0600);
            byte[] buf = new byte[5];
            Os.read(fd, buf, 0, buf.length);
            Log.v("KobeWang3", "This is Client : " + new String(buf));
            return fd;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

3.2 Server端

public class RemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new SendBinder();
    }

    public class SendBinder extends Binder {
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            if (code == 1) {
                try {
                    FileDescriptor fd = data.readFileDescriptor().getFileDescriptor();
                    FileReader fileReader = new FileReader(fd);
                    BufferedReader bf = new BufferedReader(fileReader);
                    String str = bf.readLine();
                    Log.v("KobeWang3", "This is Server : " + str);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
    }
}

别忘了让RemoteService运行在其他进程

<service
    android:name=".RemoteService"
    android:exported="true"
    android:process=":remote">
</service>

3.3 运行结果

果然结果和我们分析的一致

11747 11747 V KobeWang3: This is Client : hello
11810 11830 V KobeWang3: This is Server : world

四、ParcelFileDescriptor

ParcelFileDescriptor是android提供的,继承于Parcelable,可以在AIDL中直接使用。

4.1 用法

用java层File对象创建ParcelFileDescriptor

public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
        final FileDescriptor fd = openInternal(file, mode);
        if (fd == null) return null;

        return new ParcelFileDescriptor(fd);
    }

private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
        final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);

        int realMode = S_IRWXU | S_IRWXG;
        if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
        if ((mode & MODE_WORLD_WRITEABLE) != 0) realMode |= S_IWOTH;

        final String path = file.getPath();
        try {
            return Os.open(path, flags, realMode);//重新open一次。
        } catch (ErrnoException e) {
            throw new FileNotFoundException(e.getMessage());
        }
    }

4.2 注意点

file1:java层File对象对应的fd1指向内核空间的file结构体
file2:ParcelFileDescriptor会根据path重新open,新建一个fd2指向内核空间新建的file结构体
虽然file1和file2指向同一个实体文件,但是两者的读写指针是独立的。
经过Binder通信传递ParcelFileDescriptor对象。Server端拿到的fd1指向的是file2。
假如Client端用java层File对象读文件,Server端拿到的ParcelFileDescriptor对应的fd1读写文件,两者并不会有任何影响。

五、为什么要学Linux Kernel

作为Java程序员出身我,其实对Linux Kernel并不熟悉,一年前,我开始努力尝试学习Linux Kernel,发现自己对很多上层的细节,有了更加深入的理解,我相信我继续努力学下去。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容