揭开字符设备驱动程序的面纱

来源:互联网 发布:matlab中迭代算法 编辑:程序博客网 时间:2024/06/11 13:01
Linux驱动开发庖丁解牛之三

——揭开字符设备驱动程序的面纱


http://www.kuqin.com/article/04linux/1047068.html

By:dreamice 2008-11-23

[email]dreamice.jiang@gmail.com[/email]



1.写在前面的话
我们知道,在Linux设备驱动开发中,包括三大设备类:字符设备,块设备和网络设备。而字符设备,作为最简单的设备类,为此,我们将从最简单的字符设备开始,走进Linux驱动程序设计的神秘殿堂。
——我们已经踏上了真正的设备驱动开发的道路了!
有志者,事竟成。付出越多,而上苍定会以同等的收获回馈于你,当然,最重要的一点是:我们必须走上正确的道路,做正确的付出。开始吧……
参考书目:
《Linux Device Driver》第三版
《Understanding the linux kernel》第三版
《Linux设备驱动开发详解》

2.必备之“砖”
盖大楼,得预先准备好砖头。同样的道理,要写好驱动程序,我们也必须准备好自己的“砖头”,拿好这些砖头,便会真正如庖丁解牛般,游刃于Linux驱动程序设计的神奇艺术之中。
在Linux的设计之初,曾提出:一切皆文件,如果一个东西不是文件,那就是进程。由此可见,文件的概念在Linux系统中可谓是根深蒂固,以至于它深入到对驱动程序的控制,这也是情理之中的事。
下图描述了Linux系统中虚拟文件系统和进程之间的关系:


fs




 dreamice 回复于:2008-11-23 22:37:23

图表 1进程和文件系统的关系
在上图中,我们看到了Process,File object,dentry object,inode object以及Sperblock object等概念。Process就是指一个特定的进程,而File obeject对应于进程打开的一个文件;dentry object描述了一个目录项;inode object则对应于磁盘上一个特定的文件;Sperblock object描述了文件系统的相关信息。从这个图中,可以看到进程到磁盘上一个文件实体的路径及对应关系。下面,我们一次看看这些实体结构在内核中的定义。
2.1 File object
File结构代表一个打开的文件,系统中每个打开的文件,在内核空间都对应一个file结构。它由内核在调用open时创建,并传递给在该文件上操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭以后,内核才会释放这个结构。
在内核中,通常以filp来代表指向file结构的指针。File结构的详细定义如下:
//linux/fs.h
779 struct file {
 780         /*
 781          * fu_list becomes invalid after file_free is called and queued via
 782          * fu_rcuhead for RCU freeing
 783          */
 784         union {
 785                 struct list_head        fu_list;
 786                 struct rcu_head         fu_rcuhead;
 787         } f_u;
 788         struct path             f_path;
 789 #define f_dentry        f_path.dentry
 790 #define f_vfsmnt        f_path.mnt
 791         const struct file_operations    *f_op; //与文件操作相关的函数指针结构
 792         atomic_t                f_count;
 793         unsigned int            f_flags;
 794         mode_t                  f_mode;
 795         loff_t                  f_pos;
 796         struct fown_struct      f_owner;
 797         unsigned int            f_uid, f_gid;
 798         struct file_ra_state    f_ra;
 799
 800         u64                     f_version;
 801 #ifdef CONFIG_SECURITY
 802         void                    *f_security;
803 #endif
 804         /* needed for tty driver, and maybe others */
 805         void                    *private_data;
 806
 807 #ifdef CONFIG_EPOLL
 808         /* Used by fs/eventpoll.c to link all the hooks to this file */
 809         struct list_head        f_ep_links;
 810         spinlock_t              f_ep_lock;
 811 #endif /* #ifdef CONFIG_EPOLL */
 812         struct address_space    *f_mapping;
 813 };

1166 /*
1167  * NOTE:
1168  * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
1169  * can be called without the big kernel lock held in all filesystems.
1170  */
1171 struct file_operations {
1172         struct module *owner;
1173         loff_t (*llseek) (struct file *, loff_t, int);
1174         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
1175         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
1176         ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1177         ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1178         int (*readdir) (struct file *, void *, filldir_t);
1179         unsigned int (*poll) (struct file *, struct poll_table_struct *);
1180         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
1181         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
1182         long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
1183         int (*mmap) (struct file *, struct vm_area_struct *);
1184         int (*open) (struct inode *, struct file *);
1185         int (*flush) (struct file *, fl_owner_t id);
1186         int (*release) (struct inode *, struct file *);
1187         int (*fsync) (struct file *, struct dentry *, int datasync);
1188         int (*aio_fsync) (struct kiocb *, int datasync);
1189         int (*fasync) (int, struct file *, int);
1190         int (*lock) (struct file *, int, struct file_lock *);
1191         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
1192         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
1193         int (*check_flags)(int);
1194         int (*dir_notify)(struct file *filp, unsigned long arg);
1195         int (*flock) (struct file *, int, struct file_lock *);
1196         ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
1197         ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
1198         int (*setlease)(struct file *, long, struct file_lock **);
1199 };
其中,蓝色字体标出部分,为与驱动程序最为密切的部分。由于很多书中都对这些结构体做了详细的阐述,这里就不再赘述了。

