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);
......
}
- 由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