当前位置: 代码迷 >> Android >> 基于goldfish跟android2.3.5学习之:开天辟地Android启动机制[三]
  详细解决方案

基于goldfish跟android2.3.5学习之:开天辟地Android启动机制[三]

热度:55   发布时间:2016-05-01 17:39:42.0
基于goldfish和android2.3.5学习之:开天辟地Android启动机制[三]

***************************************************************************************************************************
作者:EasyWave                                                                                 时间:2012.08.01

类别:Android系统源码分析                                                              声明:转载,请保留链接

注意:如有错误,欢迎指正。这些是我学习的日志文章......

***************************************************************************************************************************

         在我的博文基于goldfish和android2.3.5学习之:开天辟地Android启动机制[二]中,部分的介绍了uevent事件机制,这篇博文将更深入的详细分析uevent的机制以及如何android是如何透过内核传递过来的数据通过uevent机制检测设备的hotplug事件。同时也分析到了andorid_ids,还是先把get_android_id()函数拿出来分析一下吧,代码如下:

static int get_android_id(const char *id){    unsigned int i;    for (i = 0; i < ARRAY_SIZE(android_ids); i++) //查找android_ids表格        if (!strcmp(id, android_ids[i].name)) //比较android_ids中的name            return android_ids[i].aid; //返回andorid_ids表格中的aid    return 0;}

而andorid_ids是在android_filesystem_config.h文件中,具体的位置在andorid2.3.5/system/core/include/private中。具体的代码如下:

struct android_id_info {    const char *name;    unsigned aid;};static const struct android_id_info android_ids[] = {    { "root",      AID_ROOT, },    { "system",    AID_SYSTEM, },    { "radio",     AID_RADIO, },    { "bluetooth", AID_BLUETOOTH, },    { "graphics",  AID_GRAPHICS, },    { "input",     AID_INPUT, },    { "audio",     AID_AUDIO, },    { "camera",    AID_CAMERA, },    { "log",       AID_LOG, },    { "compass",   AID_COMPASS, },    { "mount",     AID_MOUNT, },    { "wifi",      AID_WIFI, },    { "dhcp",      AID_DHCP, },    { "adb",       AID_ADB, },    { "install",   AID_INSTALL, },    { "media",     AID_MEDIA, },    { "nfc",       AID_NFC, },    { "shell",     AID_SHELL, },    { "cache",     AID_CACHE, },    { "diag",      AID_DIAG, },    { "net_bt_admin", AID_NET_BT_ADMIN, },    { "net_bt",    AID_NET_BT, },    { "sdcard_rw", AID_SDCARD_RW, },    { "vpn",       AID_VPN, },    { "keystore",  AID_KEYSTORE, },    { "usb",       AID_USB, },    { "gps",       AID_GPS, },    { "inet",      AID_INET, },    { "net_raw",   AID_NET_RAW, },    { "net_admin", AID_NET_ADMIN, },    { "misc",      AID_MISC, },    { "nobody",    AID_NOBODY, },};#define android_id_count \    (sizeof(android_ids) / sizeof(android_ids[0]))  
宏定义如下代码:

/* This is the master Users and Groups config for the platform.** DO NOT EVER RENUMBER.*/#define AID_ROOT             0  /* traditional unix root user */#define AID_SYSTEM        1000  /* system server */#define AID_RADIO         1001  /* telephony subsystem, RIL */#define AID_BLUETOOTH     1002  /* bluetooth subsystem */#define AID_GRAPHICS      1003  /* graphics devices */#define AID_INPUT         1004  /* input devices */#define AID_AUDIO         1005  /* audio devices */#define AID_CAMERA        1006  /* camera devices */#define AID_LOG           1007  /* log devices */#define AID_COMPASS       1008  /* compass device */#define AID_MOUNT         1009  /* mountd socket */#define AID_WIFI          1010  /* wifi subsystem */#define AID_ADB           1011  /* android debug bridge (adbd) */#define AID_INSTALL       1012  /* group for installing packages */#define AID_MEDIA         1013  /* mediaserver process */#define AID_DHCP          1014  /* dhcp client */#define AID_SDCARD_RW     1015  /* external storage write access */#define AID_VPN           1016  /* vpn system */#define AID_KEYSTORE      1017  /* keystore subsystem */#define AID_USB           1018  /* USB devices */#define AID_GPS           1021  /* GPS daemon */#define AID_UNUSED1       1022  /* deprecated, DO NOT USE */#define AID_RFU1          1023  /* RFU */#define AID_RFU2          1024  /* RFU */#define AID_NFC           1025  /* nfc subsystem */#define AID_SHELL         2000  /* adb and debug shell user */#define AID_CACHE         2001  /* cache access */#define AID_DIAG          2002  /* access to diagnostic resources *//* The 3000 series are intended for use as supplemental group id's only. * They indicate special Android capabilities that the kernel is aware of. */#define AID_NET_BT_ADMIN  3001  /* bluetooth: create any socket */#define AID_NET_BT        3002  /* bluetooth: create sco, rfcomm or l2cap sockets */#define AID_INET          3003  /* can create AF_INET and AF_INET6 sockets */#define AID_NET_RAW       3004  /* can create raw INET sockets */#define AID_NET_ADMIN     3005  /* can configure interfaces and routing tables. */#define AID_MISC          9998  /* access to misc storage */#define AID_NOBODY        9999#define AID_APP          10000 /* first app user */
上面的都是部分摘录,如果要想看明白这些代码,你手边必须要有android2.3.5的源码,否则,这些你还是无法看明白的。还记得这个函数吗?void set_device_permission(int nargs, char **args),看我的上一篇博文。这里只摘录跟上面有关部分的代码,如下:

    ret = get_android_id(args[2]); //得到android_id定义的name,具体见:android_ids表格。    if (ret < 0) {        ERROR("invalid uid '%s'\n", args[2]);        free(tmp);        return;    }    uid = ret; //设置用户id    ret = get_android_id(args[3]); //得到android_id定义的id,具体见:android_ids表格,    if (ret < 0) {        ERROR("invalid gid '%s'\n", args[3]);        free(tmp);        return;    }    gid = ret; //设置group的id    add_dev_perms(name, attr, perm, uid, gid, prefix); //添加到链表中。
    以/dev/null            0666  root      root来分析吧,如何得到uid   

ret = get_android_id(args[2]);这个是抓取前面/dev/null 中的第三项,也就是红色加粗部分,透过root匹配之后,读取到了AID_ROOT。

     以/dev/null            0666   root       root来分析吧,如何得到gid   

ret = get_android_id(args[2]);这个是抓取前面/dev/null 中的第四项,也就是蓝色加粗部分,透过root匹配之后,读取到了AID_ROOT。

     

      现在再来详细介绍ueventd_main()没有分析完的代码,源码分析继续中。当然先说明源码的位置:在Andorid2.3.5源码system/core/init.c中。详细的代码如下:

int ueventd_main(int argc, char **argv){    struct pollfd ufd;    int nr;    char tmp[32];    open_devnull_stdio();    log_init();    INFO("starting ueventd\n");    get_hardware_name(hardware, &revision);    ueventd_parse_config_file("/ueventd.rc");    snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);    ueventd_parse_config_file(tmp); ////解析ueventd.xxxxxx.rc文件,比如:goldfish为ueventd.goldfish.rc文件,具体的分析可以参考ueventd.rc    device_init(); //初始化uevent socket,用于从Linux内核动态的抓取设备的状态变化,同时处理冷开机事件。     ufd.events = POLLIN; //POLLIN的意思,当内核有数据可读时,无阻塞的返回    ufd.fd = get_device_fd(); //得到相应的从内核传过来的有数据具体文件描述符fd.    while(1) { //循环操作        ufd.revents = 0;        nr = poll(&ufd, 1, -1);        if (nr <= 0)            continue;        if (ufd.revents == POLLIN)               handle_device_fd(); //处理具体的从内核传递过来的设备变化事件,像null、usb、ttyS0等等,很多吧。这是我的理解,如有不对之处还望见谅    } //至于内核的ueventd事件,就需要去理解linux设备驱动模型了,这个会在以后专门在linux内核栏目中详细的介绍这个,因为这个驱动模型很复杂。}

        应用程序如果需要检测设备的热插拔事件,一般会用到这个特殊的socket,在linux中就是这个NETLINK,我们可以透过这个socket中的NETLINK_KOBJ_UEVEN来检测设备的热插拔的动作。其实android的文件系统采用和busybox以及udev类似的方式。在busybox中采用的mdev来自动的抓取linux所有的设备驱动,一般是从sys目录中去抓取。自动根据驱动的名称来分配相应设备的主设备号和次设备号,以及是字符设备,还是块设备和网络设备。。如果我们能够用这个方式来理解andorid的文件系统,这样对于分析android的文件系统有很大的帮助。

        说了太多的废话了,还是先来具体看看device_init()函数吧,函数的具体位置在system/core/init/devices.c中,代码如下:

void device_init(void){    suseconds_t t0, t1;    struct stat info;    int fd;    device_fd = open_uevent_socket(); //建立socket,并且将进程ID和socket建立起连接    if(device_fd < 0)        return;    fcntl(device_fd, F_SETFD, FD_CLOEXEC);//这两句诗设置设备文件的属性,同时强制加锁    fcntl(device_fd, F_SETFL, O_NONBLOCK);    //因为android系统冷启动时,会在dev/下生成一个.coldboot_done文件。所以需要检查这个文件。    if (stat(coldboot_done, &info) < 0) { //查询coldboot_done的状态,并且存入info中,如果不成功的话,则倒计时进入冷启动。        t0 = get_usecs(); //得到当前的时间        coldboot("/sys/class");         coldboot("/sys/block");        coldboot("/sys/devices");        t1 = get_usecs();        fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);        close(fd);        log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));     } else {        log_event_print("skipping coldboot, already done\n"); //冷启动完成    }}

具体的分析下open_uevent_socket()函数,这个需要重点关注下,这个可是实时的监视着内核中设备驱动的变化。因此详细的分析一下。

static int open_uevent_socket(void){    struct sockaddr_nl addr;    int sz = 64*1024; // XXX larger? udev uses 16MB!    int on = 1;    int s;    memset(&addr, 0, sizeof(addr));    addr.nl_family = AF_NETLINK; //透过AF_NETLINK与内核进行通信    addr.nl_pid = getpid(); //得到进程PID    addr.nl_groups = 0xffffffff;     //定义socket描述符,这是一个异步通信机制,SOCK_DGRAM的意思是无连接不可靠的连接    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); //具体的事件为NETLINK_KOBJECT_UEVENT,对于这个需要对linux的设备驱动模型需要有一定的了解    if(s < 0) //如果失败,直接返回-1,也就是0xFFFFFF。        return -1;    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); //SO_RCVBUFFORCE是一个特殊的接收缓冲区,意思是不受接收缓冲区大小的上限限制    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); //SO_PASSCRED的意思是:允许接收进程辅助信息发送的信用证明。    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { //将进程和socket联系起来。        close(s);        return -1; //失败的话,返回-1.    }    return s; //成功返回}
   分析到了这一步之后,基本上ueventd机制基本上也差不多了。还是接上device_init()函数下部分的代码来回顾一下,部分代码如下:

    ufd.events = POLLIN;     ufd.fd = get_device_fd(); ///得到相应的从内核传过来的有数据具体文件描述符fd.    while(1) {        ufd.revents = 0; //先将实际动作的事件清零        nr = poll(&ufd, 1, -1); //监视发生的事件        if (nr <= 0) //如果无事件继续            continue;        if (ufd.revents == POLLIN) //如果监视到发生的事件,则进入具体的事件处理中,比如字符设备,块设备,还是网络设备。               handle_device_fd(); //具体的处理事件在这个函数中。    }
   对于ueventd的理解也许是比较困难一点,这个是因为需要对linux内核的设备驱动模型要有一定的理解,并且需要知道内核的设备驱动模式的运行机制有一定的理解。简单的讲一下,在linux内核中,只要是加载设备驱动,就会产生uevent事件。如果可以将内核的调试信息打印出来,[email protected] [email protected]息。android内核是如何启动ueventd的呢?在init.rc中有很详细的指示。init.rc的部分代码如下:

on early-init    start ueventdon initsysclktz 0loglevel 3# setup the global environment    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin    export LD_LIBRARY_PATH /vendor/lib:/system/lib    export ANDROID_BOOTLOGO 1    export ANDROID_ROOT /system    export ANDROID_ASSETS /system/app    export ANDROID_DATA /data    export EXTERNAL_STORAGE /mnt/sdcard    export ASEC_MOUNTPOINT /mnt/asec    export LOOP_MOUNTPOINT /mnt/obb
         看到了 on early-init 下的start uventd的吗?对就是这个。因为init是个守护进程,也是内核启动之后,第一个要调用运行的程序。而init进程会首先去抓取init.rc文件。至于内核启动之后,为什么说init是第一个运行的程序呢。这个会在以后会专门开辟一篇博文来详细讲述这个问题。这里就不详细说了,好了关于ueventd部分。已经基本上分析完了,其实ueventd就是初始化内核中所有的设备驱动,然后透过ueventd分配好所有的设备。当然,这个是需要根据内核的具体已有的设备驱动。


         未完,Andriod源码的分析还请继续关注《基于goldfish和android2.3.5学习之:开天辟地Android启动机制[四]》 。。。

 

2楼Thinkingword昨天 21:47
LZ是搞底层开发的吧,说的很详细,持续关注你以后的作品。
Re: wavemcu昨天 22:24
回复Thinkingword 做linux底层驱动和应用开发,目前了解下Android系统的运作。一起探讨哈!
1楼gzzaigcn昨天 15:39
楼主,楼主这里不涉及内核如何启动android吗
  相关解决方案