10复杂框架字符设备创建并注册过程
10.1完成基本模块框架
如:
module_init()module_exit()module_licence()头文件
10.2申请cdev结构体、申请file_operations结构体、创建设备号变量major
1)在文件开头创建设备号,模块代码中创建cdev变量和fops变量
2)
初始化fops结构体中的实现操作函数
10.3在函数中完成设备号注册与设备注册
1)使用 regester_chrdev_region()完成设备号注册,动态注册于静态注册两者,注意:
出错处理,设备号与整形数的转化MKDEV()
2)使用cdev_init()完成fops与cdev结构体绑定
3)使用cdev_add()完成内核的注册
10.4模块驱动的卸载
1)设备的注销
2)设备号的注销
*******************************************************************************
需要记住所有操作的函数名,不知用法可以使用sourceinsight搜索例子,照抄例子完成
见下图:
一个是设备号注册子系统,一个是操作子系统
11
file_operations常用函数
1)头文件
#include
#include
2)(*read)
对应上层的read函数
copy_to_user
copy_to_user(void__user*to,constvoid*from,unsignedlongn)
3)(*write)
对应上层的write函数
copy_from_user
copy_from_user(void*to,constvoid__user*from,unsignedlongn)
4)(*ioctl)
多用于设备控制,如lcd等
long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);
int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);
常用系统封装好的宏定义,完成命令的命名如下:
#defineWDIOC_GETSUPPORT_IOR(WATCHDOG_IOCTL_BASE,0,structwatchdog_info)
#defineWDIOC_GETSTATUS_IOR(WATCHDOG_IOCTL_BASE,1,int)
#defineWDIOC_GETBOOTSTATUS_IOR(WATCHDOG_IOCTL_BASE,2,int)
#defineWDIOC_GETTEMP_IOR(WATCHDOG_IOCTL_BASE,3,int)
#defineWDIOC_SETOPTIONS_IOR(WATCHDOG_IOCTL_BASE,4,int)
#define_IOR(type,nr,size)_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define_IOC(dir,type,nr,size)\
(((dir)<<_IOC_DIRSHIFT)|\
((type)<<_IOC_TYPESHIFT)|\
((nr)<<_IOC_NRSHIFT)|\
((size)<<_IOC_SIZESHIFT))
5)(*open)
设备的打开
int(*open)(structinode*,structfile*);
6)(*release)
对应上层的close
int(*release)(structinode*,structfile*);
7)(*fasync)
用于信号的操作
12
同步互斥操作
●同步互斥是为了解决临界资源访问问题
●就是控制一个资源谁先访问,谁后访问,并且只允许一个进程访问它,其它进程访问时等待。
●多用于多进程、多线程
12.1内核工作状态:
12.1.1抢占式内核和非抢占式内核区别
抢占式内核就是内核拥有多进程运行,并与应用程序共享cpu
非抢占式内核就是内核一个进程独享cpu,内核不返回,应用程序不执行。
12.1.2三种解决同步互斥的情况
1)、单CPU不具备抢占式的内核
但cpu,当cup执行操作某个资源时,不可能出现同时执行操作另一个进程操作这个资源,所以,进程上下文中,不需要考虑并发和互斥的问题
唯一可能出现该问题,只有中断。
解决办法:
开关中断:
local_irq_disable()------->local_irq_save(flags)
local_irq_enable()------->local_irq_restore(flags)
2)、单CPU具备抢占内核
进程上下文中,中断上下文都要考虑
解决办法:
同步互斥+开关中断
3)、多CPU
cpu可以同时操作某个资源,所以必须考虑同步互斥问题,并且中断和进程都要考虑
解决办法:
同步互斥+开关中断
13同步互斥函数总结
1)整型原子
2)自旋锁
3)信号量
13.1自旋锁
当等待时候执行while
(1),cpu一直占用,所以一般内部执行时间必须很短,如果没有解锁前又取锁,就会导致系统崩溃,所以使用时请慎行!
spinlock_tlock;定义
spin_lock_init(lock);初始化
spin_lock(lock);获得锁
spin_unlock(lock)释放锁
spin_lock_irq()=spin_lock()+local_irq_disable()
spin_unlock_irq()=spin_unlock()+local_irq_enable
spin_lock_irq()=spin_lock()+local_irq_save()
spin_unlock_irq()=spin_unlock()+local_irq_restore()
13.2信号量
structsemaphoresem;
sema_init()
DECLARE_MUTEX(name)完成两步:
初始化并设置初始值为1
DECLARE_MUTEX_LOCKED(name):
初始化并设置初始值为0
down(&sem);获得信号,消耗信号
up(&sem);释放信号
14阻塞IO编程流程
1、队列头结点的定义和初始化
2、人为的定义一个逻辑表达式作为我们的条件
3、wait_event(队列名)
唤醒
4、wake_up(队列名的地址)
1)DECLARE_WAIT_HEAD(NAME)定义初始化队列头
2)wait_event()
3)wait_up()
在全局声明DECLARE_WAIT_HEAD(my_wait)
●wait_event(my_wait,(i==0));当i==0的时候则运行(等到i==0的时候)
●wait_up(&my_wait)用于唤醒该队列
还有其它函数:
wait_event_interruptible(queue,conditon)可以中断唤醒
15轮询操作上层select下层poll
上层应用使用select下层内核调用poll,实现非阻塞方式,上层通过select访问内核子系统(内核有专门的子系统负责这功能),下次值需要将连个队列注册,则一旦有变化将mask放回内核子系统,再有内核子系统返回给上层的select函数fd。
unsignedint(*poll)(structfile*filp,structpoll_table*table);
主要结构:
staticunsignedintglobalfifo_poll(structfile*filp,poll_table*wait)
{
unsignedintmask=0;
structglobalfifo_dev*dev=filp->private_data;/*获得设备结构体指针*/
down(&dev->sem);
poll_wait(filp,&dev->r_wait,wait);
poll_wait(filp,&dev->w_wait,wait);
/*fifo非空*/
if(dev->current_len!
=0)
{
mask|=POLLIN|POLLRDNORM;/*标示数据可获得*/
}
/*fifo非满*/
if(dev->current_len!
=GLOBALFIFO_SIZE)
{
mask|=POLLOUT|POLLWRNORM;/*标示数据可写入*/
}
up(&dev->sem);
returnmask;
}
16信号处理
16.1上层应用:
#if1
fcntl(fd,F_SETOWN,getpid());
oflags=fcntl(fd,F_GETFL);
fcntl(fd,F_SETFL,oflags|FASYNC);即调用下层的globalfifo_fasync
#endif
#if0
fcntl(0,F_SETOWN,getpid());
oflags=fcntl(0,F_GETFL);
fcntl(0,F_SETFL,oflags|FASYNC);
16.2下层:
kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
staticintglobalfifo_fasync(intfd,structfile*filp,intmode)
{
structglobalfifo_dev*dev=filp->private_data;
returnfasync_helper(fd,filp,mode,&dev->async_queue);
}
17中断
17.1中断简介
中断与内核调度的重要方式,中断可以抢cpu。
内核对每一个中断源,都使用structirqdescirq_desc结构体来描述,
每一个SOC芯片,都有很多的中断源,内核通过一个叫做NR_IRQS的宏定义这些中断源的数量
structirqdescirq_desc[NR_IRQS]
这个中断源不等于芯片厂商直接提供的中断源数量
S5PC100:
在内核中arch/arm/mach-s5pc100/include/mach/irqs.h这个文件描述了该SOC芯片在内核的中断子系统的源数量
17.2中断程序编写步骤和方式
17.2.1查找硬件你产生中断的引脚
由原理图找出key1在ENIT1上,然后找到该引脚对应的中断号,
17.2.2申请irq
●request_irq(unsignedintirq,irq_handler_thandler,unsignedlongflags,
constchar*name,void*dev)
●使用这个函数即可向内核子系统申请irq,注意参数的填写。
分别是。
irq号,irq处理函数,irq标志(有宏定义),可以看见的名称,传给处理函数的参数
注意:
中断号不是我们查得的中断号,是系统通过一种线性映射维护的一张中断向量表,我们去差,在内核源代码中:
/work/linux_kernel_copy/arch/arm/mach-s5pc100/include/mach/irqs.h(没有)
/work/linux_kernel_copy/arch/arm/plat-s5p/include/plat/irqs.h(找到)
root@ubuntu:
/work/linux_kernel_copy/include/linux#viinterrupt.h
irq_eint
(1)表示外部中断号1
17.2.3编写中断处理函数
staticirqreturn_tmy_irq(intirq,void*my_arg){
printk("irq_yes...=\n");
returnIRQ_HANDLED;}
17.2.4exit的时候释放中断
free_irq(IRQ_EINT
(1),NULL);
17.3具体编程实现截图
1)注册,在init里面注意:
flages
2)释放,在exit里面
3)中断服务程序注意返回值,
***********此时中断即可完成,但中断里面不能执行阻塞和循环锁,时间不能太长,所以中断有几种处理机制***************见书:
197
18中断新模型--上半部中断和下半部中断的实现
1,软中断和tasklet是中断处理机制,只是比紧急中断慢一点执行。
2,工作队列是将剩余的处理函数压入进程,与进程同级别受cpu调度。