Android設備目錄和節點的創建

    在編寫linux設備驅動程序的時候,很多時候都是利用mknod命令來手動創建設備節點的,帶上名字和主次設備號就可以在/dev目錄下生成設備節點。同樣Android沿用了linux內核,很多設備驅動的節點是又是什麼時候創建的呢? 

    在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 maincore,其中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_classfunc(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的設備目錄和節點就創建完成了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章