2.2 inode object
内核用inode结构在内部表示文件,它和file结构的不同之处在于:file表示打开的文件描述符,对单个文件,可能有多个表示打开的文件描述符的file结构,但他们都指向同一个inode结构。
Inode结构的详细定义如下:
593 struct inode {
 594         struct hlist_node       i_hash;
 595         struct list_head        i_list;
 596         struct list_head        i_sb_list;
 597         struct list_head        i_dentry;
 598         unsigned long           i_ino;
 599         atomic_t                i_count;
 600         unsigned int            i_nlink;
 601         uid_t                   i_uid;
 602         gid_t                   i_gid;
 603         dev_t                   i_rdev;
 604         u64                     i_version;
 605         loff_t                  i_size;
 606 #ifdef __NEED_I_SIZE_ORDERED
 607         seqcount_t              i_size_seqcount;
 608 #endif
 609         struct timespec         i_atime;
 610         struct timespec         i_mtime;
 611         struct timespec         i_ctime;
 612         unsigned int            i_blkbits;
 613         blkcnt_t                i_blocks;
 614         unsigned short          i_bytes;
 615         umode_t                 i_mode;
 616         spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
 617         struct mutex            i_mutex;
 618         struct rw_semaphore     i_alloc_sem;
 619         const struct inode_operations   *i_op;//inode操作函数集合
 620         const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
 621         struct super_block      *i_sb;
 622         struct file_lock        *i_flock;
 623         struct address_space    *i_mapping;
 624         struct address_space    i_data;
 625 #ifdef CONFIG_QUOTA
 626         struct dquot            *i_dquot[MAXQUOTAS];
 627 #endif
 628         struct list_head        i_devices;
 629         union {
 630                 struct pipe_inode_info  *i_pipe;
 631                 struct block_device     *i_bdev;
 632                 struct cdev             *i_cdev;
 633         };
 634         int                     i_cindex;
 635
 636         __u32                   i_generation;
 637
638 #ifdef CONFIG_DNOTIFY
 639         unsigned long           i_dnotify_mask; /* Directory notify events */
 640         struct dnotify_struct   *i_dnotify; /* for directory notifications */
 641 #endif
 642
 643 #ifdef CONFIG_INOTIFY
 644         struct list_head        inotify_watches; /* watches on this inode */
 645         struct mutex            inotify_mutex;  /* protects the watches list */
 646 #endif
 647
 648         unsigned long           i_state;
 649         unsigned long           dirtied_when;   /* jiffies of first dirtying */
 650
 651         unsigned int            i_flags;
 652
 653         atomic_t                i_writecount;
 654 #ifdef CONFIG_SECURITY
 655         void                    *i_security;
 656 #endif
 657         void                    *i_private; /* fs or device private pointer */
 658 };
