1. 程式人生 > >Android ueventd淺析

Android ueventd淺析

platform
  mstar828
  android 5.0.1

在linux2.6之後,udev取代了devfs,但是在android中卻沒有udev或者mdev1,而是由ueventd程序實現了類似功能(管理裝置節點許可權、建立裝置節點)。

ueventd通過兩種方式建立裝置節點:

  • 靜態,ueventd啟動時,根據在sysfs中預定義的uevent資訊建立裝置節點;
  • 動態,系統執行過程中,當接收到核心uevent事件時(如插入u盤),動態建立裝置節點。

1. ueventd啟動過程

在init.rc中,當觸發條件為“early-init”時ueventd被啟動:
system/core/rootdir/init.rc

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000

    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
    write /sys/fs/selinux/checkreqprot 0

    # Set the security context for the init process.
    # This should occur before anything else
(e.g. ueventd) is started. setcon u:r:init:s0 # Set the security context of /adb_keys if present. restorecon /adb_keys start ueventd

在執行環境中檢視命令“/sbin/ueventd”,其實它是”/init”的軟連結:

[email protected]:/ # ls sbin -l                                                 
-rwxr-x--- root     root    499152
1970-01-01 08:00 adbd -rwxr-x--- root root 3325472 1970-01-01 08:00 healthd lrwxrwxrwx root root 1970-01-01 08:00 ueventd -> ../init lrwxrwxrwx root root 1970-01-01 08:00 watchdogd -> ../init

通過分析Android.mk可知,ueventd.c、watchdog.c與init.c被編譯成了同一個可執行檔案“/init”,並建立了軟連結“/sbin/ueventd”、“/sbin/watchdog”指向“/init”:

system/core/init/Android.mk

LOCAL_SRC_FILES:= \
    builtins.c \
    init.c \
    devices.c \
    property_service.c \
    util.c \
    parser.c \
    keychords.c \
    signal_handler.c \
    init_parser.c \
    ueventd.c \
    ueventd_parser.c \
    watchdogd.c

......

LOCAL_MODULE:= init

......

# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init
SYMLINKS := \
    $(TARGET_ROOT_OUT)/sbin/ueventd \
    $(TARGET_ROOT_OUT)/sbin/watchdogd

原來在檔案init.c的main()函式中有一個巧妙的處理:可以通過判斷第一個執行引數來啟動不同的程序:

  • 如果執行“./ueventd”,進入第一個條件分支,執行uevent_main()函式;
  • 如果執行“./watchdog”,進入第二個條件分支,執行watchdogd_main()函式;
  • 如果執行”./init”,跳過所有分支條件,繼續執行main()函式。

system/core/init/init.c

int main(int argc, char **argv)
{

    ......

    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);

    if (!strcmp(basename(argv[0]), "watchdogd"))
        return watchdogd_main(argc, argv);

    ......

}

因此,指令碼init.rc中的命令“start ueventd”最終執行的是ueventd_main()函式。

2. ueventd程式碼分析

2.1 main

ueventd_main()函式就是ueventd程序的主體,實現了以下幾個功能:

  • 解析ueventd.rc檔案,管理裝置節點許可權;
  • 遞迴掃描/sys目錄,根據uevent檔案,靜態建立裝置節點;
  • 通過netlink獲取核心uevent事件,動態建立裝置節點。

system/core/init/ueventd.c

int ueventd_main(int argc, char **argv)
{
    struct pollfd ufd;
    int nr;
    char tmp[32];

    INFO("starting ueventd\n");

    ......

    // 解析ueventd.rc檔案
    ueventd_parse_config_file("/ueventd.rc");

    // 解析廠商相關的ueventd.$(TARGET_BOARD_PLATFORM).rc檔案
    snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
    ueventd_parse_config_file(tmp);

    // 建立netlink sockfd(全域性變數device_fd),用於監聽uevent事件
    // 執行coldboot,遞迴掃描/sys目錄下uevent檔案,建立相應裝置節點
    device_init();

    ufd.events = POLLIN;
    // 獲取device_init()建立的sockfd
    ufd.fd = get_device_fd();

    while(1) {
        ufd.revents = 0;
        // 通過sockfd監聽核心uevent事件
        nr = poll(&ufd, 1, -1);
        if (nr <= 0)
            continue;
        if (ufd.revents & POLLIN)
            // 當接收到核心uevent事件時,建立相應裝置節點
            handle_device_fd();
    }
}

2.2 device_init

device_init()函式做了兩件事:

  • uevent_open_socket(),建立netlink套接字,並賦值給全域性變數device_fd,用於後續的uevent事件監聽,uevent_open_socket()函式涉及到netlink機制與socket程式設計,具體分析請參考:uevent_open_socket()淺析

  • coldboot(),遞迴掃描/sys目錄下的uevent節點,然後寫入字串“add”,強制觸發核心uevent事件。

這裡我們對coldboot()函式程式碼進行重點分析,呼叫關係如下:

main() -> device_init()-> coldboot() -> do_coldboot()

