您的位置:首页 > 运维架构 > Linux

闲聊linux中的input设备(1)Linux中的设备大家族

2011-09-09 00:17 309 查看
用过linux的哥们都知道,linux所有的设备都是以文件的形式实现的,要访问一个设备,我们只需要以open、read、write的形式对设备的进行操作就可以了。在linux系统的/dev目录下,罗列了当前系统支持的所有设备。运行 ls /dev一下,着实吓了一大跳,

[root@localhost ~]# ls /dev

adsp full midi ram9 tty15 tty42 ttyS3

agpgart fuse mixer ramdisk tty16 tty43 urandom

audio hpet net random tty17 tty44 usbdev1.1_ep00

bsg hvc0 null root tty18 tty45 usbdev1.1_ep81

bus hvc1 nvram rtc tty19 tty46 usbmon0

cdrom hvc2 oldmem scd0 tty2 tty47 usbmon1

console hvc3 parport0 sda tty20 tty48 vcs

core hvc4 parport1 sda1 tty21 tty49 vcs1

disk hvc5 parport2 sda2 tty22 tty5 vcs2

dmmidi hvc6 parport3 sda3 tty23 tty50 vcs3

dsp hvc7 port sequencer tty24 tty51 vcs4

fd initctl ppp sequencer2 tty25 tty52 vcs5

fd0 input ptmx sg0 tty26 tty53 vcs6

fd0u1040 kmsg pts sg1 tty27 tty54 vcs7

fd0u1120 log ram shm tty28 tty55 vcs8

fd0u1440 loop0 ram0 snapshot tty29 tty56 vcsa

fd0u1600 loop1 ram1 snd tty3 tty57 vcsa1

fd0u1680 loop2 ram10 sr0 tty30 tty58 vcsa2

fd0u1722 loop3 ram11 stderr tty31 tty59 vcsa3

fd0u1743 loop4 ram12 stdin tty32 tty6 vcsa4

fd0u1760 loop5 ram13 stdout tty33 tty60 vcsa5

fd0u1840 loop6 ram14 systty tty34 tty61 vcsa6

fd0u1920 loop7 ram15 tty tty35 tty62 vcsa7

fd0u360 lp0 ram2 tty0 tty36 tty63 vcsa8

fd0u720 lp1 ram3 tty1 tty37 tty7 X0R

fd0u800 lp2 ram4 tty10 tty38 tty8 XOR

fd0u820 lp3 ram5 tty11 tty39 tty9 zero

fd0u830 MAKEDEV ram6 tty12 tty4 ttyS0

floppy mapper ram7 tty13 tty40 ttyS1

floppy-fd0 mem ram8 tty14 tty41 ttyS2

这么多设备那么管理起来是不是很麻烦,linux内核的开发者智商自然在你我之上,他们把所有的这些设备归为三大类,即平时我们熟悉的字符设备、块设备、网络设备。运行 ls –l /dev(这里我只取一部分显示信息)

crw-rw----+ 1 root root 14, 12 12-16 00:57 adsp

crw------- 1 root root 10, 175 12-16 00:57 agpgart

crw-rw----+ 1 root root 14, 4 12-16 00:57 audio

drwxr-xr-x 2 root root 80 12-16 00:57 bsg

drwxr-xr-x 3 root root 60 12-16 00:57 bus

lrwxrwxrwx 1 root root 3 12-16 00:57 cdrom -> sr0

crw------- 1 lmm670 root 5, 1 12-16 00:57 console

lrwxrwxrwx 1 root root 11 12-16 00:57 core -> /proc/kcore

drwxr-xr-x 5 root root 100 12-16 00:57 disk

crw-rw---- 1 root root 14, 9 12-16 00:57 dmmidi

crw-rw----+ 1 root root 14, 3 12-16 00:57 dsp

lrwxrwxrwx 1 root root 13 12-16 00:57 fd -> /proc/self/fd

brw-r----- 1 root floppy 2, 0 12-16 00:57 fd0

brw-r----- 1 root floppy 2, 84 12-16 00:57 fd0u1040

brw-r----- 1 root floppy 2, 88 12-16 00:57 fd0u1120

brw-r----- 1 root floppy 2, 28 12-16 00:57 fd0u1440

brw-r----- 1 root floppy 2, 124 12-16 00:57 fd0u1600

brw-r----- 1 root floppy 2, 44 12-16 00:57 fd0u1680

brw-r----- 1 root floppy 2, 60 12-16 00:57 fd0u1722