2.3 Super block object
Super block object对应于一个特定的文件系统,通常对应于存放在磁盘扇区中的文件系统超级块或文件系统控制块,而对于非基于文件系统的文件,他们会在使用现场创建超级块,并将其保存到内存中。
一下是结构体的详细描述:
981 struct super_block {
 982         struct list_head        s_list;         /* Keep this first */
 983         dev_t                   s_dev;          /* search index; _not_ kdev_t */
 984         unsigned long           s_blocksize;
 985         unsigned char           s_blocksize_bits;
 986         unsigned char           s_dirt;
 987         unsigned long long      s_maxbytes;     /* Max file size */
 988         struct file_system_type *s_type;
 989         const struct super_operations   *s_op;
 990         struct dquot_operations *dq_op;
 991         struct quotactl_ops     *s_qcop;
 992         const struct export_operations *s_export_op;
 993         unsigned long           s_flags;
 994         unsigned long           s_magic;
 995         struct dentry           *s_root;
 996         struct rw_semaphore     s_umount;
 997         struct mutex            s_lock;
 998         int                     s_count;
 999         int                     s_syncing;
1000         int                     s_need_sync_fs;
1001         atomic_t                s_active;
1002 #ifdef CONFIG_SECURITY
1003         void                    *s_security;
1004 #endif
1005         struct xattr_handler    **s_xattr;
1006
1007         struct list_head        s_inodes;       /* all inodes */
1008         struct list_head        s_dirty;        /* dirty inodes */
1009         struct list_head        s_io;           /* parked for writeback */
1010         struct list_head        s_more_io;      /* parked for more writeback */
1011         struct hlist_head       s_anon;         /* anonymous dentries for (nfs) exporting */
1012         struct list_head        s_files;
1013
1014         struct block_device     *s_bdev;
1015         struct mtd_info         *s_mtd;
1016         struct list_head        s_instances;
1017         struct quota_info       s_dquot;        /* Diskquota specific options */
1018
1019         int                     s_frozen;
1020         wait_queue_head_t       s_wait_unfrozen;
1021
1022         char s_id[32];                          /* Informational name */
1023
1024         void                    *s_fs_info;     /* Filesystem private info */
1025
1026         /*
1027          * The next field is for VFS *only*. No filesystems have any business
1028          * even looking at it. You had been warned.
1029          */
1030         struct mutex s_vfs_rename_mutex;        /* Kludge */
1031
1032         /* Granularity of c/m/atime in ns.
1033            Cannot be worse than a second */
1034         u32                s_time_gran;
1035
1036         /*
1037          * Filesystem subtype.  If non-empty the filesystem type field
1038          * in /proc/mounts will be "type.subtype"
1039          */
1040         char *s_subtype;
1041
1042         /*
1043          * Saved mount options for lazy filesystems using
1044          * generic_show_options()
1045          */
1046         char *s_options;
1047 };

2.4 Identry object
Linux中把目录也当作文件,为了方便查找操作,虚拟文件系统(VFS)引入了目录项的概念。每个dentry代表路径中一个特定部分。由于驱动程序很少涉及到dentry,所以在这里就不做描述了。

3. 实例剖析Linux字符设备驱动程序
Linux的变化真的是太快了。当我们还在研读最新版的LDD3(基于内核2.6.11)时,而实际上,它的驱动程序框架结构已经发生了很大的变化。当我还在投入的学习scull示例驱动程序的时候,我发现,对于编写一个字符设备驱动程序,已经有了新的变化。原本打算剖析scull程序,来达到融会贯通的目的,然而,面对变化,我不得不去学习一种全新的字符设备驱动程序的编写。
我想这是一种挑战,学习,是一个终身的过程。
3.1 描述字符设备体结构(cdev)
在新版的linux内核中,使用cdev结构体描述一个字符设备,其定义如下:
//include/linux/cdev.h
13 struct cdev {
 14         struct kobject kobj;//kobject对象
 15         struct module *owner;
 16         const struct file_operations *ops;//文件操作结构体
 17         struct list_head list;
 18         dev_t dev;//定设备的主次设备号
 19         unsigned int count;
 20 };
(关于kobject还正在研究中……)
结构中的ops定义了操作File 的函数指针集,dev存储了主次设备号。注意,在很多著作中谈到Linux2.6内核中,dev_t是一个32位的类型,其中高12位存储主设备号,低20位存储次设备号。但我们最好不要这样去引用,内核版本的变化可谓是瞬息万变,很可能有一天这个约定就变化了,因此就将导致旧的驱动程序将不再兼容新的内核。最好的方式是使用下面两个宏定义获取主次设备号:
MAJOR(dev_t dev)
MINOR(dev_t dev)
使用下面的宏则可以将主次设备号生成一个dev_t:
MKDEV(int major, int minor)
一下是操作cdev结构体的相关内核函数:
22 void cdev_init(struct cdev *, const struct file_operations *); //初始化cdev结构,并建立//cdev结构和file_operations之间的连接
 23
 24 struct cdev *cdev_alloc(void);//动态申请cdev内存
 25
 26 void cdev_put(struct cdev *p);//解除读一个cdev的引用,相应的cdev_get函数增加
//对cdev的引用计数
 27
 28 int cdev_add(struct cdev *, dev_t, unsigned);//向系统添加一个cdev
 29
 30 void cdev_del(struct cdev *);//删除系统中一个cdev
 31
 32 void cd_forget(struct inode *);//
