Udev詳解

Udev詳解


udev
linux用戶空間設備管理(Linux userspace device management),在kernel 2.6.13後被使用,動態提供了在系統中實際存在的設備節點。
udev與不同的發佈版本有很強的依賴性。一個系統如果沒有安裝正確的udev版本,有可能造成無法啓動或者工作不正常。udev的開發團隊也不推薦使用新版本替代系統當前版本的udev.
隨着udev發佈的工具和規則文件可以改變爲己用,不是公共的API。不要直接從外部程序調用/lib/udev目錄下的私有工具,因爲在下一個發佈版本中有可能不存在這個私有工具。獲取udev相關信息只通過udevadm和libudev。在/lib/udev和/dev/.udev目錄下的所有Tools和rules都是私有的,可能在不同版本中不同。
udev要求:
  支持sysfs, procfs, signalfd, inotify,unix domain sockets, networking and hotplug的Linux kernel。
需要配置kernel的以下選項:
   CONFIG_HOTPLUG=y
      CONFIG_UEVENT_HELPER_PATH=""
      CONFIG_NET=y
      CONFIG_UNIX=y
      CONFIG_SYSFS=y
      CONFIG_SYSFS_DEPRECATED*=n
      CONFIG_PROC_FS=y
      CONFIG_INOTIFY_USER=y
      CONFIG_SIGNALFD=y 
 可選項:
   CONFIG_TMPFS=y
      CONFIG_TMPFS_POSIX_ACL=y (user ACLs for device nodes)
      CONFIG_BLK_DEV_BSG=y (SCSI devices)
 如果CONFIG_SYSFS_DEPRECATED*=y則udev將不可用。
 kernel配置中禁用掉/sbin/hotplug,如果未禁用,將導致系統不穩定,因爲kernel並行創建了許多進程,會出現內存溢出。
 需要將proc文件系統掛到/proc,將sysfs掛載到/sys,因爲udev會需要這些目錄。

udev在系統啓動時,將devtmpfs掛載到/dev目錄下,udev管理kernel創建的設備節點的權限和所有權,udev會創建附加的軟鏈接。udev也可以使用tmpfs文件系統,像/dev/null, /dev/console, /dev/kmsg這樣的靜態結點,需要在udev開始時創建。
 udev守護進程通過內核的方式來開始處理設備事件,啓動過程中,kernel將爲所有udev配置的所有已存在的設備發送事件,通常是通過以下命令:
   /sbin/udevadm trigger --type=subsystems
      /sbin/udevadm trigger --type=devices
  重啓udev守護進程不需要已存在設備的規則。
  規則改變後會自動識別,不需要重啓守護進程或者發送信號。
 
基於kernel的發送設備文件創建/移除的事件,udev在/dev目錄下創建/移除結點。
所有的kernel事件與一組指定的規則相匹配,這些規則與事件處理函數和kernel模塊有關。所有的設備結點都需要主設備號、從設備號。udev還可以創建所有權限,以及軟連接來指向結點,和事件處理函數。


爲什麼使用udev


在此之前的設備文件管理方法(靜態文件和devfs)有幾個缺點:


*不確定的設備映射。特別是那些動態設備,比如USB設備,設備文件到實際設備的映射並不可靠和確定。舉一個例子:如果你有兩個USB打印機。一個可能稱 爲/dev/usb/lp0,另外一個便是/dev/usb/lp1。但是到底哪個是哪個並不清楚,lp0,lp1和實際的設備沒有一一對應的關係,因爲 他可能因爲發現設備的順序,打印機本身關閉等原因而導致這種映射並不確定。理想的方式應該是:兩個打印機應該採用基於他們的序列號或者其他標識信息的唯一 設備文件來映射。但是靜態文件和devfs都無法做到這點。


*沒有足夠的主/輔設備號。我們知道,每一個設備文件是有兩個8位的數字:一個是主設備號 ,另外一個是輔設備號來分配的。這兩個8位的數字加上設備類型(塊設備或者字符設備)來唯一標識一個設備。不幸的是,關聯這些身邊的的數字並不足夠。


*/dev目錄下文件太多。一個系統採用靜態設備文件關聯的方式,那麼這個目錄下的文件必然是足夠多。而同時你又不知道在你的系統上到底有那些設備文件是激活的。


*命名不夠靈活。儘管devfs解決了以前的一些問題,但是它自身又帶來了一些問題。其中一個就是命名不夠靈活;你別想非常簡單的就能修改設備文件的名字。缺省的devfs命令機制本身也很奇怪,他需要修改大量的配置文件和程序。;


*內核內存使用,devfs特有的另外一個問題是,作爲內核驅動模塊,devfs需要消耗大量的內存,特別當系統上有大量的設備時(比如上面我們提到的系統一個上有好幾千磁盤時)


udev的目標是想解決上面提到的這些問題,他通採用用戶空間(user-space)工具來管理/dev/目錄樹,他和文件系統分開。知道如何改變缺省配置能讓你之大如何定製自己的系統,比如創建設備字符連接,改變設備文件屬組,權限等。

 

