***************************************************************************************************************************
作者: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吗