Android Binder 机制初步学习 笔记(四,完结)—— Binder 简单应用示例

来源:互联网 发布:js删除数组中元素 编辑:程序博客网 时间:2024/06/05 15:23

  • NOTE
  • Binder 通信实践
    • 为虚拟字符设备 Freg 编写驱动
      • 1 fregh
      • 2 fregc
      • 3 Kconfig
      • 4 Makefile
      • 5 编译内核驱动模块
    • Binder 实例 Common 模块
      • 1 IFregServiceh
      • 2 IFregServicecpp
    • Binder 实例 Server 模块
      • 1 FregServercpp
      • 2 Androidmk
    • Binder 实例 Client 模块
      • 1 FregClientcpp
      • 2 Androidmk
    • 测试结果


NOTE

  • 源码版本:Android 7.1.2。
  • 内核版本:android-goldfish-3.4
  • 内核下载:git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git (清华镜像站)
  • 以下分析思路均来自老罗的《Android 系统源代码情景分析(修订版)》

Binder 通信实践

  • 基于之前介绍过的 Binder 通信库,我们可以写一个简单的应用实例来熟悉它的使用方法。
  • 在参考书中,这个关于 Binder 的例子里实现了一个 Service 组件,这个组件负责管理一个虚拟的硬件设备 freg,这个虚拟的设备是作者在介绍 HAL 层时实现的。这里我们只需要用到这个设备的内核驱动部分,所以首先我们要实现这个虚拟设备。
  • 完成设备的驱动程序后,就要开始写我们的 Binder 实例了。这个实例分为三个模块:
    1. common
      • 实现硬件访问服务接口 IFregService
      • 实现 Binder 本地对象类 BnFregServiceBinder 代理对象类 BpFregService
    2. server
      • 实现了 Server 进程,其中包含了一个 Service 组件 FregService
    3. client
      • 实现了一个 Client 进程,它通过一个 BpFregService 代理对象去访问运行在 Server 进程中的 Service 组件 FregService 所提供的服务。

1. 为虚拟字符设备 Freg 编写驱动

  • /kernel/goldfish/drivers 下新建一个文件夹 freg
    • mkdir freg

1.1 freg.h

  • 定义四个字符串常量,分别描述 freg 在设备文件系统中的名称。
  • 定义结构体 fake_reg_dev 描述虚拟设备 freg
    • val:描述一个虚拟寄存器。
    • sem:信号量,用于同步访问寄存器。
    • dev:标准 Linux 字符设备结构体变量,用于标志 freg 为字符设备类型。
#ifndef _FAKE_REG_H_#define _FAKE_REG_H_#include <linux/cdev.h>#include <linux/semaphore.h>#define FREG_DEVICE_NODE_NAME  "freg"#define FREG_DEVICE_FILE_NAME  "freg"#define FREG_DEVICE_PROC_NAME  "freg"#define FREG_DEVICE_CLASS_NAME "freg"struct fake_reg_dev {        int val;        struct semaphore sem;        struct cdev dev;};#endif

1.2 freg.c

  • 向用户空间提供三个访问设备 freg 的寄存器 val 的接口:
    • proc 文件系统接口。
    • 传统设备文件系统接口。
    • devfs 文件系统接口。
  • 首先定义一些相关变量以及函数原型:
#include <linux/init.h>#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/proc_fs.h>#include <linux/device.h>#include <asm/uaccess.h>#include "freg.h"/* the major/minor device number */static int freg_major = 0;static int freg_minor = 0;/* types and struct of the device */static struct class* freg_class = NULL;static struct fake_reg_dev* freg_dev = NULL;/* traditional operations */static int freg_open(struct inode* inode,                        struct file* filp);static int freg_release(struct inode* inode,                        struct file* filp);static ssize_t freg_read(struct file* filp,                        char __user *buf,                        size_t count,                        loff_t* f_pos);static ssize_t freg_write(struct file* filp,                        const char __user *buf,                        size_t count,                        loff_t* f_pos);/* traditional operations list */static struct file_operations freg_fops = {        .owner   = THIS_MODULE,        .open    = freg_open,        .release = freg_release,        .read    = freg_read,        .write   = freg_write,};/* device properties operation of filesystem 'devfs' */static ssize_t freg_val_show(struct device* dev,                        struct device_attribute* attr,                        char* buf);static ssize_t freg_val_store(struct device* dev,                        struct device_attribute* attr,                        const char* buf,                        size_t count);/* devfs filesystem's device properties */static DEVICE_ATTR(val,                S_IRUGO | S_IWUSR,                freg_val_show,                freg_val_store);
  • 实现传统设备文件操作:
    • freg_open / freg_release:打开 / 关闭设备。
    • freg_read / freg_write:读取 / 写入 val