brw-r----- 1 root floppy 2, 76 12-16 00:57 fd0u1743

brw-r----- 1 root floppy 2, 96 12-16 00:57 fd0u1760

brw-r----- 1 root floppy 2, 116 12-16 00:57 fd0u1840

brw-r----- 1 root floppy 2, 100 12-16 00:57 fd0u1920

brw-r----- 1 root floppy 2, 12 12-16 00:57 fd0u360

brw-r----- 1 root floppy 2, 16 12-16 00:57 fd0u720

brw-r----- 1 root floppy 2, 120 12-16 00:57 fd0u800

brw-r----- 1 root floppy 2, 52 12-16 00:57 fd0u820

brw-r----- 1 root floppy 2, 68 12-16 00:57 fd0u830

lrwxrwxrwx 1 root root 3 12-16 00:57 floppy -> fd0

lrwxrwxrwx 1 root root 3 12-16 00:57 floppy-fd0 -> fd0

crw-rw-rw- 1 root root 1, 7 12-16 00:57 full

crw-rw---- 1 root fuse 10, 229 12-16 00:57 fuse

crw-rw---- 1 root root 10, 228 12-16 00:57 hpet

crw-rw---- 1 root uucp 229, 0 12-16 00:57 hvc0

crw-rw---- 1 root uucp 229, 1 12-16 00:57 hvc1

crw-rw---- 1 root uucp 229, 2 12-16 00:57 hvc2

crw-rw---- 1 root uucp 229, 3 12-16 00:57 hvc3

crw-rw---- 1 root uucp 229, 4 12-16 00:57 hvc4

crw-rw---- 1 root uucp 229, 5 12-16 00:57 hvc5

crw-rw---- 1 root uucp 229, 6 12-16 00:57 hvc6

crw-rw---- 1 root uucp 229, 7 12-16 00:57 hvc7

prw------- 1 root root 0 12-16 00:58 initctl

drwxr-xr-x 3 root root 200 12-16 00:57 input

crw-rw---- 1 root root 1, 11 12-16 00:57 kmsg

srw-rw-rw- 1 root root 0 12-16 00:58 log

brw-r----- 1 root disk 7, 0 12-16 00:57 loop0

大家可以看到,每一行的第一个字母,代表着此文件的类型。c表示字符设备,b表示块设备,s表示网络设备,细心的哥们会问,不是说只有三类设备吗,怎么还有其他类型开头的呢?比如d、l等等。对不起,这里讲的是文件类型,d表示是一个目录文件,l表示一个链接文件。至于这三者之间的区别,我就不在这啰嗦了,Google一下一大堆。我要强调的是,无论上面三个设备中的任何一种设备,要想在linux实现它的设备驱动,首先要对它进行一系列的初始化工作,然后需给它提供一个设备操作集合(或者更简单一点理解:接口函数),用来提供给我们的上层程序进行访问,比如open,read,write等等。要不然,我要你这个设备驱动干嘛。在字符设备驱动中,我们的操作集函数是这样的

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};


网名为“卖血去上网”的兄弟要说了,写一个驱动接口函数怎么这么复杂,要提供这么多接口函数。其实不然,一般的设备驱动接口函数并不需要把上面的所有都实现,只需要实现里面的一些:比如我们这里:

static const struct file_operations evdev_fops = {
.owner           = THIS_MODULE,
.read              = evdev_read,
.write             = evdev_write,
.poll        = evdev_poll,
.open             = evdev_open,
.release    = evdev_release,
.unlocked_ioctl      = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl  = evdev_ioctl_compat,
#endif
.fasync           = evdev_fasync,
.flush             = evdev_flush
};


兄弟们注意了,这个结构体就是前面那个结构体的实现,类似于c++中的类和对象。结构中若干个等式中,我们最终要实现的是右边的那些函数。这样做的目的大家都清楚:实现统一接口,增加程序的可移植性。上层代码每次open
evdev这个设备的时候最终都会通过file_operations落实到我们的evdev_open函数。鲁迅先生曾说过:驱动代码写起来其实并不难,当接口函数多了就变难了。说了一大推好像被忽悠了,怎么没提到一点关于input设备的信息,关于input设备我现在只提一句,input设备的接口函数,linux内核已经为我们写好了。毕竟时代在进步,内核在更新,鲁先生的话也可以改一下了:设备驱动的实现本身很难,自从有了input设备子系统,就变得不难了(当然只针对input设备)。到底何谓input设备呢?请看下一节。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: