android init进程分析 ueventd — 设备节点的创建、固件更新过程

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;

    intfd;

 

    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;

    intdfd, 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);

        }

    }

}

write(fd, add , 4)激活内核,重发add事件的uevent,handle_device_fd();针对event消息,做响应的处理。

 

 

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()

{

    charmsg[UEVENT_MSG_LEN+2];

    intn;

    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;

    intret;

 

    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);

    }

}

如果有协处理器, 还要下载协处理器的firmware,这里是处理协处理器要下载firmware的指令,fork一个子进程处理。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值