/* open this device */static int freg_open(struct inode* inode,                        struct file* filp){        struct fake_reg_dev* dev;        /* save our struct to the private_data of file pointer */        dev = container_of(inode->i_cdev, struct fake_reg_dev, dev);        filp->private_data = dev;        return 0;}/* release this device */static int freg_release(struct inode* inode,                        struct file* filp){        return 0;}/* read the value from the device */static ssize_t freg_read(struct file* filp,                        char __user *buf,                        size_t count,                        loff_t* f_pos){        ssize_t err = 0;        struct fake_reg_dev* dev = filp->private_data;        /* synchronize */        if (down_interruptible(&(dev->sem)))        {                return -ERESTARTSYS;        }        if (count < sizeof(dev->val))        {                goto out;        }        /* copy value to the buffer */        if (copy_to_user(buf, &(dev->val), sizeof(dev->val)))        {                err = -EFAULT;                goto out;        }        err = sizeof(dev->val);out:        up(&(dev->sem));        return err;}/* write value to the device */static ssize_t freg_write(struct file* filp,                        const char __user *buf,                        size_t count,                        loff_t* f_pos){        ssize_t err = 0;        struct fake_reg_dev* dev = filp->private_data;        /* synchronize */        if (down_interruptible(&(dev->sem)))        {                return -ERESTARTSYS;        }        if (count != sizeof(dev->val))        {                goto out;        }        /* copy buffer to the value */        if (copy_from_user(&(dev->val), buf, count))        {                err = -EFAULT;                goto out;        }        err = sizeof(dev->val);out:        up(&(dev->sem));        return err;}
  • 定义用于访问设备的 devfs 文件系统接口:
    • 将寄存器 val 当做设备的一个属性,通过读写熟悉达到访问目的。
    • freg_val_show:读取 val
    • freg_val_store:写入 val
    • 为方便编写 proc 文件系统接口,将一些共通的操作提取出来,写成两个内部函数 __freg_get_val__freg_set_val
/* read dev->val to buffer */static ssize_t __freg_get_val(struct fake_reg_dev* dev,                        const char* buf){        int val = 0;        if (down_interruptible(&(dev->sem)))        {                return -ERESTARTSYS;        }        val = dev->val;        up(&(dev->sem));        return snprintf(buf, PAGE_SIZE, "%d\n", val);}/* write buffer value to dev->val */static ssize_t __freg_set_val(struct fake_reg_dev* dev,                        const char* buf,                        size_t count){        int val = 0;        /* str to int */        val = simple_strtol(buf, NULL, 10);        if (down_interruptible(&(dev->sem)))        {                return -ERESTARTSYS;        }        dev->val = val;        up(&(dev->sem));        return count;}/* read device properties value */static ssize_t freg_val_show(struct device* dev,                        struct device_attribute* attr,                        char* buf){        struct fake_reg_dev* hdev = (struct fake_reg_dev*) dev_get_drvdata(dev);        return __freg_get_val(hdev, buf);}/* write value to device properties */static ssize_t freg_val_store(struct device* dev,                        struct device_attribute* attr,                        const char* buf,                        size_t count){        struct fake_reg_dev* hdev = (struct fake_reg_dev*) dev_get_drvdata(dev);        return __freg_set_val(hdev, buf, count);}
  • 定义 proc 文件系统接口:
    • freg_proc_read / freg_proc_write:寄存器读写操作。
    • freg_create_proc / freg_remove_proc:创建 / 删除 /proc/freg 文件。
    • 注意在 creat 的时候,原本有一句 entry->owner = THIS_MODULE 的,但是在这个版本里不需要。
