***************************************************************************************************************************
作者: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[3]);这个是抓取前面/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事件。如果可以将内核的调试信息打印出来,会看到诸如此类的add@xxxxxxx 以及移除设备驱动的时候会出现remove@xxxxxxx等等信息。android内核是如何启动ueventd的呢?在init.rc中有很详细的指示。init.rc的部分代码如下:
on early-init
start ueventd
on init
sysclktz 0
loglevel 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源码的分析还请继续关注《基于android2.3.5学习之:开天辟地Android启动机制[四]》 。。。