udev的配置文件
一般放在/etc/udev/和/lib/udev/。udev的主配置文件是/etc/udev/udev.conf,裏面主要設置
udev_root和udev_log。udev_root指定放置設備結點的位置,默認是/dev。udev_log保存記錄優先級,有效的值爲err, info和debug或者syslog優先級支持的數字。
udev的規則文件
一般存放在/lib/udev/rules.d/和/etc/udev/rules.d/,前者存放默認的規則,後者存放自定義的規則。還有一個存放臨時規則的目錄/run/udev/rules.d/。這些規則彙總後按字母排序。但是/etc/udev/rules.d/下面的同名規則優先於/lib/udev/rules.d/默認的規則。
規則文件必須以.rules會後綴,否則被忽略。
規則文件支持:==,!=,=,+=,:=,*,?,[]。分別表示相等,不等,賦值,添加入列,終極賦值(後面的無效),[]配置之中某一個,[0-9]表示0到9中的一個。
關鍵字:ACTION,DEVPATH,KERNEL,NAME,SYMLINK,SYBSYSTEM,DRIVER,ATTR{filename},KERNELS,SYBSYSTEMS,DRIVERS,ATTRS{filename},TAGS,ENV{key},TAG,TEST{octal mod mask},PROGRAM,RESULT。
ACTION:事件活動名稱;常用ACTION=="add"或者ACTION=="remove"表示添加或移除。
DEVPATH:事件設備路徑;比如/dev
KERNEL:事件設備名稱;如:sd[a-z][0-9]
NAME:結點或者網絡接口名;設置一次,可全局使用。
SYBSYSTEM:事件設備子系統;比如:usb,sound,net
DRIVER:事件設備驅動名,比如:usb
ATTR{filename}:匹配事件設備的sysfs屬性值。
PROGRAM:執行匹配的程序,返回0,爲真。
ENV{key}:環境變量
RESULT:返回最後PROGRAM調用的字串。
RUN:爲指定的設備添加PROGRAM列表。
LABEL:常和GOTO使用。
BUS:總線的名字,比如IDE,USB
OPTIONS:特殊選項;last_rule爲這類設備規則執行;ignore_device 忽略當前規則; ignore_remove 忽略接下來的並移走請求,all_partitions 爲所有的磁盤分區創建設備文件。
更多參數請man udev.

libudev參考手冊:
libudev主要由以下六部分組成:
{
   udev 
   udev_list
   udev_device
   udev_monitor
   udev_enumerate
   udev_queue
}


udev結構體:主要保存從配置文件讀取的默認值。

struct udev {
 int refcount;
 void (*log_fn)(struct udev *udev,
         int priority, const char *file, int line, const char *fn,
         const char *format, va_list args);
 void *userdata;
 char *sys_path;
 char *dev_path;
 char *rules_path;
 char *run_config_path;
 char *run_path;
 struct udev_list_node properties_list;
 int log_priority;
};
udev_list_entry結構體:設備鏈表入口
struct udev_list_entry {
 struct udev_list_node node;
 struct udev *udev;
 struct udev_list_node *list;
 char *name;
 char *value;
 int num;
};
udev_device結構體:udev設備鏈表
struct udev_device {
 struct udev *udev;
 struct udev_device *parent_device;
 char *syspath;
 const char *devpath;
 char *sysname;
 const char *sysnum;
 char *devnode;
 mode_t devnode_mode;
 char *subsystem;
 char *devtype;
 char *driver;
 char *action;
 char *devpath_old;
 char *knodename;
 char *id_filename;
 char **envp;
 char *monitor_buf;
 size_t monitor_buf_len;
 struct udev_list_node devlinks_list;
 struct udev_list_node properties_list;
 struct udev_list_node sysattr_value_list;
 struct udev_list_node sysattr_list;
 struct udev_list_node tags_list;
 unsigned long long int seqnum;
 unsigned long long int usec_initialized;
 int timeout;
 int devlink_priority;
 int refcount;
 dev_t devnum;
 int ifindex;
 int watch_handle;
 int maj, min;
 bool parent_set;
 bool subsystem_set;
 bool devtype_set;
 bool devlinks_uptodate;
 bool envp_uptodate;
 bool tags_uptodate;
 bool driver_set;
 bool info_loaded;
 bool db_loaded;
 bool uevent_loaded;
 bool is_initialized;
 bool sysattr_list_read;
 bool db_persist;
};
udev_monitor結構體:udev設備事件源
struct udev_monitor {
 struct udev *udev;
 int refcount;
 int sock;
 struct sockaddr_nl snl;
 struct sockaddr_nl snl_trusted_sender;
 struct sockaddr_nl snl_destination;
 struct sockaddr_un sun;
 socklen_t addrlen;
 struct udev_list_node filter_subsystem_list;
 struct udev_list_node filter_tag_list;
 bool bound;
};
udev_enumerate結構體:查找和排序sys設備
struct udev_enumerate {
 struct udev *udev;
 int refcount;
 struct udev_list_node sysattr_match_list;
 struct udev_list_node sysattr_nomatch_list;
 struct udev_list_node subsystem_match_list;
 struct udev_list_node subsystem_nomatch_list;
 struct udev_list_node sysname_match_list;
 struct udev_list_node properties_match_list;
 struct udev_list_node tags_match_list;
 struct udev_device *parent_match;
 struct udev_list_node devices_list;
 struct syspath *devices;
 unsigned int devices_cur;
 unsigned int devices_max;
 bool devices_uptodate:1;
 bool match_is_initialized;
};
udev_queue結構體:存取當前活動事件
struct udev_queue {
 struct udev *udev;
 int refcount;
 struct udev_list_node queue_list;
 struct udev_list_node failed_list;
};


udev代碼的一般編譯選項:
./configure \
    --prefix=/usr \
    --sysconfdir=/etc \
    --sbindir=/sbin \
    --libdir=/usr/lib64 \
    --with-rootlibdir=/lib64 \
    --libexecdir=/lib/udev
   

更多資料可以參閱:
 http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/
 http://www.kernel.org/pub/linux/utils/kernel/hotplug/gudev/
發佈了26 篇原創文章 · 獲贊 23 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章