/* read dev->val to page buffer */static ssize_t freg_proc_read(char* page,                        char** start,                        off_t off,                        int count,                        int* eof,                        void* data){        if (off > 0)        {                *eof = 1;                return 0;        }        return __freg_get_val(freg_dev, page);}/* save buffer value to dev->val */static ssize_t freg_proc_write(struct file* filp,                        const char __user *buff,                        unsigned long len,                        void* data){        int err = 0;        char* page = NULL;        if (len > PAGE_SIZE)        {                printk(KERN_ALERT"The buff is too large: %lu.\n", len);                return -EFAULT;        }        page = (char*) __get_free_page(GFP_KERNEL);        if (!page)        {                printk(KERN_ALERT"Failed to alloc page.\n");                return -ENOMEM;        }        /* copy user buffer to kernel buffer */        if (copy_from_user(page, buff, len))        {                printk(KERN_ALERT"Failed to copy buff from user.\n");                err = -EFAULT;                goto out;        }        err = __freg_set_val(freg_dev, page, len);out:        free_page((unsigned long) page);        return err;}/* create file 'proc/freg' */static void freg_create_proc(void){        struct proc_dir_entry* entry;        entry = create_proc_entry(FREG_DEVICE_PROC_NAME, 0, NULL);        if (entry)        {                entry->read_proc = freg_proc_read;                entry->write_proc = freg_proc_write;        }}/* remove file 'proc/freg' */static void freg_remove_proc(void){        remove_proc_entry(FREG_DEVICE_PROC_NAME, NULL);}
  • 定义驱动的模块加载与卸载函数:
    • freg_init:注册与初始化 freg
    • freg_exit:反注册与释放 freg
    • 注意在 setup 中,采用了 sema_init(&(dev->sem), 1) 来初始化信号量,而在源码分析的原文中,这里用的是 init_MUTEX(&(dev->sem)),但目前这个操作已经被弃用了。
/* init device */static int __freg_setup_dev(struct fake_reg_dev* dev){        int err;        dev_t devno = MKDEV(freg_major, freg_minor);        memset(dev, 0, sizeof(struct fake_reg_dev));        /* init cdev */        cdev_init(&(dev->dev), &freg_fops);        dev->dev.owner = THIS_MODULE;        dev->dev.ops = &freg_fops;        /* register cdev */        err = cdev_add(&(dev->dev), devno, 1);        if (err)        {                return err;        }        /* init freg_dev */        sema_init(&(dev->sem), 1);        dev->val = 0;        return 0;}/* load module */static int __init freg_init(void){        int err = -1;        dev_t dev = 0;        struct device* temp = NULL;        printk(KERN_ALERT"Initializing freg device.\n");        /* dynamic allocation of the major and minor number */        err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME);        if (err < 0)        {                printk(KERN_ALERT"Failed to alloc char dev region.\n");                goto fail;        }        freg_major = MAJOR(dev);        freg_minor = MINOR(dev);        /* alloc struct freg */        freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL);        if (!freg_dev)        {                err = -ENOMEM;                printk(KERN_ALERT"Failed to alloc freg device.\n");                goto unregister;        }        /* init device */        err = __freg_setup_dev(freg_dev);        if (err)        {                printk(KERN_ALERT"Failed to setup freg device: %d.\n", err);                goto cleanup;        }        /* create freg_class dir 'freg' in '/sys/class' */        freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME);        if (IS_ERR(freg_class))        {                err = PTR_ERR(freg_class);                printk(KERN_ALERT"Failed to create freg device class.\n");                goto destroy_cdev;        }        /* create dir 'freg' in '/dev/' and 'sys/class/freg' */        temp = device_create(freg_class, NULL, dev, NULL, "%s", FREG_DEVICE_FILE_NAME);        if (IS_ERR(freg_class))        {                err = PTR_ERR(temp);                printk(KERN_ALERT"Falied to create freg device.\n");                goto destroy_class;        }        /* create properties dir 'val' in 'sys/class/freg/freg' */        err = device_create_file(temp, &dev_attr_val);        if (err < 0)        {                printk(KERN_ALERT"Failed to create attribute val of freg device.\n");                goto destroy_device;        }        dev_set_drvdata(temp, freg_dev);        /* create 'proc/freg' */        freg_create_proc();        printk(KERN_ALERT"Succeeded to initialize freg device.\n");        return 0;destroy_device:        device_destroy(freg_class, dev);destroy_class:        class_destroy(freg_class);destroy_cdev:        cdev_del(&(freg_dev->dev));cleanup:        kfree(freg_dev);unregister:        unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1);fail:        return err;}/* unload module */static void __exit freg_exit(void){        dev_t devno = MKDEV(freg_major, freg_minor);        printk(KERN_ALERT"Destroy freg device.\n");        /* delete '/proc/freg' */        freg_remove_proc();        /* destroy device and its class */        if (freg_class)        {                device_destroy(freg_class, MKDEV(freg_major, freg_minor));                class_destroy(freg_class);        }        /* delete cdev and release mem */        if (freg_dev)        {                cdev_del(&(freg_dev->dev));                kfree(freg_dev);        }        /* release devno */        unregister_chrdev_region(devno, 1);}MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Fake Register Driver");module_init(freg_init);module_exit(freg_exit);

