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/