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/