1.3 Kconfig

  • 驱动程序写完后,需要继续编写一些编译配置文件。
  • 这个文件定义了驱动的编译选项。
config FREG    tristate "Fake Register Driver"    default n    help    This is the freg driver for android system.
  • freg 文件夹下写好这个文件后,还要去修改内核的 Kconfig 文件,否则无法编译我们加入的新内核驱动。
  • kernel/goldfish/drivers/Kconfig 中,加入:
    • source "drivers/freg/Kconfig"

1.4 Makefile

  • 这个驱动的编译脚本很简单,只有一行:
    • obj-$(CONFIG_FREG) += freg.o
  • 相应地,我们也需要修改内核的 Makefile 文件:
    • drivers/Makefile
    • 添加语句:obj-$(CONFIG_FREG) += freg/

1.5 编译内核驱动模块

  • 指令 make menuconfig 配置编译方式:
    • 用上下键选择 Device Drivers 项,按下 Enter
    • 选择 Fake Register Driver 项,按下 Y
    • 保存配置,退出。
  • 执行 make,成功后会有提示:
    • Kernel: arch/arm/boot/zImage is ready
  • 之后启动模拟器 emulator 的时候,加上 -kernel kernel/goldfish/arch/arm/boot/zImage,就能使用编译好的内核了。

2. Binder 实例 —— Common 模块

  • ./external/binder 文件夹下创建 common 文件夹:
    • mkdir common
    • 进入 cd common

2.1 IFregService.h

  • FREG_SERVICE 描述了该 Service 组件注册到 Service Manager 的名称。
  • 定义硬件访问服务接口 IFregService
    • getVal:读取硬件设备 freg 中寄存器 val 的值。
    • setVal:写入 val 值。
  • 定义 Binder 本地对象类 BnFregService
    • 实现成员函数 onTransact,用于处理收到的信息。
  • DECLARE_META_INTERFACE
    • 作用是声明 IFregService 类的元接口
    • 定义一个静态成员变量 descriptor,用于描述接口名。
    • 定义成员函数 getInterfaceDescriptor,用于获取接口名。
    • 定义静态成员函数 asInterface,用于将一个 IBinder 对象转换为 IFregService 接口。
    • 定义了 IFregService 的构造与析构函数。
#ifndef IFREGSERVICE_H_#define IFREGSERVICE_H_#include <utils/RefBase.h>#include <binder/IInterface.h>#include <binder/Parcel.h>#define FREG_SERVICE "stone.FregService"using namespace android;class IFregService : public IInterface{public :        DECLARE_META_INTERFACE(FregService);        virtual int32_t getVal() = 0;        virtual void setVal(int32_t val) = 0;};class BnFregService : public BnInterface<IFregService>{public :        virtual status_t onTransact(uint32_t code,                                const Parcel& data,                                Parcel* reply,                                uint32_t flags = 0);};#endif

