(懒人最近想起我还有csdn好久没打理了,这个android init躺在我的草稿箱中快5年了,稍微改改发出来吧)
ueventd主要是负责设备节点的创建、权限设定等一些列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理。
ueventd实际和init是同一个binary,只是走了不同分支,可参看前一部分。
ueventd的整体代码比较简单,主要是三部分:
解析ueventd.rc 初始化设备信息 循环polling uevent消息主函数及相关功能如下如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
int
ueventd_main(
int
argc,
char
**argv)
{
// 和init一样,没有std输入输出
open_devnull_stdio();
// 初始化kernel log,让ueventd的log,通过kernel printk的log输出到串口中
klog_init();
//解析和处理ueventd的rc文件
import_kernel_cmdline(
0
, import_kernel_nv);
get_hardware_name(hardware, &revision);
ueventd_parse_config_file(/ueventd.rc);
snprintf(tmp, sizeof(tmp), /ueventd.%s.rc, hardware);
ueventd_parse_config_file(tmp);
// 设备初始化
device_init();
// polling uevent消息,对设备进行管理
ufd.events = POLLIN;
ufd.fd = get_device_fd();
while
(
1
) {
ufd.revents =
0
;
nr = poll(&ufd,
1
, -
1
);
if
(nr <=
0
)
continue
;
if
(ufd.revents == POLLIN)
handle_device_fd();
// polling到消息,处理event消息
}
}
|
处理和解析ueventd.rc
这部分相比init.rc来说,巨简单,没什么特别的。主要是通过rc文件,来控制目录节点的权限。如:
1
2
3
4
5
6
7
|
/dev/ttyUSB2
0666
radio radio
/dev/ts0710mux*
0640
radio radio
/dev/ppp
0666
radio vpn
# sysfs properties
/sys/devices/virtual/input/input* enable
0666
system system
/sys/devices/virtual/input/input* poll_delay
0666
system system
|
设备初始化
kernel在加载设备时,会通过socket发送uevent事件给userspace, 在init里,通过接受这些uevent事件,来创建设备的节点。主要函数是device_init()
初始化函数为device_init,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
void
device_init(
void
)
{
suseconds_t t0, t1;
struct stat info;
int
fd;
sehandle = NULL;
if
(is_selinux_enabled() >
0
) {
sehandle = selinux_android_file_context_handle();
}
/* is 256K enough? udev uses 16MB! */
device_fd = uevent_open_socket(
256
*
1024
,
true
);
if
(device_fd <
0
)
return
;
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
fcntl(device_fd, F_SETFL, O_NONBLOCK);
if
(stat(coldboot_done, &info) <
0
) {
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
, ((
long
) (t1 - t0)));
}
else
{
log_event_print(skipping coldboot, already done
);
}
}
|
open_uevent_socket();
这是打开uevent的socket。这里的uevent是用到netlink中的内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)功能,是内核和用户态进行双向数据传输的非常好的方式,除了eventd外,netd和vold也是使用uevent的。
初始化函数中,比较要注意的coldboot这个函数,字面意思是冷启动,它的作用是,对那些在uventd启动前,已经add上的驱动,再重新操作一下,让他们再发一次event消息,上层号针对性处理。
这里对 /sys/class,/sys/block和/sys/devices下的设备遍历一遍:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
static
void
do_coldboot(DIR *d)
{
struct dirent *de;
int
dfd, fd;
dfd = dirfd(d);
fd = openat(dfd, uevent, O_WRONLY);
if
(fd >=
0
) {
write(fd, add
,
4
);
close(fd);
handle_device_fd();
}
while
((de = readdir(d))) {
DIR *d2;
if
(de->d_type != DT_DIR || de->d_name[
0
] ==
'.'
)
continue
;
fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
if
(fd <
0
)
continue
;
d2 = fdopendir(fd);
if
(d2 ==
0
)
close(fd);
else
{
do_coldboot(d2);
closedir(d2);
}
}
}
|
uevent消息处理
初始化好了之后,daemon程序只要polling新的event事件即可,polling到了,就调用handle_device_fd();来处理,可以看看这个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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] =
''
;
msg[n+
1
] =
''
;
struct uevent uevent;
parse_event(msg, &uevent);
handle_device_event(&uevent);
handle_firmware_event(&uevent);
}
}
|
功能就是接受内核发的event消息,然后parser此消息,处理对应的消息事件。
这里:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
static
void
handle_device_event(struct uevent *uevent)
{
if
(!strcmp(uevent->action,add) || !strcmp(uevent->action, change))
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,创建/删除设备节点,同时以ueventd.rc中描述的权限设置更新一些节点权限。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
static
void
handle_firmware_event(struct uevent *uevent)
{
pid_t pid;
int
ret;
if
(strcmp(uevent->subsystem, firmware))
return
;
if
(strcmp(uevent->action, add))
return
;
/* we fork, to avoid making large memory allocations in init proper */
pid = fork();
if
(!pid) {
process_firmware_event(uevent);
exit(EXIT_SUCCESS);
}
}
|