---------------------------等待事件-------------------------------
wait_event(wq, condition)
等待以 wq 为等待队列头的等待队列被唤醒,前
提是 condition 条件必须满足(为真),否则一直阻
塞 。 此 函 数 会 将 进 程 设 置 为
TASK_UNINTERRUPTIBLE 状态
wait_event_timeout(wq, condition, timeout)
功能和 wait_event 类似,但是此函数可以添加超
时时间,以 jiffies 为单位。此函数有返回值,如
果返回 0 的话表示超时时间到,而且 condition
为假。为 1 的话表示 condition 为真,也就是条
件满足了。
wait_event_interruptible(wq, condition)
与 wait_event 函数类似,但是此函数将进程设置
为 TASK_INTERRUPTIBLE,就是可以被信号打
断。
wait_event_interruptible_timeout(wq,
condition, timeout)
与 wait_event_timeout 函数类似,此函数也将进
程设置为 TASK_INTERRUPTIBLE,可以被信号
打断
---------------------------等待事件------------------------
select
poll
epoll
-------------------------------------
platfrom 总线驱动模型
device 设备
driver 驱动
重新学习设备树:
dts 主要是描述板级信息
dtsi 主要描述soc信息;
make dtbs 命令编译dtb设备树文件
新板子添加设备dtb方法,
1添加一个dts文件
2、在dtb-$(CONFIG_SOC_IMX6ULL)下,添加一个板子的dtb信息
设备树dts语法:
1、头文件使用include 包含.h ,dts 和.dtsi文件;
2、节点格式:labe:node_name@unit_address ;{labe:标签;node_name,节点名字;unit_address:色号被地址或是寄存器地址,没有地址可以填0}
3、数据形式:
a,字符串
b, 32位无符号整数,数组
3,字符串列表;逗哈(,)分割
4、compatible 主要用于设备和驱动的绑定;属性格式"manufacturer,mode":nanufacturer是厂商名称,mode是驱动名字;属性之可以是多个,多个之间逗号(,)间隔
5、model 用于描述设备模块信息,属性是一个字符串,
6、status 设备状态;{"okey","disable","fail","fail-sss"};
7、#adress-cells 和 #size_cells :#address-cells决定了reg属性中地址的占用字节长度,#size-cells决定了reg属性长度信息所占的字长(32位)
8、reg :属性形式:<start_addres,address_length>
9、range 属性形式:<child_address,parent_address,child_address_length>
10、name
11、device_type
12、aliases 定义别名
13、chosen 主要是uboot向Linux传递参数;
中断有关的设备树属性信息:
①、 #interrupt-cells,指定中断源的信息 cells 个数。
②、 interrupt-controller,表示当前节点为中断控制器。
③、 interrupts,指定中断号,触发方式等。
④、 interrupt-parent,指定父中断,也就是中断控制器
---------------------------------------------
设备树在文件系统中的体现: 在/proc/device-tree
设备节点:
struct device_node {
const char *name; /* 节点名字 */
const char *type; /* 设备类型 */
phandle phandle;
const char *full_name; /* 节点全名 */
struct fwnode_handle fwnode;
struct property *properties; /* 属性 */
struct property *deadprops; /* removed 属性 */
struct device_node *parent; /* 父节点 */
struct device_node *child; /* 子节点 */
struct device_node *sibling;
struct kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
OF函数:
1、通过节点名查找指定节点:struct device_node * of_find_node_by_name(struct device_node *from_node,const char *name);
2、通过device_type查找指定节点:struct device_node * of_fine_node_by_type(struct device_node *from_node,const char *type);
3、通过compatible和device_type查找节点 struct device_node * of_find_compatible_node(struct device_node *from_node,const char *type,const char*compatible_name);
4、通过of_device_id来查找节点:struct device_node * of_find_matching_node_and_match(struct device_node *from_node,const char matchs*,const char** match);
5、通过路径来查找节点:struct device_node * of_find_by_name(const char *path);
6、查找指定节点的父节点:struct device_node * of_get_parent(const struct device_node *node)
7、迭代查找子节点:of_get_next_child(struct device_node *parent,struct device_node prev)
8、查找指定属性:property * of_find_property(struct device_node *node,const char *name,int *len);len属性字节数
9、获取属性元素的数量 :int of_property-count_elems_of_size(struct device_node *node,const char *prop_name,int len);//元素长度
10、从属性中获取指定索引位置的u32数据 int of_property_read_u32_index(struct device_node *node,const propname,u32 index,u32 *value);
11、读取属性中 u8、 u16、 u32 和 u64 类型的数组数据:
int of_property_read_u8_array(const struct device_node *np,
const char *propname,
u8 *out_values,
size_t sz)
int of_property_read_u16_array(const struct device_node *np,
const char *propname,
u16 *out_values,
size_t sz)
int of_property_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz)
int of_property_read_u64_array(const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz)
12、读取这种只有一个整形值的属性
int of_property_read_u8(const struct device_node *np,
const char *propname,
u8 *out_value)
int of_property_read_u16(const struct device_node *np,
const char *propname,
u16 *out_value)
int of_property_read_u32(const struct device_node *np,
const char *propname,
u32 *out_value)
int of_property_read_u64(const struct device_node *np,
const char *propname,
u64 *out_value)
13、读取属性中的字符串:int of_property_read_string(const struct device_node *np,const char *propname,char **value_str)
14、获取#address-cells数据 int of_n_addr_cells(const struct device_node *np);
15、获取#size-cells数据 int of_n_size_cells(const struct device_node *np);
------------------------
16、查看compatible属性是否包含conpat指定的字符串:of_device_is_compatible(const struct device_node *np,const char *compat_str);//返回 0 则包含
17、获取地址相关的属性,主要是“reg”或者“assigned-addresses”属性值:of_get_addr();
18、将从设备树得到的地址转换为物理地址:u64 of_translate_address(struct device_node *dev, const _be32 * in_addr);
19、将reg属性抓换位resource结构数据:int of_address_to_resource(struct device_node *dev,int index,struct *resource *re);
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;//常见参数IORESOURCE_MEM 、 IORESOURCE_REG 和IORESOURCE_IRQ
struct resource *parent, *sibling, *child;
};
20、获取内存地址对应的虚拟地址:of_iomap(struct device_node *np,int index);//index:reg 属性中要完成内存映射的段
--------------和gpio相关的of函数---------------------
21、获取设备树中某个属性中包含几个gpio信息 int of_gpio_named_count(struct device_node*np,const char *propname);
22、统计gpios这个属性的gpio数量 int of_gpio_count(struct device_node *np);
23、获取gpio编号 int of_get_named_gpio(struct *device_node *np,const char *propname,int index);//返回gpio编号
====================pintctrl 子系统==========start=======================
pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等
<mux_reg conf_reg input_reg mux_mode input_val> value
mux_reg 寄存器偏移地址
conf_reg 寄存器偏移地址
input_reg 寄存器偏移地址
mux_reg 寄存器值
input_reg 寄存器值
添加pinctrl模板:
1、在iomuxc节点下添加
2、节点前缀一定是pinctrl_
3、通过“fsl,pins”属性配置
{
pinctrl_test:test
{
fsl,pins = <
//pin配置信息
>
}
}
====================pintctrl 子系统==========end=======================
====================gpio 子系统==========end=======================
相关api:
1、申请一个gpio管脚: int gpio_request(unsigned gpio,const char *label);
2、释放gpio引脚: void gpio_free(unsigned gpio);
3、设置gppio输入: int gpio_direction_input(unsigned gpio);
4、设置gpio输出: int gpio_direction_output(unsigned gpio);
5、获取gpio值: int gpio_get_value (unsigned gpio);
6、设置gpio值: void gpio_set_value(unsigned gpio,int value);
====================gpio 子系统==========end=======================
==========原子操作==================================================
ATOMIC_INIT(int i) 定义原子变量的时候对其初始化。
int atomic_read(atomic_t *v) 读取 v 的值,并且返回。
void atomic_set(atomic_t *v, int i) 向 v 写入 i 值。
void atomic_add(int i, atomic_t *v) 给 v 加上 i 值。
void atomic_sub(int i, atomic_t *v) 从 v 减去 i 值。
void atomic_inc(atomic_t *v) 给 v 加 1,也就是自增。
void atomic_dec(atomic_t *v) 从 v 减 1,也就是自减
int atomic_dec_return(atomic_t *v) 从 v 减 1,并且返回 v 的值。
int atomic_inc_return(atomic_t *v) 给 v 加 1,并且返回 v 的值。
int atomic_sub_and_test(int i, atomic_t *v) 从 v 减 i,如果结果为 0 就返回真,否则返回假
int atomic_dec_and_test(atomic_t *v) 从 v 减 1,如果结果为 0 就返回真,否则返回假
int atomic_inc_and_test(atomic_t *v) 给 v 加 1,如果结果为 0 就返回真,否则返回假
int atomic_add_negative(int i, atomic_t *v) 给 v 加 i,如果结果为负就返回真,否则返回假
==========原子操作==================================================
==========原子位操作 API 函数==================================================
void set_bit(int nr, void *p) 将 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p) 将 p 地址的第 nr 位清零。
void change_bit(int nr, void *p) 将 p 地址的第 nr 位进行翻转。
int test_bit(int nr, void *p) 获取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p) 将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。
int test_and_clear_bit(int nr, void *p) 将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。
int test_and_change_bit(int nr, void *p) 将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值。
==========原子位操作 API 函数==================================================
==========自旋锁 API 函数==================================================
1、没有获取到锁就一直等着,cpu不处于工作状态,不释放cpu资源
2、中断里面使用自旋锁,但是在中断里面使用自旋锁的时候,在获取锁之前一定要先禁止本地中断
标准自旋锁:
一般代码内使用自旋锁:
DEFINE_SPINLOCK(spinlock_t lock) 定义并初始化一个自选变量。
int spin_lock_init(spinlock_t *lock) 初始化自旋锁。
void spin_lock(spinlock_t *lock) 获取指定的自旋锁,也叫做加锁。
void spin_unlock(spinlock_t *lock) 释放指定的自旋锁。
int spin_trylock(spinlock_t *lock) 尝试获取指定的自旋锁,如果没有获取到就返回 0
int spin_is_locked(spinlock_t *lock) 检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0。
中断代码部分的自旋锁:
void spin_lock_irq(spinlock_t *lock) 禁止本地中断,并获取自旋锁。
void spin_unlock_irq(spinlock_t *lock) 激活本地中断,并释放自旋锁。
建议使用以下两个:
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) 保存中断状态,禁止本地中断,并获取自旋锁。
void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags) 将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁。
在下半部里面使用自旋锁:
void spin_lock_bh(spinlock_t *lock) 关闭下半部,并获取自旋锁。
void spin_unlock_bh(spinlock_t *lock) 打开下半部,并释放自旋锁
------------------------------------------------------------------
读写自旋锁:
读写自旋锁为读和写操作提供了不同的锁,一次只能允许一个写操作,也就是只能一个线程持有写锁,而且不能进行读操作。但是当没有写操作的时候允许一个或多个线程持有读锁,可以进行并发的读操作
初始化读写自旋锁
DEFINE_RWLOCK(rwlock_t lock) 定义并初始化读写锁
void rwlock_init(rwlock_t *lock) 初始化读写锁。
读锁
void read_lock(rwlock_t *lock) 获取读锁。
void read_unlock(rwlock_t *lock) 释放读锁。
void read_lock_irq(rwlock_t *lock) 禁止本地中断,并且获取读锁。
void read_unlock_irq(rwlock_t *lock) 打开本地中断,并且释放读锁。
void read_lock_irqsave(rwlock_t *lock,unsigned long flags) 保存中断状态,禁止本地中断,并获取读锁。
void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags) 将中断状态恢复到以前的状态,并且激活本地中断,释放读锁。
void read_lock_bh(rwlock_t *lock) 关闭下半部,并获取读锁。
void read_unlock_bh(rwlock_t *lock) 打开下半部,并释放读锁。
写锁
void write_lock(rwlock_t *lock) 获取读锁。
void write_unlock(rwlock_t *lock) 释放读锁。
void write_lock_irq(rwlock_t *lock) 禁止本地中断,并且获取读锁。
void write_unlock_irq(rwlock_t *lock) 打开本地中断,并且释放读锁。
void write_lock_irqsave(rwlock_t *lock,unsigned long flags) 保存中断状态,禁止本地中断,并获取读锁。
void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags)将中断状态恢复到以前的状态,并且激活本地中断,释放读锁。
void write_lock_bh(rwlock_t *lock) 关闭下半部,并获取读锁。
void write_unlock_bh(rwlock_t *lock) 打开下半部,并释放读锁
-----------------------------------------------------------------------
顺序锁
描述:顺序锁在读写锁的基础上衍生而来的,使用读写锁的时候读操作和写操作不能同时进行。使用顺序锁的话可以允许在写的时候进行读操作,也就是实现同时读写,但是不允许同时进行并发的写操作
初始化顺序自旋锁
DEFINE_SEQLOCK(seqlock_t sl) 定义并初始化顺序锁
void seqlock_ini seqlock_t *sl) 初始化顺序锁。
顺序锁写操作
void write_seqlock(seqlock_t *sl) 获取写顺序锁。
void write_sequnlock(seqlock_t *sl) 释放写顺序锁。
void write_seqlock_irq(seqlock_t *sl) 禁止本地中断,并且获取写顺序锁
void write_sequnlock_irq(seqlock_t *sl) 打开本地中断,并且释放写顺序锁。
void write_seqlock_irqsave(seqlock_t *sl,unsigned long flags)保存中断状态,禁止本地中断,并获取写顺序锁。
void write_sequnlock_irqrestore(seqlock_t *sl,unsigned long flags)将中断状态恢复到以前的状态,并且激活本地中断,释放写顺序锁。
void write_seqlock_bh(seqlock_t *sl) 关闭下半部,并获取写读锁。
void write_sequnlock_bh(seqlock_t *sl) 打开下半部,并释放写读锁。
顺序锁读操作
unsigned read_seqbegin(const seqlock_t *sl)读单元访问共享资源的时候调用此函数,此函数会返回顺序锁的顺序号。
unsigned read_seqretry(const seqlock_t *sl,unsigned start)读结束以后调用此函数检查在读的过程中有没有对资源进行写操作,如果有的话就要重读
==========自旋锁 API 函数==================================================
==========信号量===========================================================
信号量的特点:
①、因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场合。
②、因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
③、如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势
DEFINE_SEAMPHORE(name) 定义一个信号量,并且设置信号量的值为 1。
void sema_init(struct semaphore *sem, int val) 初始化信号量 sem,设置信号量值为 val。
void down(struct semaphore *sem) 获取信号量,因为会导致休眠,因此不能在中断中使用。
int down_trylock(struct semaphore *sem); 尝试获取信号量,如果能获取到信号量就获取,并且返回 0。如果不能就返回非 0,并且不会进入休眠。
int down_interruptible(struct semaphore *sem); 获取信号量,和 down 类似,只是使用 down 进入休眠状态的线程不能被信号打断。而使用此函数进入休眠以后是可以被信号打断的。
void up(struct semaphore *sem); 释放信号量
==========信号量===========================================================
==========互斥体===========================================================
注意事项:
①、 mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
②、和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数。
③、因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁。
DEFINE_MUTEX(name) 定义并初始化一个 mutex 变量。
void mutex_init(mutex *lock) 初始化 mutex。
void mutex_lock(struct mutex *lock)获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠。
void mutex_unlock(struct mutex *lock) 释放 mutex,也就给 mutex 解锁。
int mutex_trylock(struct mutex *lock)尝试获取 mutex,如果成功就返回 1,如果失败就返回 0。
int mutex_is_locked(struct mutex *lock)判断 mutex 是否被获取,如果是的话就返回1,否则返回 0。
int mutex_lock_interruptible(struct mutex *lock)使用此函数获取信号量失败进入休眠以后可以被信号打断
==========互斥体===========================================================
===========================内核定时器=======================================
Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数
time_after(unkown, known) unkown>known ?1:0;
time_before(unkown, known) unkown<=known ?1:0; unkown 通常为 jiffies, known 通常是需要对比的值。
time_after_eq(unkown, known) unkown>=known ?1:0;
time_before_eq(unkown, known) unkown<=known ?1:0;
jiffies 和 ms、 us、 ns 之间的转换函数
===========================内核定时器=======================================
将 jiffies 类型的参数 j 分别转换为对应的毫秒、微秒、纳秒。
int jiffies_to_msecs(const unsigned long j)
int jiffies_to_usecs(const unsigned long j)
u64 jiffies_to_nsecs(const unsigned long j)
将毫秒、微秒、纳秒转换为 jiffies 类型。
long msecs_to_jiffies(const unsigned int m)
long usecs_to_jiffies(const unsigned int u)
unsigned long nsecs_to_jiffies(u64 n)
定时器:
void init_timer(struct timer_list *timer);//初始化定时器
void add_timer(struct timer_list *timeer);//添加以后自动运行
void del_timer(struct timer_list *timeer);//多核处理器需要等待处理函数退出才可以删除
void del_timer_sync(struct timer_list *timer);//等待其他处理器处理完成以后再删除
int mod_timer((struct timer_list *timeer,unsigned long expries);
内核延时函数:
纳秒、微秒和毫秒延时函数。
void ndelay(unsigned long nsecs)
void udelay(unsigned long usecs)
void mdelay(unsigned long mseces)
===========================内核定时器=======================================
===========================Liunx中断=======================================
不可以在中断上下文和其他禁止睡眠的代码段中使用 irq_requst;
irq_requst函数自动激活中断不需要手动开启
int requst_irq(unsigned int irq,//要申请的中断号
irq_handler_t handler,//中断处理函数
unsigned long flag,//中断标志
const char *name, //中断名字
void *dev)//如果将flag设置位IRQ_SHARED的话,dev用来区分不同的中断
flag:
IRQF_SHARED 设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话, request_irq 函数的 dev 参数就是唯一区分他们的标志。
IRQF_ONESHOT 单次中断,中断执行一次就接触。
IRQF_TRIGGER_NONE 无触发。
IRQF_TRIGGER_RISING 上升沿触发。
IRQF_TRIGGER_FALLING 下降沿触发。
IRQF_TRIGGER_HIGH 高电平触发。
IRQF_TRIGGER_LOW 低电平触发
释放中断:
void free_irq(unsigned int irq,void *dev);
中断处理函数:
irqreturn_t(*irq_handler_t (int ,void*));
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
中断使能和禁止:
void enable_irq(unsigned int irq);
void disable_irq(unsigned int irq);//等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出
void disable_irq_nosync(unsigned int irq);
全局中断关闭:
local_irq_enable();
local_irq_disable();
保存恢复中断状态的禁止和恢复:
local_irq_save(flags);//保存在flags 禁止
local_irq_restore(flags)//恢复
上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。
注册软中断:
void open_softirq(int nr,void (*action)(struct softirq_action));
触发软中断:
void raise_softirq(unsigned int nr);
nr:可选项
enum
{
HI_SOFTIRQ=0, /* 高优先级软中断 */
TIMER_SOFTIRQ, /* 定时器软中断 */
NET_TX_SOFTIRQ, /* 网络数据发送软中断 */
NET_RX_SOFTIRQ, /* 网络数据接收软中断 */
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, /* tasklet 软中断 */
SCHED_SOFTIRQ, /* 调度软中断 */
HRTIMER_SOFTIRQ, /* 高精度定时器软中断 */
RCU_SOFTIRQ, /* RCU 软中断 */
NR_SOFTIRQS
};
tasklet:
struct tasklet_struct
{
struct tasklet_struct *next; /* 下一个 tasklet */
unsigned long state; /* tasklet 状态 */
atomic_t count; /* 计数器,记录对 tasklet 的引用数 */
void (*func)(unsigned long); /* tasklet 执行的函数 */
unsigned long data; /* 函数 func 的参数 */
};
初始化tasklet:
DECLARE_TASKLET(name, func, data);
void tasklet_init(struct task_stuct *t,void (*fun)(unsigned long),unsigned long data );
中断上半部分:
void tasklet_schedule(struct task_stuct *t);
工作队列:
如果工作可以在睡眠,那么可以选择工作队列;
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; /* 工作队列处理函数 */
// void (*work_func_t)(struct work_struct *work);
};
初始化:INIT_WORK(_work,_fun);
工作调度:bool schedule_work(struct work_struct * work);
中断有关的设备树属性信息:
①、 #interrupt-cells,指定中断源的信息 cells 个数。
②、 interrupt-controller,表示当前节点为中断控制器。
③、 interrupts,指定中断号,触发方式等。
④、 interrupt-parent,指定父中断,也就是中断控制器
设备树操作:
从设备树获取中断号:
unsigned int irq_of_parse_and_map(struct device_node *dev,int index);
从设备树获取gpio对应的中断号:
int gpio_to_arq(unsigned int irq);
===========================Liunx中断=======================================
=================================等待队列===========================================
等待队列;
等待队列头:
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
1、初始化队列头
void init_waitqueue_head(wait_queue_head_t *q)
DECLARE_WAIT_QUEUE_HEAD
等待队列项:
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
DECLARE_WAITQUEUE(name, tsk)//初始化队列项
2、添加到队列头
void add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)
3、移除到队列头:
void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)
4、等待唤醒
void wake_up(wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)
参数 q 就是要唤醒的等待队列头,这两个函数会将这个等待队列头中的所有进程都唤醒。
wake_up 函数可以唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 状态的进程,
wake_up_interruptible 函数只能唤醒处于 TASK_INTERRUPTIBLE 状态的进程。
=================================等待队列===========================================
应用程序下的阻塞和非阻塞相关函数
select
int select(int nfsd, //要监视的文件描述符个数
fd_set *readfds,//监视指定描述符的读变化
fd_set *writefd,//监视指定描述符的写变化
fd_set *exceptfds,//监视指定描述符的异常变化
struct timeval* timeout);//超时
返回值:0超时 -1失败;>0表示有多少个变化;
struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微妙 */
void FD_ZERO(fd_set *set)
void FD_SET(int fd, fd_set *set)
void FD_CLR(int fd, fd_set *set)
int FD_ISSET(int fd, fd_set *set)
pool
struct foolfd{
int fd;
short events;
short reevents;
}
事件集合
POLLIN//有数据可以读取
POLLPRI//有紧急数据读取
POLLOUT//可以写数据
POLLERR//发生错误
POLLHHUP//文件挂起
POLLNVAL //无效请求
POLLRDNORM <=>POLLIN
int poll (struct pollfd *fds, //监视的文件描述符监视事件集合
nfds_t nfs,//集合数量
time timeout);//超时ms
返回发生事件的个数,0超时,-1发生错误
epoll
1、创建epoll句柄:int epoll_create(int size);//size 无意义
struct epoll_event {
uint32_t events; /* epoll 事件 */
epoll_data_t data; /* 用户数据 */
};
2、操作epoll句柄: int epoll_ctl(int epfd,//句柄
int op,//EPOOL_CTL-ADD,EPOOL_CTL-MOD,EPOOL_CTL-DEL
int fd, //文件描述符
struct epoll_event * event);
3、等待事件发生:int epoll_wait(int epfd,
struct epoll_event *events, //发生事件集合
int maxevents,//事件集合大小必须大于零
int timeout);//ms
驱动下的poll函数:
unsigned int (*poll) (struct file *filp,
struct poll_table_struct *wait);//一般传给poll_wait();
返回值:向应用程序返回设备或者资源状态,可以返回的资源状态
poll_wait 函数原型如下:
void poll_wait(struct file * filp,
wait_queue_head_t * wait_address, 要添加到 poll_table 中的等待队列头
poll_table *p)是 poll_table; file_operations 中 poll 函数的 wait 参数。
应用程序下的信号处理:
sighandler_t signal(int signum,sighandler_t handler);
typedef void (*sighandler_t)(int signum);
---------------------------------------------------------------------
应用程序对异步通知的处理包括以下三部:
1、注册信号处理函数
应用程序根据驱动程序所使用的信号来设置信号的处理函数,应用程序使用 signal 函数来
设置信号的处理函数。前面已经详细的讲过了,这里就不细讲了。
2、将本应用程序的进程号告诉给内核
使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核。
3、开启异步通知
使用如下两行程序开启异步通知:
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */
重点就是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函
数就会执行。
-----------------------------------------------------------------------
驱动内部异步处理:
实现函数 int (*fasync) (int fd, struct file *filp, int on);
发送信号:void kill_fasync(struct fasync_struct **fp, int sig, int band);
fp:要操作的 fasync_struct。
sig: 要发送的信号。
band: 可读时设置为 POLL_IN,可写时设置为 POLL_OUT。
==================================================================================