2.2 IFregService.cpp

  • 定义枚举变量,表示进程间通信代码:
    • GET_VAL:对应成员函数 getVal
    • SET_VAL:对应成员函数 setVal
  • 定义 Binder 代理对象类 BpFregService,它实现了 IFregService 接口:
    • getVal
      • 将需要传输的数据封装到 Parcel 对象中。
      • 通过 remote 函数获得一个 BpBinder 代理对象。
      • 通过代理对象的成员函数 transact 来请求运行在 Server 进程中的一个 Binder 本地对象执行一个 GET_VAL 操作。
      • 操作的返回结果是一个整数,封装在另一个 Parcel 对象中返回。
    • setVal
      • 将传递的数据封装在一个 Parcel 对象中。
      • 同样获取 BpBinder 代理对象。
      • 通过代理的成员函数 transact 请求 SET_VAL 操作。
      • 通过 SET_VAL 操作将一个整数写入到设备 freg 的寄存器中。
  • IMPLEMENT_META_INTERFACE
    • 与头文件中的宏是对应的,它实现了 IFregService 类的元接口。
    • 将接口名设置为 "stone.IFregService"
    • 实现 IFregService 类的构造,析构函数(空实现)。
    • 实现成员函数 getInterfaceDescriptor
    • 实现成员函数 asInterface
      • 其参数 obj 应指向一个类型为 BnFregServiceBinder 本地对象,或一个类型为 BpBinderBinder 代理对象,否则其返回值为 NULL
      • 若指向本地对象,则调用成员函数 queryLocalInterface 直接返回一个 IFregService 接口。
      • 若指向代理对象,则成员函数 queryLocalInterface 返回 NULL,随后将其封装成一个 BpFregService 对象,并将它的 IFregService 接口返回。
  • 实现了 BnFregService 类成员函数 onTransact
    • 负责将 GET_VALSET_VAL 请求分发给其子类的成员函数 getValsetVal 处理。
    • BnFregService 的子类为 FregService,其具体实现了 getValsetVal
#define LOG_TAG "IFregService"#include <utils/Log.h>#include "IFregService.h"using namespace android;enum{        GET_VAL = IBinder::FIRST_CALL_TRANSACTION,        SET_VAL};class BpFregService : public BpInterface<IFregService>{public :        BpFregService(const sp<IBinder>& impl)                : BpInterface<IFregService>(impl)        {        }        int32_t getVal()        {                Parcel data;                data.writeInterfaceToken(IFregService::getInterfaceDescriptor());                Parcel reply;                remote()->transact(GET_VAL, data, &reply);                int32_t val = reply.readInt32();                return val;        }        void setVal(int32_t val)        {                Parcel data;                data.writeInterfaceToken(IFregService::getInterfaceDescriptor());                data.writeInt32(val);                Parcel reply;                remote()->transact(SET_VAL, data, &reply);        }};IMPLEMENT_META_INTERFACE(FregService, "stone.IFregService");status_t BnFregService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){        switch(code)        {                case GET_VAL:                {                        CHECK_INTERFACE(IFregService, data, reply);                        int32_t val = getVal();                        reply->writeInt32(val);                        return NO_ERROR;                }                case SET_VAL:                {                        CHECK_INTERFACE(IFregService, data, reply);                        int32_t val = data.readInt32();                        setVal(val);                        return NO_ERROR;                }                default:                {                        return BBinder::onTransact(code, data, reply, flags);                }        }}

3. Binder 实例 —— Server 模块

  • ./external/binder 文件夹下创建 common 文件夹:
    • mkdir server
    • 进入 cd server

3.1 FregServer.cpp

  • 首先实现了一个 Service 组件类 FregService
    • 继承了 BnFregService,并具体实现了 IFregService 接口。
    • 构造函数中,调用 open 函数来打开设备文件 /dev/freg,并将得到的文件描述符保存在成员变量 fd 中。
    • 相应地,析构函数中则会关闭设备文件。
    • getValsetVal 都会通过打开的设备文件对其寄存器进行读写操作。
    • 静态成员函数 instantiate 负责将 FregService 组件注册到 Service Manager 中,并将注册名设置为 "stone.FregService",如此一来,Client 进程就能通过该名称获取这个 FregService 组件的一个代理对象了。
  • main 函数即是该 Server 进程的主函数:
    • 调用 instantiate 注册组件。
    • 调用 ProcessState 对象成员函数 startThreadPool 启动 Binder 线程池。
    • 调用主线程 IPCThreadState 对象的成员 joinThreadPool 将主线程添加到进程 Binder 线程池中,用于处理来自 Client 的通信请求。
