Linux设备驱动工程师之路——简单字符设备驱动程序

Linux设备驱动工程师之路——简单字符设备驱动程序

K-Style

转载请注明来自于衡阳师范学院08电2 K-Style http://blog.csdn.net/ayangke,QQ:843308498 邮箱:[email protected]

一、重要知识点

1. 主次设备号

dev_t

dev_t是内核中用来表示设备编号的数据类型;

int MAJOR(dev_t dev)

int MINOR(dev_t dev)

这两个宏抽取主次设备号。

dev­_t MKDEV(unsigned int major, unsignedint minor)

这个宏由主/次设备号构造一个dev_t结构。

2. 分配和释放设备号

int register_chardev_region(dev_t first,unsigned int count, char *name)

静态申请设备号。

Int alloc_chardev_region(dev_t *dev,unsigned int firstminor, unsigned int count, char *name)

动态申请设备号,注意第一个参数是传地址,而静态则是传值。

3. 几种重要的数据结构

struct file

file结构代表一个打开的文件,它由内核在open时创建,并传递给该文件上进行操作的所有函数,直到最后的close函数。

file结构private_data是跨系统调用时保存状态信息非常有用的资源。

file结构的f_ops 保存了文件的当前读写位置。

struct inode

内核用inode代表一个磁盘上的文件,它和file结构不同,后者表示打开的文件描述符。对于单个文件,可能会有许多个表示打开文件的文件描述符file结构,但他们都指单个inode结构。inode的dev_t i_rdev成员包含了真正的设备编号,struct cdev *i_cdev包含了指向struct cdev结构的指针。

struct file_operations

file_operations结构保存了字符设备驱动程序的方法。

4. 字符设备的注册和注销

struct cdev *cdev_alloc(void);

void cdev_init(struct cdev *dev, structfile_operations *fops);

int cdev_add(struct cdev *dev, dev_t num,unsigned int count);

void cdev_del(struct cdev *dev);

用来管理cdev结构的函数,内核中使用该结构表示字符设备。注意cdev_add函数的count参数为次设备的个数,要想拥有多个次设备,就必须将该参数设为次设备的个数。

5. 并发处理

信号量和自旋锁的区别,使用信号量时当调用进程试图获得一个锁定了的锁时会导致进程睡眠,而自旋锁则是一直循法的等待一直到该锁解锁了为止。

1)信号量

DECLARE_MUTEX(name);

DECLARE_MUTEX_LOCKED(name);

声明和初始化用在互斥模式中的信号量的两个宏

void init_MUTEX(struct semaphore *sem)

void init_MUTEX_LOCKER(struct semaphore*sem);

这两个函数可以在运行时初始化信号量

void down(struct semaphore *sem);

int down_interruptible(struct semaphore*sem);

int down_trylock(struct semahpore *sem);

void up(struct semaphore *sem);

锁定和解锁信号量。如果必要,down会将调用进程置于不可中断的休眠状态;相反,down_interruptible可被信号中断。down_trylock不会休眠,并且会在信号量不可用时立即返回。锁定信号量的代码最后必须使用up解锁该信号量。

2)自旋锁

spionlock_t lock = SPIN_LOCK_UNLOCKED;

spin_lock_init(spinlock_t *lock);

初始化自旋锁的两种方式。

voidspin_lock(spinlock_t *lock);

锁定自旋锁

voidspin_unlock(spinlock_t *lock);

解锁自旋锁

二、驱动代码



三、疑点难点

1.

__init

__initdata

__exit

__exitdata

仅用于模块初始化或清除阶段的函数(__init和__exit)和数据(__initdata和__exitdata)标记。标记为初始化项目会在初始化结束后丢弃;而退出在内核未被配置为可卸载模块的情况下被简单的丢弃。被标记为__exit的函数只能在模块卸载或者系统关闭时被调用,其他任何用法都是错误的。内核通过对应的目标对象放置在可执行文件的特殊ELF段中而让这些标记起作用。

2.static

初始化函数应该被声明为static,因为这种函数在特定文件之外没有其他意义。因为一个模块函数要对内核其他部分课件,则必须显示导出,因此这并不是什么强制性规则。

3. struct module *owner

内核使用这个字段以避免在模块操作正在使用时卸载该模块。几乎在所有的情况下,该成员都会被初始化为THIS_MODULE。它是定义在<linux/module.h>中的一个宏。

4 __user

我们会注意到许多参数包括含有__user字串,它其实是一种形式的文档而已,表面指针是一个用户指针,因此不能被直接用。对通常的编译来讲,__user没有任何效果,但是可由外部检查软件使用,用来寻找对用户空间地址错误使用。

你可能感兴趣的:(linux)