3.2 分配和释放设备号
int register_chrdev_region(dev_t from, unsigned count, const char *name)
该函数用于已知起始设备的设备号的情况。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
对设备号未知的情况,使用该函数向内核动态申请未被占用的设备号。
对于一个驱动程序员来说,如果不确定设备号,最好不要妄自定义一个设备号进行注册,这样的后果是:如果该设备号已用,将注册不成功;如果该设备号在后来将被引用,那么将导致其他设备无法注册。所以,在这种情况下,最好使用动态注册的方式。
void unregister_chrdev_region(dev_t from, unsigned count)
该函数释放先前注册的设备。

3.3 一个简单的字符设备驱动程序
global_mem.c
  1 /*=============================================================  2     A simple example of char device drivers  3  4       5     <[email]dreamice.jiang@gmail.com[/email]>  6  ============================================================*/  7 #include <linux/module.h>  8 #include <linux/types.h>  9 #include <linux/fs.h> 10 #include <linux/errno.h> 11 #include <linux/mm.h> 12 #include <linux/sched.h> 13 #include <linux/init.h> 14 #include <linux/cdev.h> 15 #include <asm/io.h> 16 #include <asm/system.h> 17 #include <asm/uaccess.h> 18 19 #define GLOBALMEM_SIZE  0x1000  /* 虚拟字符设备内存缓冲区大小 */ 20 #define MEM_CLEAR 0x1  /*  ioctl操作指令 (这里只是简单起见,不建议这么定义)*/ 21 //#define GLOBALMEM_MAJOR 254    /*  静态定义主设备号 */ 22 #define GLOBALMEM_MAJOR 0 /*定义动态申请主设备号*/ 23 24 static int globalmem_major = GLOBALMEM_MAJOR; 25 /*globalmem 虚拟字符设备结构体 */ 26 struct globalmem_dev 27 { 28   struct cdev cdev; /*cdev结构体 */ 29   unsigned char mem[GLOBALMEM_SIZE]; /*虚拟设备内存区大小*/ 30 }; 31 32 struct globalmem_dev *globalmem_devp;  34 int globalmem_open(struct inode *inode, struct file *filp) 35 { 36   /*将设备结构体指针赋给文件私有数据 */ 37   filp->private_data = globalmem_devp; 38   return 0; 39 } 40 /*释放函数*/ 41 int globalmem_release(struct inode *inode, struct file *filp) 42 { 43   return 0; 44 } 45 46 /* ioct操作函数*/ 47 static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned 48   int cmd, unsigned long arg) 49 { 50   struct globalmem_dev *dev = filp->private_data;/*&raquo;&ntilde;&micro;&Atilde;&Eacute;è±&cedil;&frac12;á&sup1;&sup1;&Igrave;&aring;&Ouml;&cedil;&Otilde;&euml;*/ 51 52   switch (cmd) 53   { 54     case MEM_CLEAR: 55       memset(dev->mem, 0, GLOBALMEM_SIZE); 56       printk(KERN_INFO "globalmem is set to zero\n"); 57       break; 58 59     default: 60       return  - EINVAL; 61   } 62   return 0; 63 } 64 65 /*read函数*/ 66 static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, 67   loff_t *ppos) 68 { 69   unsigned long p =  *ppos; 70   unsigned int count = size; 71   int ret = 0; 72   struct globalmem_dev *dev = filp->private_data;  73 74    75   if (p >= GLOBALMEM_SIZE) 76     return count ?  - ENXIO: 0; 77   if (count > GLOBALMEM_SIZE - p) 78     count = GLOBALMEM_SIZE - p; 79 80    81   if (copy_to_user(buf, (void*)(dev->mem + p), count)) 82   { 83     ret =  - EFAULT; 84   } 85   else 86   { 87     *ppos += count; 88     ret = count; 89 90     printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p); 91   } 92 93   return ret; 94 } 95 96 /*&ETH;&acute;&ordm;&macr;&Ecirc;&yacute;*/ 97 static ssize_t globalmem_write(struct file *filp, const char __user *buf, 98   size_t size, loff_t *ppos) 99 {100   unsigned long p =  *ppos;101   unsigned int count = size;102   int ret = 0;103   struct globalmem_dev *dev = filp->private_data;104105   106   if (p >= GLOBALMEM_SIZE)107     return count ?  - ENXIO: 0;108   if (count > GLOBALMEM_SIZE - p)109     count = GLOBALMEM_SIZE - p;110111   //112   if (copy_from_user(dev->mem + p, buf, count))113     ret =  - EFAULT;114   else115   {116     *ppos += count;117     ret = count;118119     printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);120   }121122   return ret;123 }124125 126 static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)127 {128   loff_t ret = 0;129   switch (orig)130   {131     case 0:  //起始位置132       if (offset < 0)133       {134         ret =  - EINVAL;135         break;136       }137       if ((unsigned int)offset > GLOBALMEM_SIZE)138       {139         ret =  - EINVAL;140         break;141       }142       filp->f_pos = (unsigned int)offset;143       ret = filp->f_pos;144       break;145     case 1:   /*当前位置*/146       if ((filp->f_pos + offset) > GLOBALMEM_SIZE)147       {148         ret =  - EINVAL;149         break;150       }151       if ((filp->f_pos + offset) < 0)152       {153         ret =  - EINVAL;154         break;155       }156       filp->f_pos += offset;157       ret = filp->f_pos;158       break;159     default:160       ret =  - EINVAL;161       break;162   }163   return ret;164 }165166 /*操作函数结构体*/167 static const struct file_operations globalmem_fops =168 {169   .owner = THIS_MODULE,170   .llseek = globalmem_llseek,171   .read = globalmem_read,172   .write = globalmem_write,173   .ioctl = globalmem_ioctl,174   .open = globalmem_open,175   .release = globalmem_release,176 };177178 /*初始化并注册cdev */179 static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)180 {181   int err, devno = MKDEV(globalmem_major, index);182183   cdev_init(&dev->cdev, &globalmem_fops);184   dev->cdev.owner = THIS_MODULE;185   dev->cdev.ops = &globalmem_fops;186   err = cdev_add(&dev->cdev, devno, 1);187   if (err)188     printk(KERN_NOTICE "Error %d adding LED%d", err, index);189 }190191 /*设备驱动模块加载*/192 int globalmem_init(void)193 {194   int result;195   dev_t devno = MKDEV(globalmem_major, 0);196197   /*静态指定设备编号*/198   if (globalmem_major)199     result = register_chrdev_region(devno, 1, "globalmem");200   else  /*动态申请*/201   {202     result = alloc_chrdev_region(&devno, 0, 1, "globalmem");203     globalmem_major = MAJOR(devno);204   }205   if (result < 0)206     return result;207208   /* 动态申请设备结构体内存*/209   globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);210   if (!globalmem_devp)    /*&Eacute;ê&Ccedil;&euml;&Ecirc;§°&Uuml;*/211   {212     result =  - ENOMEM;213     goto fail_malloc;214   }215   memset(globalmem_devp, 0, sizeof(struct globalmem_dev));216217   globalmem_setup_cdev(globalmem_devp, 0);218   printk(KERN_INFO"Init global_mem success!\n");219   return 0;220221   fail_malloc: unregister_chrdev_region(devno, 1);222   return result;223 }224225 /*模块卸载函数*/226 void globalmem_exit(void)227 {228   cdev_del(&globalmem_devp->cdev);   /*注销cdev*/229   kfree(globalmem_devp);     /*释放结构体内存*/230   unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/231   printk(KERN_INFO"Bye-bye global_mem!\n");232 }233234 MODULE_AUTHOR("Dreamice");235 MODULE_LICENSE("Dual BSD/GPL");236237 module_param(globalmem_major, int, S_IRUGO);238239 module_init(globalmem_init);240 module_exit(globalmem_exit);