#define LOG_TAG "FregServer"#include <stdlib.h>#include <fcntl.h>#include <utils/Log.h>#include <binder/IServiceManager.h>#include <binder/IPCThreadState.h>#include "../common/IFregService.h"#define FREG_DEVICE_NAME "/dev/freg"class FregService : public BnFregService{public :        FregService()        {                fd = open(FREG_DEVICE_NAME, O_RDWR);                if (-1 == fd)                {                        ALOGE("Failed to open device %s.\n", FREG_DEVICE_NAME);                }        }        virtual ~FregService()        {                if (fd != -1)                {                        close(fd);                }        }public :        static void instantiate()        {                defaultServiceManager()->addService(String16(FREG_SERVICE), new FregService());        }        int32_t getVal()        {                int32_t val = 0;                if (fd != -1)                {                        read(fd, &val, sizeof(val));                }                return val;        }        void setVal(int32_t val)        {                if (fd != -1)                {                        write(fd, &val, sizeof(val));                }        }private :        int fd;};int main(int argc, char** argv){        FregService::instantiate();        ProcessState::self()->startThreadPool();        IPCThreadState::self()->joinThreadPool();        return 0;}

3.2 Android.mk

  • server 模块的编译脚本。
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := ../common/IFregService.cpp \        FregServer.cppLOCAL_SHARED_LIBRARIES := libcutils libutils libbinderLOCAL_MODULE := FregServerinclude $(BUILD_EXECUTABLE)

4. Binder 实例 —— Client 模块

  • ./external/binder 文件夹下创建 common 文件夹:
    • mkdir server
    • 进入 cd server

4.1 FregClient.cpp

  • 首先调用 defaultServiceManager() 获取 Service Manager 的代理对象。
  • 调用代理对象成员函数 getService 获取名为 "stone.FregService"Service 组件的一个类型为 BpBinder 的代理对象。
  • BpBinder 代理对象封装成 BpFregService 代理对象,获取其 IFregService 接口,保存在变量 service 中。
  • 通过 service->getVal() 获取寄存器的当前值,并打印。
  • 通过 service->setVal() 设置寄存器的值,这个值为当前值加一。
  • 再次通过 service->getVal() 获取寄存器值并打印。
  • 可以预见到的结果是,运行 FregClient 后屏幕上应有的输出应该是:nn + 1
  • 由于寄存器值初始化为 0,所以第一次运行 FregClient 时输出应为 01
  • 可以预见到的是,第二次运行 FregClient 时输出为 12
#define LOG_TAG "FregClient"#include <utils/Log.h>#include <binder/IServiceManager.h>#include "../common/IFregService.h"int main(){        sp<IBinder> binder = defaultServiceManager()->getService(String16(FREG_SERVICE));        if (binder == NULL)        {                ALOGE("Failed to get freg service: %s.\n", FREG_SERVICE);                return -1;        }        sp<IFregService> service = IFregService::asInterface(binder);        if (service == NULL)        {                ALOGE("Failed to get freg service interface.\n");                return -2;        }        printf("Read original value from FregService:\n");        int32_t val = service->getVal();        printf(" %d.\n", val);        printf("Add value 1 to FregService.\n");        val += 1;        service->setVal(val);        printf("Read the value from FregService again:\n");        val = service->getVal();        printf(" %d.\n", val);        return 0;}

4.2 Android.mk

  • client 模块的编译脚本。
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := ../common/IFregService.cpp \        FregClient.cppLOCAL_SHARED_LIBRARIES := libcutils libutils libbinderLOCAL_MODULE := FregClientinclude $(BUILD_EXECUTABLE)

5. 测试结果

  • 回到 Android 根目录下:
    • mmm ./external/binder/server/
    • mmm ./external/binder/client/
    • make snod
  • 编译成功后:
    • emulator -kernel kernel/goldfish/arch/arm/boot/zImage &
    • adb shell
  • 接下来的操作与输出如下:
    • 启动 FregServer 模块。
    • 启用一次 FregClient 观察到两个值:01
    • 启用第二次 FregClient 观察到两个值:12
  • 实测结果与 4.1 中的分析一致,说明这个实例已经成功运作了。

generic:/ # FregServer &
[1] 1139
generic:/ # FregClient
Read original value from FregService:
0.
Add value 1 to FregService.
Read the value from FregService again:
1.
generic:/ # FregClient
Read original value from FregService:
1.
Add value 1 to FregService.
Read the value from FregService again:
2.

阅读全文
0 0
原创粉丝点击