system/core/init/devices.c

void device_init(void)
{

    ......
    // 建立netlink sockfd(全域性變數device_fd),用於監聽uevent事件
    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);

    // 遞迴掃描/sys目錄下uevent檔案,建立相應裝置節點
    if (stat(coldboot_done, &info) < 0) {

        ......

        coldboot("/sys/class");
        coldboot("/sys/block");
        coldboot("/sys/devices");

        ......

    }

    ......

}
static void coldboot(const char *path)
{
    DIR *d = opendir(path);
    if(d) {
        do_coldboot(d);
        closedir(d);
    }
}
static void do_coldboot(DIR *d)
{
    struct dirent *de;
    int dfd, fd;

    // 獲得目錄檔案描述符
    dfd = dirfd(d);

    // 開啟目錄中的uevent節點,寫入“add\n”觸發核心uevent事件並處理
    fd = openat(dfd, "uevent", O_WRONLY);
    if(fd >= 0) {
        write(fd, "add\n", 4);
        close(fd);
        handle_device_fd();
    }

    // 遞迴呼叫do_coldboot(),掃描uevent節點
    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);
        }
    }
}

2.3 handle_device_id

在main()函式中通過poll監聽到核心uevent事件後,由handler_device_id()函式進行處理:

  • 解析uevent事件;
  • 動態建立裝置節點。

這一部分程式碼的呼叫關係如下:

main() -> handle_device_id() -> handle_device_event() -> handle_generic_device_event() -> handle_device() -> make_device() -> mknode()

system/core/init/devices.c

void handle_device_fd()
{
    char msg[UEVENT_MSG_LEN+2];
    int n;

    // 通過sockfd呼叫recvmsg()獲取核心uevent事件,以字串形式存入msg
    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {

        ......

        struct uevent uevent;
        // 將字串msg解析成uevent
        parse_event(msg, &uevent);

        ......
        // 處理裝置相關uevent事件
        handle_device_event(&uevent);
        // 處理韌體相關uevent事件(暫不分析)
        handle_firmware_event(&uevent);
    }
}
static void handle_device_event(struct uevent *uevent)
{
    ......

        handle_generic_device_event(uevent);

    ......

}
static void handle_generic_device_event(struct uevent *uevent)
{
    ......

    // 根據uevent事件中子系統名稱,建立/dev目錄及其子目錄
    } else if(!strncmp(uevent->subsystem, "input", 5)) {
        base = "/dev/input/";
        make_dir(base, 0755);
    } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
        base = "/dev/mtd/";
        make_dir(base, 0755);
    } else if(!strncmp(uevent->subsystem, "sound", 5)) {
        base = "/dev/snd/";
        make_dir(base, 0755);
    } else if(!strncmp(uevent->subsystem, "misc", 4) &&
                 !strncmp(name, "log_", 4)) {
        kernel_logger();
        base = "/dev/log/";
        make_dir(base, 0755);
        name += 4;
    } else
        base = "/dev/";

    links = get_character_device_symlinks(uevent);

    if (!devpath[0])
        snprintf(devpath, sizeof(devpath), "%s%s", base, name);

    // 根據uevent事件中的資訊建立/刪除節點及連結
    handle_device(uevent->action, devpath, uevent->path, 0,
            uevent->major, uevent->minor, links);
}
static void handle_device(const char *action, const char *devpath,
        const char *path, int block, int major, int minor, char **links)
{
    ......

    // 當uevent事件中的atcion為“add”時,建立節點及連結
    if(!strcmp(action, "add")) {
        make_device(devpath, path, block, major, minor, (const char **)links);
        if (links) {
            for (i = 0; links[i]; i++)
                make_link(devpath, links[i]);
        }
    }

    // 當uevent事件中的atcion為“remove”,刪除連結
    if(!strcmp(action, "remove")) {
        if (links) {
            for (i = 0; links[i]; i++)
                remove_link(devpath, links[i]);
        }
        unlink(devpath);
    }

    ......
}
static void make_device(const char *path,
                        const char *upath UNUSED,
                        int block, int major, int minor,
                        const char **links)
{
    ......

    // 合成裝置號
    dev = makedev(major, minor);
    ......
    // 根據檔案路徑、許可權、裝置號建立節點
    mknod(path, mode, dev);

    ......
}
  1. 由busybox提供的簡化版udev,適用於嵌入式應用場合

相關推薦

Android ueventd淺析

