Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android init進程分析 ueventd

android init進程分析 ueventd

編輯:關於Android編程

(懶人最近想起我還有csdn好久沒打理了,這個android init躺在我的草稿箱中快5年了,稍微改改發出來吧)

ueventd主要是負責設備節點的創建、權限設定等一些列工作。服務通過使用uevent,監控驅動發送的消息,做進一步處理。
ueventd實際和init是同一個binary,只是走了不同分支,可參看前一部分。

ueventd的整體代碼比較簡單,主要是三部分:

解析ueventd.rc 初始化設備信息 循環polling uevent消息

主函數及相關功能如下如下:

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文件,來控制目錄節點的權限。如:

 

/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,如下

 

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下的設備遍歷一遍:

 

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);
        }
    }
}
write(fd, add , 4)激活內核,重發add事件的uevent,handle_device_fd();針對event消息,做響應的處理。

 

 

uevent消息處理

初始化好了之後,daemon程序只要polling新的event事件即可,polling到了,就調用handle_device_fd();來處理,可以看看這個函數:

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此消息,處理對應的消息事件。

這裡:

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中描述的權限設置更新一些節點權限。

 

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);
    }
}
如果有協處理器, 還要下載協處理器的firmware,這裡是處理協處理器要下載firmware的指令,fork一個子進程處理。



 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved