在分析以前,咱們首先要知道uevent做用是什麼。在此咱們先來看一個uevent機制的框架圖:html
該圖片來自:Linux設備模型(3)_Ueventlinux
經過圖片咱們能夠肯定uevent的做用:設備產生上報事件時會觸發uevent接口,uevent則經過netlink和kmod這兩種方式把事件上報到用戶空間。kmod會直接調用用戶空間的程序,netlink只是將事件上報到用戶空間。正則表達式
以前咱們分析的大部分設備驅動都會在/dev/目錄下建立節點給用戶使用。那麼在咱們調用device_create()後內核會作什麼呢?vim
如今咱們來分析device_create()的詳細調用關係:網絡
device_create() -> va_start(vargs, fmt); /* 初始化va_list可變參數變量 */ -> dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs); -> dev = kzalloc(sizeof(*dev), GFP_KERNEL); -> dev->devt = devt; /* 設置device成員 */ -> retval = device_register(dev); -> device_initialize(dev); /* 初始化device鏈表頭 */ -> device_add(dev); /* 添加device */ -> kobject_uevent(&dev->kobj, KOBJ_ADD); -> kobject_uevent_env(kobj, action, NULL); -> env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); /* 分配環境變量 */ -> if (uevent_helper[0] && !kobj_usermode_filter(kobj)) -> argv [0] = uevent_helper; /* 下面調用的就是uevent_helper程序 */ -> call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC); /* 調用應用程序argv[0] */ -> va_end(vargs);
爲了肯定調用程序,咱們能夠在代碼中添加打印語句,如8-14行:框架
1 if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { 2 char *argv [3]; 3 4 argv [0] = uevent_helper; 5 argv [1] = (char *)subsystem; 6 argv [2] = NULL; 7 8 int i; 9 for (i = 0; i < 2; ++i) { /* 參數 */ 10 printk("device: argv[%d] = %s\n", i, argv[i]); 11 } 12 for (i = 0; env[i]; ++i) { /* 環境變量 */ 13 printk("device: envp[%d] = %s", i, env[i]); 14 } 15 16 retval = add_uevent_var(env, "HOME=/"); 17 if (retval) 18 goto exit; 19 retval = add_uevent_var(env, 20 "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); 21 if (retval) 22 goto exit; 23 24 retval = call_usermodehelper(argv[0], argv, 25 env->envp, UMH_WAIT_EXEC); 26 }
從新編譯燒寫內核後,insmod某個模塊後能夠肯定uevent_helper爲/sbin/mdevide
/sbin/mdev定義在busybox的mdev.c中:函數
咱們使用SI4建立busybox工程後,打開mdev.c,分析mdev_main()函數:post
1 int mdev_main(int argc, char **argv) 2 { 3 char *action; 4 char *env_path; 5 RESERVE_CONFIG_BUFFER(temp,PATH_MAX); 6 7 xchdir("/dev"); 8 9 if (argc == 2 && !strcmp(argv[1],"-s")) { /* 判斷參數個數,若是不是mdev -s進入if */ 10 struct stat st; 11 12 xstat("/", &st); 13 root_major = major(st.st_dev); 14 root_minor = minor(st.st_dev); 15 16 recursive_action("/sys/block", 17 ACTION_RECURSE | ACTION_FOLLOWLINKS, 18 fileAction, dirAction, temp, 0); 19 20 recursive_action("/sys/class", 21 ACTION_RECURSE | ACTION_FOLLOWLINKS, 22 fileAction, dirAction, temp, 0); 23 24 } else { /* 熱拔插mdev -s */ 25 action = getenv("ACTION"); /* 設備驅動中ACTION = add */ 26 env_path = getenv("DEVPATH"); /* DEVPATH = /class/dma_test */ 27 if (!action || !env_path) 28 bb_show_usage(); 29 30 sprintf(temp, "/sys%s", env_path); /* temp = /sys/class/dma_test */ 31 if (!strcmp(action, "remove")) 32 make_device(temp, 1); 33 else if (!strcmp(action, "add")) { 34 make_device(temp, 0); 35 36 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) 37 load_firmware(getenv("FIRMWARE"), temp); 38 } 39 } 40 41 if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); 42 return 0; 43 }
此函數最終調用make_device(temp, 0)建立設備,調用層次以下:ui
make_device(temp, 0); -> device_name = bb_basename(path); -> if (ENABLE_FEATURE_MDEV_CONF) /* 若是配置了支持mdev.conf選項 */ -> fd = open("/etc/mdev.conf", O_RDONLY); /* 操做mdev.conf文件 */ -> if (!delete) /* 若是是建立設備節點 */ -> mknod(device_name, mode | type, makedev(major, minor) /* 建立節點 */
下面咱們來看看如何使用mdev.conf,參考工程中mdev.txt文件:
如設置初始化腳本/etc/init.d/rcS:
Here's a typical code snippet from the init script: [1] mount -t sysfs sysfs /sys [2] echo /bin/mdev > /proc/sys/kernel/hotplug [3] mdev -s Of course, a more "full" setup would entail executing this before the previous code snippet: [4] mount -t tmpfs mdev /dev [5] mkdir /dev/pts [6] mount -t devpts devpts /dev/pts
/etc/ndev.conf文件格式:
the format: <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>] The special characters have the meaning: @ Run after creating the device. $ Run before removing the device. * Run both after creating and before removing the device.
the format: <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>] The special characters have the meaning: @ Run after creating the device. $ Run before removing the device. * Run both after creating and before removing the device.
其中,
<device regex>:正則表達式,可參考:正則表達式 - 語法 | 菜鳥教程
<uid>:用戶ID
<gid>:組ID
<octal permissions>:/dev/dma_test的權限
<command>:命令
瞭解上面知識後,下一節開始編輯mdev.conf實現U盤自動掛載
在網絡文件系統根目錄中執行:
# vim etc/mdev.conf
添加一行:
sda[1-9]+ 0:0 660 * if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi
其中,
sda[1-9]+表示重複匹配1-9的數字屢次
*表示建立設備節點以後和刪除設備節點以前執行命令
命令表示若是ACTION是add,則掛載,不然取消掛載
效果以下圖,亂碼是因爲開發板不支持中文: