在编写linux设备驱动程序的时候,很多时候都是利用mknod命令来手动创建设备节点的,带上名字和主次设备号就可以在/dev目录下生成设备节点。同样Android沿用了linux内核,很多设备驱动的节点是又是什么时候创建的呢?
Android的service大都是编译成可执行文件以命令的格式,我们注意到在init.rc中又这么个service值得关注下。
Android的服务不是选项不是disabled并且带core和main的选项的服务都是需要开机自动加载的服务。而ueventd是由system/core/init/ueventd.c编译而成的。
Ueventd的main函数做的事情比较多,首先是要解析根文件系统下的ueventd.rc以及ueventd.${hardware}.rc。
接收的uevent的小心不能超过1024个字节,如果超出就算溢出将不会处理。如果接收的uevent有效,解析这个uevent会根据设备的类型来解析。之后handle_device_event会处理设备的event。而handle_firmware_event则是和某些设备需要firmware回去处理firmware的加载。
首先得到设备的名字,然后是创建一些设备的子目录,如果uevent->subsystem是usb,就需要创建一个/dev/bus/usb的目录。如果是uevent->subsystem是graphic的话,就需要创建一个/dev/graphics的目录,按照这样依次比较下去,创建设备类所需要的子目录。
如果uevent的action是设备添加,就会调用make_device来创建设备节点。
通过get_device_perm得到设备的访问权限,makedev根据主设备号和次设备号得到dev。设置临时setegid,然后又mknod就在设备相应的目录下面创建了设备节点。最后将设备的egid设备为AID_ROOT。到这里整个android的设备目录和节点就创建完成了。
在kernel自解压模块加载完成之后,会去运行android第一个应用程序init。在init.c的main函数中。
System/core/init/init.c
int main(int argc, char **argv)
{
……
action_for_each_trigger("boot", action_add_queue_tail);
……
}
在init进程解析init.rc脚本完成后,在onboot的最后两句是classstart main和core,其中class
Start是命令,在keyword.h中定义了class_start对应的function实际就是do_class_start。
System/core/init/builtins.c
int do_class_start(int nargs, char **args)
{
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}
System/core/init/init_parser.c
void service_for_each_class(const char *classname, void (*func)(struct service *svc))
{
……
list_for_each(node, &service_list)
{
svc = node_to_item(node, struct service, slist);
if (!strcmp(svc->classname, classname))
{
func(svc);
}
}
}
在之前解析init.rc脚本的时候,service会被放在service_list的链表里。接下来就是要执行service_for_each_class的func(svc),也就是service_start_if_not_disabled。
System/core/init/builtins.c
static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED))
{
service_start(svc, NULL);
}
}
Android的service大都是编译成可执行文件以命令的格式,我们注意到在init.rc中又这么个service值得关注下。
service ueventd /sbin/ueventd
class core
critical
Android的服务不是选项不是disabled并且带core和main的选项的服务都是需要开机自动加载的服务。而ueventd是由system/core/init/ueventd.c编译而成的。
System/core/init/ueventd.c
int ueventd_main(int argc, char **argv)
{
……
ueventd_parse_config_file("/ueventd.rc");
……
snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
ueventd_parse_config_file(tmp);
……
device_init();
……
while(1) {
ufd.revents = 0;
nr = poll(&ufd, 1, -1);
if (nr <= 0)
continue;
if (ufd.revents == POLLIN)
handle_device_fd();
}
}
Ueventd的main函数做的事情比较多,首先是要解析根文件系统下的ueventd.rc以及ueventd.${hardware}.rc。
System/core/init/devices.c
void handle_device_fd()
{
……
char msg[UEVENT_MSG_LEN+2];
int n;
while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
continue;
msg[n] = '\0';
msg[n+1] = '\0';
struct uevent uevent;
parse_event(msg, &uevent);
handle_device_event(&uevent);
handle_firmware_event(&uevent);
}
}
接收的uevent的小心不能超过1024个字节,如果超出就算溢出将不会处理。如果接收的uevent有效,解析这个uevent会根据设备的类型来解析。之后handle_device_event会处理设备的event。而handle_firmware_event则是和某些设备需要firmware回去处理firmware的加载。
System/core/init/devices.c
static void handle_device_event(struct uevent *uevent)
{
if (!strcmp(uevent->action,"add"))
fixup_sys_perms(uevent->path);
if (!strncmp(uevent->subsystem, "block", 5)) {
handle_block_device_event(uevent);
} else if (!strncmp(uevent->subsystem, "platform", 8)) {
handle_platform_device_event(uevent);
} else {
handle_generic_device_event(uevent);
}
}
首先得到设备的名字,然后是创建一些设备的子目录,如果uevent->subsystem是usb,就需要创建一个/dev/bus/usb的目录。如果是uevent->subsystem是graphic的话,就需要创建一个/dev/graphics的目录,按照这样依次比较下去,创建设备类所需要的子目录。
System/core/init/devices.c
static void handle_device(const char *action, const char *devpath, const char *path, int block, int major, int minor, char **links)
{
……
if(!strcmp(action, "add")) {
make_device(devpath, path, block, major, minor);
……
}
如果uevent的action是设备添加,就会调用make_device来创建设备节点。
System/core/init/devices.c
static void make_device(const char *path, const char *upath, int block, int major, int minor)
{
……
mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
dev = makedev(major, minor);
setegid(gid);
mknod(path, mode, dev);
chown(path, uid, -1);
setegid(AID_ROOT);
}
通过get_device_perm得到设备的访问权限,makedev根据主设备号和次设备号得到dev。设置临时setegid,然后又mknod就在设备相应的目录下面创建了设备节点。最后将设备的egid设备为AID_ROOT。到这里整个android的设备目录和节点就创建完成了。