在Linux系统编程及嵌入式编程中,输入输出的基本方法就是读写访问/dev目录下的对应设备文件。例如,最常见的/dev/ttyUSBx设备对应于插入的USB-UART转接器;/dev/videox设备对应于摄像头设备。其中,设备名称结尾的x代指Linux内核为多个同种设备的编号id。实际工作中发现,Linux内核仅仅是按照发现设备的顺序从0开始依次编号,并不对设备号的顺序做出任何保证,并且在设备移除后,id保留一段时间后被新插入的设备复用。由于以上原因,即便是多次启动也可能带来对同一硬件设备的不同编号,故默认设备名不能固定,必须通过人为判断输入,给用户带来了麻烦。
其实,Linux系统已经内置针对这一问题的解决办法。
在目录/etc/udev/rules.d内,可以建立.rules文件,比如
/etc/udev/rules.d/50-udev.rules
加入语句
KERNEL=="hdb", NAME="my_disk"
匹配被内核命名为”hdb”的设备,重命名为 /dev/my_disk
KERNEL=="hdb", DRIVER=="ide-disk", SYMLINK+="sparedisk"
匹配被内核命名为”hdb”且驱动程序哦为”ide-disk”的设备,以默认名称命名,并创建一个符号链接/dev/sparedisk指向该设备
在规则中可以使用预设变量和通配符。比如:
- 用%k表示设备名
KERNEL=="mice", NAME="input/%k"
会将/dev/mice设备转移到/dev/input/mice出现
- 用%n表示设备编号
KERNEL=="loop0", NAME="loop/%n", SYMLINK+="%k"
会将/dev/loop设备转移到/dev/loop/0,并在/dev/loop0创建一个符号链接
- 常用通配符 * ? []
KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
匹配所有软盘设备,并转移到/dev/floppy文件夹下,以对应设备号命名,并在原位置创建符号链接
KERNEL=="hiddev*", NAME="usb/%k"
将所有以hiddev开头的设备移动到/dev/usb/文件夹内
理论上,这样能够解决相当一部分设备的重命名问题。但是,若计算机插入了多个同类设备,则这一方法无力区分。解决办法是利用SUBSYSTEM和ATTRS关键字,沿设备树向上搜索,找到每个设备独有的特征标识,并将其写到规则文件里。为了查看设备的特征标识,可以选用以下几个命令:
find /sys -name dev
在sys文件树中寻找dev设备节点路径
udevadm info --name=/dev/ttyUSB1 --attribute-walk
或
udevinfo -a -p /sys/block/sda
搜索驱动树
几个例子
- 打印机原名为 /dev/lp0
# udevinfo -a -p $(udevinfo -q path -n /dev/lp0)
looking at device '/class/usb/lp0':
KERNEL=="lp0"
SUBSYSTEM=="usb"
DRIVER==""
ATTR{dev}=="180:0"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb1/1-1':
SUBSYSTEMS=="usb"
ATTRS{manufacturer}=="EPSON"
ATTRS{product}=="USB Printer"
ATTRS{serial}=="L72010011070626380"
其中有特征标识serial=”L72010011070626380”,故写
SUBSYSTEM=="usb", ATTRS{serial}=="L72010011070626380", SYMLINK+="epson_680"
- USB Palm Pilot
原名为/dev/ttyUSB1,规则写为
SUBSYSTEMS=="usb", ATTRS{product}=="Palm Handheld", KERNEL=="ttyUSB*", SYMLINK+="pilot"
依次匹配usb设备,生产商Palm Handheld,设备类型ttyUSB
- USB摄像头
摄像头有时会被辨认为单个分区的磁盘设备,比如/dev/sdb /dev/sdb1
#udevinfo -a -p $(udevinfo -q path -n /dev/sdb1)
looking at device '/block/sdb/sdb1':
KERNEL=="sdb1"
SUBSYSTEM=="block"
looking at parent device '/devices/pci0000:00/0000:00:02.1/usb1/1-1/1-1:1.0/host6/target6:0:0/6:0:0:0':
KERNELS=="6:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
ATTRS{rev}=="1.00"
ATTRS{model}=="X250,D560Z,C350Z"
ATTRS{vendor}=="OLYMPUS "
ATTRS{scsi_level}=="3"
ATTRS{type}=="0"
我们只需要用/dev/sdb1,但是由于驱动树的关系,所有匹配sdb1的特征也会匹配sdb。为了区分,还需要对应设备名进行匹配:
KERNEL=="sd?1", SUBSYSTEMS=="scsi", ATTRS{model}=="X250,D560Z,C350Z", SYMLINK+="camera"
- USB硬盘
KERNEL=="sd*", SUBSYSTEMS=="scsi", ATTRS{model}=="USB 2.0 Storage Device", SYMLINK+="usbhd%n"
这个规则不会改变原设备名,而会创建以下符号链接
/dev/usbhd - 可用fdisk分区的节点
/dev/usbhd1 - 第一分区
/dev/usbhd2 - 第二分区