1、并发与竞态
1)并发
处理并发的常用技术是加锁或互斥。在Linux内核中,主要通过semaphonre机制和spin_lock机制实现
1.1)内核的信号量:是一种睡眠锁
//定义信号量
struct semaphore sem;
//初始化信号量
void sema_init(struct semaphore *sem.int val);//用于初始化设置信号量的初值
//初始化互斥锁
void init_MUTEX(struct semaphore *sem); //把信号量sem的值设置为1
void init_MUTEX)LOCKED() // 设置为0
//获取信号量,获取失败后,进程状态不一样
void down()
int down_interruptible() //进入睡眠后,可以被进程或者中断唤醒
down_killable()
2、字符设备和块设备
1)字符设备通过字节访问,块设备通过块(512,1024,4096,也可以字符)访问
2)字符设备顺序访问,块设备随机访问
3、主设备号和次设备号
1)主设备号:标识与设备关联的设备驱动,标识一个特定的驱动程序
2)次设备号:确定当前所指向的是哪个设备
4、内核等待队列
在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可以看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待队列中取出队列
//定义等待队列
wait_queue_head_t my_queue;
//初始化等待队列
init_waitqueue_head(&my_queue);
//定义并初始化等待对垒
DECLARE_WAIT_QUEUE_HEAD(my_queue);
//有条件睡眠
wait_event(queue,condition); //当condition为真时,立即但会,否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上
wait_event_interruptible(queue.condition); //当condition为真时,立即但会,否则让进程进入TASK_INTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上
wait_event_killable(wait_queue_t queue,condition);// 当condition为真时,立即但会,否则让进程进入TASK__KILLABLE模式的睡眠,并挂在queue参数所指定的等待队列上
//无条件睡眠
interruptible_sleep_on()
//从等待队列中唤醒进程
wake_up(wair_queue_t *q);//从等待队列q中唤醒所有进程
wake_up_interruptible(wair_queue_t *q);//唤醒状态为TASK_INTERRUPTIBLE的进程
5、select 与poll
1)poll负责完成
使用poll_wait将等待队列添加到poll_table中
返回描述设备是否可读或可写的掩码
POLLIN:设备可读
POLLRDNORM:数据可读
POLLOUT:设备可写
POLLWRNORM:数据可写
设备可读通常返回(POLLIN|POLLRDNORM)
设备可写通常返回(POLLOUT|POLLWRNORM)
2)select
select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程
int select(int maxfd,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,const struct timeval *timeout)
返回值:
正常情况下返回满足要求的文件描述符个数,
超时返回0
出错或中断返回-1
使用方法:
将要监控的文件添加到文件描述符集
调用select开始监控
判断文件是否发生变化
文件描述符操作集:
#include <sys/select.h>
void FD_SET(inf fd,fd_set *fdset); //将文件描述符fd添加到文件描述符集fdset中
void FD_CLR(inf fd,fd_set *fdset);//将文件描述符集fdset中清除文件描述符fd
void FD_ZERO(fd_set *fdset);//清空文件描述符集fdset
void FD_ISSET(inf fd,fd_set *fdset);//检测文件描述符集fdset中的文件fd是否发生了变化