linux kernel啓動過程中創建VFS,爲各驅動設備在sys/class, sys/devices, sys/block創建目錄和文件,分配kset和kobject,uevent文件, 然後發送uevent事件到用戶空間,這些目錄和文件創建需要用戶空間實際創建設備文件,即在/dev目錄下創建設備文件,供用戶空間使用。
具體過程參考uevent機制。
用戶空間還沒有啓動時,uevent事件存在緩存中,等到用戶空間啓動後,uevent進程獲取緩存中uevent事件進行處理。並且監聽新的uevent事件進行處理,比如熱插拔事件。
uevent事件使用netlink方式通信。
設備文件創建
int ueventd_main(int argc, char **argv)
{
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
umask(000);
/* Prevent fire-and-forget children from becoming zombies.
* If we should need to wait() for some children in the future
* (as opposed to none right now), double-forking here instead
* of ignoring SIGCHLD may be the better solution.
*/
signal(SIGCHLD, SIG_IGN);
open_devnull_stdio();
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE("ueventd started!\n");
selinux_callback cb;
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
char hardware[PROP_VALUE_MAX];
property_get("ro.hardware", hardware);
ueventd_parse_config_file("/ueventd.rc"); //解析編譯階段定義好的文件,定義了各種文件權限屬性等
ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
device_init(); //處理kernel上報uevent事件,完成coldboot_done
pollfd ufd;
ufd.events = POLLIN;
ufd.fd = get_device_fd();
//繼續監聽uevent事件進行處理
while (true) {
ufd.revents = 0;
int nr = poll(&ufd, 1, -1);
if (nr <= 0) {
continue;
}
if (ufd.revents & POLLIN) {
handle_device_fd();
}
}
return 0;
}
void device_init() {
sehandle = NULL;
if (is_selinux_enabled() > 0) {
sehandle = selinux_android_file_context_handle();
selinux_status_open(true);
}
/* is 256K enough? udev uses 16MB! */
device_fd = uevent_open_socket(256*1024, true);
if (device_fd == -1) {
return;
}
fcntl(device_fd, F_SETFL, O_NONBLOCK);
if (access(COLDBOOT_DONE, F_OK) == 0) {
NOTICE("Skipping coldboot, already done!\n");
return;
}
Timer t;
coldboot("/sys/class"); // 處理sys/class 目錄下各種文件
coldboot("/sys/block");// 處理sys/block目錄下各種文件
coldboot("/sys/devices");// 處理sys/devices目錄下各種文件
close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000)); 創建"/dev/.coldboot_done",表示coldboot完成
NOTICE("Coldboot took %.2fs.\n", t.duration());
}
coldboot–>do_coldboot–
static void do_coldboot(DIR *d)
{
struct dirent *de;
int dfd, fd;
dfd = dirfd(d);
fd = openat(dfd, "uevent", O_WRONLY);//打開目錄下的uevent文件
if(fd >= 0) {
write(fd, "add\n", 4); //向uevent寫add,觸發kernel向用戶空間發送add事件
close(fd);
handle_device_fd(); //監聽處理add事件
}
//繼續處理子目錄,遞歸
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);
}
}
}
#define UEVENT_MSG_LEN 2048
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] = '\0';
msg[n+1] = '\0';
struct uevent uevent;
parse_event(msg, &uevent);
if (sehandle && selinux_status_updated() > 0) {
struct selabel_handle *sehandle2;
sehandle2 = selinux_android_file_context_handle();
if (sehandle2) {
selabel_close(sehandle);
sehandle = sehandle2;
}
}
handle_device_event(&uevent); //事件處理
handle_firmware_event(&uevent);
}
}
static void handle_device_event(struct uevent *uevent)
{
if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
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);//創建文件
}
}
創建各種設備文件, /dev目錄下
static void handle_generic_device_event(struct uevent *uevent)
{
const char *base;
const char *name;
char devpath[DEVPATH_LEN] = {0};
char **links = NULL;
name = parse_device_name(uevent, 64);
if (!name)
return;
struct ueventd_subsystem *subsystem =
ueventd_subsystem_find_by_name(uevent->subsystem);
if (subsystem) {
const char *devname;
switch (subsystem->devname_src) {
case DEVNAME_UEVENT_DEVNAME:
devname = uevent->device_name;
break;
case DEVNAME_UEVENT_DEVPATH:
devname = name;
break;
default:
ERROR("%s subsystem's devpath option is not set; ignoring event\n",
uevent->subsystem);
return;
}
if (!assemble_devpath(devpath, subsystem->dirname, devname))
return;
mkdir_recursive_for_devpath(devpath);
} else if (!strncmp(uevent->subsystem, "usb", 3)) {
if (!strcmp(uevent->subsystem, "usb")) {
if (uevent->device_name) {
if (!assemble_devpath(devpath, "/dev", uevent->device_name))
return;
mkdir_recursive_for_devpath(devpath);
}
else {
/* This imitates the file system that would be created
* if we were using devfs instead.
* Minors are broken up into groups of 128, starting at "001"
*/
int bus_id = uevent->minor / 128 + 1;
int device_id = uevent->minor % 128 + 1;
/* build directories */
make_dir("/dev/bus", 0755);
make_dir("/dev/bus/usb", 0755);
snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
make_dir(devpath, 0755);
snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
} else {
/* ignore other USB events */
return;
}
} else if (!strncmp(uevent->subsystem, "graphics", 8)) {
base = "/dev/graphics/";
make_dir(base, 0755);
} else if (!strncmp(uevent->subsystem, "drm", 3)) {
base = "/dev/dri/";
make_dir(base, 0755);
} else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
base = "/dev/oncrpc/";
make_dir(base, 0755);
} else if (!strncmp(uevent->subsystem, "adsp", 4)) {
base = "/dev/adsp/";
make_dir(base, 0755);
} else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
base = "/dev/msm_camera/";
make_dir(base, 0755);
} 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)) {
INFO("kernel logger is deprecated\n");
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);
handle_device(uevent->action, devpath, uevent->path, 0,
uevent->major, uevent->minor, links);
}
ueventd.rc中各種權設置
保存權限
ueventd_parse_config_file("/ueventd.rc");----》parse_config–》parse_line—》parse_line_device–》
set_device_permission–》add_dev_perms:
添加到dev_perms鏈表中。
前面接收處理uevent事件時,fixup_sys_perms中設置權限。