Makefile:
  1 TARGET = global_mem  2 KDIR = /lib/modules/$(shell uname -r)/build  3 PWD = $(shell pwd)  4 obj-m := $(TARGET).o  5 default:  6         make -C $(KDIR) M=$(PWD) modules  7 clean:  8         $(RM) *.o *.ko *.mod.c Module.symvers modules.order

测试:
# cd /sys/module/globalmem/parameters
# cat globalmem_major
251
# mknod /dev/globalmem c 251 0
# echo ‘hello dreamice’ > /dev/gloablmem
# cat /dev/gloablmem
hello dreamice

当然,我们也可以写c程序来测试该程序。
4.总结:
    在编写字符设备驱动程序中,最重要的是file_operations这个结构,我们通常只需要实现read,write,ioctl等函数的操作。
对于一个特定的字符设备驱动的编写,必须把该设备同一个cdev结构关联起来。我们必须完成设备号的指定或者动态申请,这是设备在系统中的唯一标识。然后,完成设备驱动程序的模块初始化,以及模块注销的相关实现。
注意,file_operations与cdev结构的关联,这与老版本的驱动程序编写有很大差异。
5.后续工作
(1)研究总结kobject以及sysfs;
(2)深入学习,通过改进该实例驱动程序,使之包含更多模块及设备驱动程序编写涉及的知识点,如多个子设备,内核同步相关知识点等,来达到更深入领会字符设备驱动程序的目的。
(3)深入剖析内核文件系统。
原创粉丝点击