platform   mstar828   android 5.0.1 在linux2.6之後,udev取代了devfs,但是在android中卻沒有udev或者mdev1,而是由ueventd程序實現了類似功能(管理裝置節點許可權、建立裝

Android ClassLoader淺析

前言 最近在看Tinker的原理,發現核心是通過ClassLoader做的,由於之前也從未接觸過ClassLoader趁著上週末看了安卓ClassLoader相關原始碼,這裡分享一發安卓的ClassLoader和熱更新的實現原理。 ClassLoader 首先我們要知道,程式

Android Service淺析(上)

什麼是Service? Service作為構建應用的四大元件之一,它用來執行長時間的後臺操作且不使用使用者介面,比如網路事務處理、檔案I/O的讀寫、音樂的播放。但是你仔細考慮這個定義可能會產生一個疑問:在Java中並沒有提供所謂的Service元件,一樣要完成

Android圖解淺析事件攔截機制

當Android系統捕獲到使用者的各種輸入事件後,如何準確的傳遞給真正的需要這個事件的控制元件?Android提供了一整套完善的事件傳遞、處理機制,來幫助開發者完成準確的事件分配與處理,這裡我就不分析原始碼了,簡單點,圖形化分發過程,便於理解,待到後期分析dispatchT

Android AIDL淺析(一)

一、什麼是AIDL? AIDL是一個縮寫,全稱是Android Interface Definition Language,也就是Android介面定義語言; AIDL是進行程序間通訊的常用方式 二、怎麼使用AIDL? 總體來說,使用AIDL的過程可

Android Service 淺析

這篇部落格用如下的結構來講解Service: 1.先看一下Service是什麼。 A Service is an application component that can perform long-running operations in the backgroun

android音訊淺析

 Android Framework的音訊子系統中,每一個音訊流對應著一個AudioTrack類的一個例項,每個AudioTrack會在建立時註冊到AudioFlinger中,由AudioFlinger把所有的AudioTrack進行混合(Mixer),然後輸送到Audio

Android BLE淺析

這篇部落格想寫很久了,只是之前一直提不起勁,剛好最近還是一如既往的閒得蛋疼,那就寫寫吧,免得自己都忘了!    剛進公司的時候,做的就是BLE的專案,隨著這個專案的不了了之,我也忘了這事。    BLE的全名是 Bluetooth Low Energy 就是低功耗藍芽的意

android linker 淺析

不同映像間的函式和資料引用都是通過它們實現的。GOT(全域性偏移表)給出了映像中所有被引用符號(函式或變數)的值。每個普通PLT表項相當於一個函式的樁函式(stub),支援懶繫結的情況下,當發生對外部函式的呼叫時,程式會通過PLT表將控制交給動態聯結器,後者解析出函式的絕對地址,修改GOT中相應的值,之後的呼

Android屬性allowBackup安全風險淺析

adding 條件 mission android手機 code 16px ans xtra ddl 1.allowBackup安全風險描述Android API Level 8及其以上Android系統提供了為應用程序數據的備份和恢復功能,此功能的開關決定於該應用程序中A

Android查缺補漏(線程篇)-- IntentService的源碼淺析

per .com 隨著 nds public message 這一 這樣的 系統 本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8975114.html 在Android中有兩個比較容易弄混的概念,Se

Android 熱修復 Tinker接入及源碼淺析

uil obj 安全 Language num sse b2c rom 其中 一、概述 放了一個大長假,happy,先祝大家2017年笑口常開。 假期中一行代碼沒寫,但是想著馬上要上班了,趕緊寫篇博客回顧下技能,於是便有了本文。 熱修復這項技術,基本上已經成為項目比較

Android Hook框架adbi原始碼淺析(二)

二、libbase 其實上面載入完SO庫後,hook的功能我們完全可以自己在動態庫中實現。而adbi作者為了方便我們使用,編寫了一個通用的hook框架工具即libbase庫。libbase依然在解決兩個問題:1.獲取要hook的目標函式地址;2.給函式打二進位制補丁即inline hook。 關於獲取ho

Android Hook框架adbi原始碼淺析(一)

adbi(The Android Dynamic Binary Instrumentation Toolkit)是一個Android平臺通用hook框架,基於動態庫注入與inline hook技術實現。該框架由兩個主要模組構成,1.hijack負責將動態庫注入到目標程序;2.libbase提供動態庫本身,它實

淺析Android Camera開發中的三個尺寸和三種變形 貢獻一個自適配Picturesize和Previewsiz

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android Service生命週期淺析

Service 作為 Android四大元件之一,應用非常廣泛。和Activity一樣,Service 也有一系列的生命週期回撥函式,我們可以用來監測 Service狀態變化,並且在適當的時候執行適當的工作。 Service生命週期圖 1. 生命週期狀態 Service生命週期流程

Android 自定義view淺析

public class SquareImageView extends View { private Paint mPaint; private Bitmap bitmap; int left; int top; //這個構造方法,是new物件,例項化物件的

Android Binder機制淺析

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

AOP淺析以及Android對AOP的應用

一、前言 大家都知道OOP,即Object-Oriented Programming,面向物件程式設計。本篇我們要講的是AOP,即 Aspect-Oriented Programming,面向切面(方面)程式設計。平常我們開發都是用OOP的程式設計思想,這種思想的精髓是把問題模組化,

Android之AppWidget 開發淺析

什麼是AppWidget   AppWidget 即桌面小部件,也叫桌面控制元件,就是能直接顯示在Android系統桌面上的小程式,先看圖:                    圖中我用黃色箭頭指示的即為AppWidget,一些使用者使用比較頻繁的程式,可以做成A