linux内核开发总结----内核同步与异步

杂项:

gcc编译器内置宏变量:
__FILE__ :当前文件名
__FUNCTION__ :当前函数名;
__LINE__ :的文件中的行数
__DATA__ :编译时的日期
__TIME__ :编译时间

gcc -E 预处理 
-S 汇编
-c 编译

as  汇编器

ld  链接器

1.属性声名:指定一个属性只需在其声明后添加__attribute__((ATTRIBUTE));

例如:void do_exit(int n)__attribute((noreturn));
属性:
noreturn:表示函数从不返回任何值。
format:表示函数使用printf,scanf或strftime风格的参数,根据格式串检查参数类型。
unused:表示该函数或变量可能不会被用到。
aligned(n):指定变量,结构体或联合体的对齐方式,以字节为单位。
packed:作用于变量或结构体成员使用最小可能的对齐。

2.内核模块参数:

MODULE_LICENSE("Dual BSD/GPL"); //声明模块采用BSD/GPL双license
参数类型 参数名;(定义一个参数)
module_parma(参数名,参数类型,参数读/写权限(/sys/module/xxx/parameters));
insmod 模块名 参数名=参数值

参数类型:byte,short,ushort,int,uint,long,ulong,bool,charp(字符指针)
读写权限:S_IRWXU,S_IRUSR

EXPORT_SYMBOL(符号名);
EXPORT_SYMBOL_GPL(符号名);
/proc/kallsyms 内核符号表

可选:
MODULE_AUTHOR();作者
MODULE_DESCRIPTION();描述
MODULE_VERSION();版本
MODULE_DEVICE_TABLE();设备表
MODULE_ALIAS();别名


3.内核空间与用户空间传递数据

用户空间--》内核空间
unsigned long copy_from_user(void*to,const void*from,unsigned long n);
to:内核目标地址
from:用户空间源地址
n:要拷贝的字节数
返回:成功返回0,失败返回没有拷贝成功的字节数。
int get_user(data,ptr);
data:可以是字节,半字,字,双字类型的内核变量。
ptr:用户空间内存指针。
返回:成功返回0,失败返回非0.
内核空间--》用户空间
unsigned long copt_to_user(void*to,const void*from,unsigned long n);
to:用户空间目标地址
from:内核空间源地址
n:要拷贝的字节数。
返回:成功返回0,失败返回没有拷贝成功的字节数。
int put_user(data,ptr);
data:可以是字节,半字,字,双字类型的内核变量。
ptr:用户空间内存指针。
返回:成功返回0,失败返回非0
用户空间内存可访问性验证:
int access_ok(int type,const void*addr,unsigned long size);
type:取值为VERIFY_READ 或VERIFY_WRITE
addr:待验证的用户内存地址
size:待验证的用户内存长度
返回值:返回非0代表用户内存可访问 ,返回0代表失败

调度:SCHED_NORMAL,SCHED_FIFO,SCHED_RR,SCHED_BATCH,SCHED_IDLE
主动调度:
current->state=TASK_INTERRUPTIBLE;
schedule();
schedule_timeout();
 被动调度:中断,时间片
 

 4.procfs:

 struct proc_dir_entry{
mode_t mode;文件权限保护位
struct module *owner;当前拥有者
read_proc_t *read_proc;读函数
write_proc_t *write_proc;写函数
 }
 typedef int(read_proc_t)(char*page,char**start,off_t off,int count,int *eof,void*data)
page:要返回给用户的信息存放页面,最多一个PAGE_SIZE.
start:一般不使用 data:一般不使有
off:读数据偏移
count:用户要读取的数据长度
eof:读到文件结尾时,需要把*eof设为1

typedef int(write_proc_t)(struct file*file,const char __user*buffer,unsigned long count,void*data)
file:该procfs文件对应的内核struct file结构。
buffer:用户要写入的数据在用户空间的指针
count:用户要写入的数据大小
data:一般不使用
创建目录:
 struct proc_dir_entry*proc_mkdir(char*name,struct proc_dir_entry*parent);
创建文件:
struct proc_dir_entry*create_proc_entry(char*name,mode_t mode,struct proc_dir_entry*parent);
删除文件:
void remove_proc_entry(char*name,struct proc_dir_entry*parent);

5,内核线程:

内核线程: 内核支持,多线程内核。
轻量级进程(LWP): 由内核支持的用户线程(各种系统调用)。是基于内核线程的抽象。
用户线程: 基于用户空间的线程库。用户线程的建立,同步,销毁,调度完全在用户空间完成。

创建内核线程:

linux/sched.h
pid_t  kernel_thread(int(*fn)(void*),void*arg,unsigned long flags);//fork实现原理

创建线程:
linux/kthread.h

struct task_struct*kthread_creat(int(*threadfn)(void*data),void*data,char namefmt[],...);

wake_up_process(struct task_struct*p);//唤醒指定的线程

创建并立即唤醒线程:
#definekthead_run(threadfn,data,namefmt,...) \
{ struct task_struct *__k=kthread_create(threadfn,data,namefmt,##__VA_ARGS__); \
if(!IS_ERR(__k))\
wake_up_process(__k); \
k;  \
}
绑定到指定的CPU核:
voidkthread_bind(struct task_struct*k,unsigned int cpu);
int kthread_stop(struct task_struct*k);结束指定的线程

线程函数内API:
int kthread_should_stop(void);//线程函数内判断是否收到结束信号
set_current_state(TASK_INTERRUPTIBLE);//设置进程当前状态
schedule();调度
schedule_timeout();//定时调度

一,Linux 内核同步方法

竞态条件 两个或更多线程同时操作资源时将会导致不一致的结果。
临界段 用于协调对共享资源的访问的代码段。
互斥锁 确保对共享资源进行排他访问的软件特性。
死锁 由两个或更多进程和资源锁导致的一种特殊情形,将会降低进程的工作效率。

中断屏蔽:
屏蔽本CPU中断: local_irq_disable();local_irq_save();
critical section //临界区
开本CPU中断: local_irq_enable();local_irq_restore();
屏蔽本CPU中断下半部:local_bh_disable();
使能本CPU中断下半部:local_bh_enable();

1.原子操作:

typedef struct {
int counter;
} atomic_t;
定义一个原子变量: atomic_t my_counter;           
定义并初始化原子变量: atomic_t my_counter=ATOMIC_INIT(0);
设置原子变量: atomic_set( &my_counter, n );
读原子变量的值:val = atomic_read( &my_counter );
原子变量+n: atomic_add( 1, &my_counter ); val=atomic_add_return(value,&atomic) 
原子变量+1: atomic_inc( &my_counter );val=atomic_inc_return(atomic_t*v);
原子变量-n:atomic_sub( 1, &my_counter );val=atomic_sub_return(value,&atomic)
原子变量-1:atomic_dec( &my_counter );var=atomic_dec_return(atomic_t*v);

改变原子变量的值并且测试是否为0
atomic_sub_and_test( 1, &my_counter )
atomic_dec_and_test( &my_counter )
atomic_inc_and_test( &my_counter )
atomic_add_negative( 1, &my_counter )

位原子操作
设置位:set_bit(nr,void*addr);//设置addr地址的第nr位为1.
清除位:clear_bit(nr,void*addr);//设置addr地址的第nr位为0.
改变位:change_bit(nr,void*addr);//对addr地址的第nr位进行反置。
测试位:test_bit(nr,void*addr);//返回addr地址的第nr位。
测试并操作位:
int test_and_set_bit(nr,void*addr);
int test_and_clear_bit(nr,void*addr);
int test_and_change_bit(nr,void*addr);
位掩码
unsigned long my_bitmask;
atomic_clear_mask( 0, &my_bitmask );
atomic_set_mask( (1<<24), &my_bitmask );

2.自旋锁(用于SMP,单CPU时禁止内核抢占)


定义自旋锁:spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;
DEFINE_SPINLOCK( my_spinlock );
初始化自旋锁:spin_lock_init( &my_spinlock );
加锁:spin_lock( &my_spinlock );spin_trylock(lock);
临界区...
解锁:spin_unlock( &my_spinlock );

(为了避免在临界区中还受到中断和底半部bh的影响)
加锁并关本地CPU中断:spin_lock_irqsave( &my_spinlock, flags );
//critical section
解锁并恢复本地CPU中断:spin_unlock_irqrestore( &my_spinlock, flags );
加锁并关本地CPU软中断:spin_lock_bh( &my_spinlock );
// critical section
加锁并恢复本地CPU软中断:spin_unlock_bh( &my_spinlock );

3.读写(自旋)锁-->RCU(读拷贝更新):可以看作读写锁的高性能版。

读写锁变量:rwlock_t my_rwlock=RW_LOCK_LOCKED;//静态初始化
读写锁初始化:rwlock_init( &my_rwlock );
写锁加锁:write_lock( &my_rwlock );
write_lock_irq(lock);
write_lock_irqsave(lock,flags);
write_lock_bh(lock);
write_trylock(lock);
//critical section -- can read and write
写锁解锁: write_unlock( &my_rwlock );
write_unlock_irq(lock);
write_unlock_irqrestore(lock,flags);
write_unlock_bh(lock);
write_unlock_irq(lock);
write_unlock_irqrestore(lock,flags);
write_unlock_bh(lock);

读锁加锁: read_lock( &my_rwlock );
read_lock_irq(lock);
read_lock_irqsave(lock,unsigned long flags);
read_lock_bh(lock);
//critical section -- can read only

读锁解锁:read_unlock( &my_rwlock );
read_unlock_irq(lock);
read_unlock_irqrestore(lock,flags);
read_unlock_bh(lock);

4.顺序锁:seqlock是对读写锁的一种优化。要求被保护的共享资源不含有指针。

定义: seqlock_t sl;
初始化: seqlock_init(&sl);

读顺序锁: unsigned rsl=read_seqbegin(seqlock_t &sl);
读顺序锁检查: int read_seqretry(seqlock_t*sl,unsigned start);//对共享资源访问完后,检查在读访问期间是否有写操作发生。
使用模型:
do{
sequnum=read_seqbegin(&sl);
......
critical section;临界区
.....
}while(read_seqretry(&sl,seqnum));

写顺序锁锁定:write_seqlock(seqlock_t*sl);
write_tryseqlock(seqlock_t*sl);
write_seqlock_irq(seqlock_t*sl);
write_seqlock_irqsave(seqlock_t*lock,unsigned long flags);
write_seqlock_bh(seqlock_t*lock);
......critical section;//临界区
写顺序锁解锁:
write_sequnlock(seqlock_t*sl);
write_sequnlock_irq(seqlock_t*sl);
write_sequnlock_irqrestore(seqlock_t*lock,unsigned long flags);
write_sequnlock_bh(seqlock_t*lock);

5.互斥体(锁)(信号量为1的特例)

声明一个互斥体: struct mutex my_mutex;
初始化互斥体: mutex_init(&my_mutex);
 定义一个互斥锁:DEFINE_MUTEX(my_mutex);
 非阻塞加锁: void fastcall mutex_trylock(&my_mutex);
 阻塞加锁: void fastcall mutex_lock(&my_mutex);
 可中断阻塞加锁:void fastcall mutex_lock_interruptible(&my_mutex);
...critical section;临界区
 解锁:void fastcall mutex_unlock( &my_mutex );
 检查锁状态:mutex_is_locked( &my_mutex );

6.信号量(初始化为0时,用于同步)

struct semaphore {
raw_spinlock_tlock;
unsigned int count;
struct list_headwait_list;
};
定义信号量:struct semaphore sem;
初始化信号量:sema_init(struct semaphore*sem,int val);//初始化信号量sem,并设置count=val;
init_MUTEX(struct semaphore*sem);//初始化信号量sem,并设置count=1;
init_MUTEX_LOCKED(struct semaphore*sem);
//初始化信号量,并将count=0。也就是创建时就处于锁定状态。
获取信号量:
void down(struct semaphore*sem);//获取信号量,可能睡眠,不能用于中断上下文
int down_interruptible(struct semaphore*sem);//获取信号量,信号量不可用时,
把进程设置为TASK_INTERRUPTIBLE的睡眠状态,
返回值0代表获取信号量正常返回,非0代表被信号打断返回。
int down_killable(struct semaphore*sem);//获取信号量,当信号量不可用时,把进程设置为TASK_KILLABLE的睡眠状态。
int down_trylock(struct semaphore*sem);//不会引起睡眠,可在中断上下文中使用。
......critical section临界区
释放信号量:
void up(struct semaphore*sem);//释放信号量sem,实质上把sem的值加1,
如果sem的值为非正数,表明有任务在等待,因此需要唤醒等待者。

7.完成变量completion;

struct completion {
unsigned int done;
wait_queue_head_t wait;
};
定义完成变量:
struct completion my_completion;
初始化完成变量:
init_completion(&my_completion);
DECLARE_COMPLETION(my_completion);
DECLARE_COMPLETION_ONSTACK(work);
等待完成变量:
void wait_for_completion(struct completion *c);
唤醒完成变量:
void complete(struct completion*c);
void complete_all(struct completion*c);

8,大内核锁BLK(越来越少用)

 lock_kernel();

 unlock_kernel();


二,内核异步方法(API) 

1.linux中断

 中断请求号IRQ->中断向量-> 224:irq_desc_t irq_desc[NR_IRQ](前32个为异常和NMI,32~48为INTR,48~255为软中断)
中断线->中断处理程序---------->中断服务程序(ISR)
中断上半部(Tob Half) 中断下半部(Bottom Half)
软中断(softirq,tasklet,work queue)

中断申请:
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char*name,void*dev_id); 
irq:想要申请的中断号
handler:想要注册的中断处理函数(返回:IRQ_NONE:未做处理,IRQ_HANDLED:正常处理后返回该值)
static irqreturn_t (*irq_handler_t)(int irq,void*dev_id);
irqflags:中断标志
IRQF_SHARED:表示多个设备共享中断
IRQF_SAMPLE_RANDOM:用于随机数种子的随机采样
IRQF_TRIGGER_RISING:上升沿触发中断
IRQF_TRIGGER_FALLING:下降沿触发中断
IRQF_TRIGGER_HIGH:高电平触发中断
IRQF_TRIGGER_LOW:低电平触发中断
name:中断设备的名称
dev_id:传递给中断处理函数的指针,通常用于共享中断时传递设备结构体指针
成功返回0,失败返回负值。
-EINVAL:表示申请的中断号无效或中断处理函数指针为空
-EBUSY:表示中断已经被占用并且不能共享


中断释放:
void free_irq(unsigned int irq,void *dev_id);

 使能和屏蔽中断:
 void disable_irq(unsigned int irq);屏蔽指定的中断
 void disable_irq_nosync(unsigned int irq);屏蔽指定的中断,立即返回,不等待可能正在执行的中断处理程序。
 void enable_irq(unsigned int irq);使能指定的中断
本CPU全部中断:
local_irq_disable();local_irq_save();
......
local_irq_enable();local_irq_restore(); 

软中断和tasklet:
local_bh_disable();
local_bh_enable();

2.中断下半部:(软中断,tasklet仍然运行于中断上下文,工作队列是进程上下文)

2.1.软中断;用软件方式模拟硬件中断,来实现异步执行。

声明软中断:<linux/interrupt.h>中定义一个枚举类型来静态声明软中断。
HI_SOFTIRQ 0
TIMER_SOFTIRQ 1
NET_TX_SOFTIRQ2
NET_RX_SOFTIRQ3
......
RCU_SOFTIRQ //建立一个新的软中断在此之前添加一项
NR_SOFTIRQS
注册软中断:
open_softirq(int nr,void(*action)(struct softirq_action*));
触发软中断:
raise_softirq(int nr);raise_softirq_irqoff(int nr);
唤醒软中断处理线程ksoftirqd:void wakeup_softirqd(void);

2.2.tasklet:基于软中断

tasklet定义并初始化:DECLARE_TASKLET(taskletname,task_func,data);
DECLARE_TASKLET_DISABLED(name,func,data);
tasklet_init(taskletname,task_func,data);
tasklet处理函数:void tasklet_func(unsigned long data);
tasklet调用:void tasklet_schedule(struct tasklet_struct*taskletname);
void tasklet_hi_schedule(struct tasklet_struct*d);
tasklet禁止:
void tasklet_disable_nosync( struct tasklet_struct * );
void tasklet_disable( struct tasklet_struct * );

tasklet使能:
void tasklet_enable( struct tasklet_struct * );
void tasklet_hi_enable( struct tasklet_struct * );
tasklet关闭:void tasklet_kill( struct tasklet_struct * );

void tasklet_kill_immediate( struct tasklet_struct *, unsigned int cpu );

2.3.工作队列:把推后的工作交由内核线程去执行。允许重新调度或睡眠。

#include <linux/workqueu.h>

#include <linux/kthread.h>

#include <linux/sched.h>

工作数据类型定义(2.6.20后分成两部分):
struct work_struct {
atomic_long_t data;
struct list_head entry;工作数据链成员
work_func_t func;工作处理函数,由用户实现
};
struct delayed_work {
struct work_struct work;工作结构体
struct timer_list timer;推后执行的定时器
/* target workqueue and CPU ->timer uses to queue ->work */
struct workqueue_struct *wq;
int cpu;
};

2.3.1,工作

初始化工作:(使用默认的工作者线程)
声明并初始化工作: DECLARE_WORK(name,func,data);

初始化工作队列: INIT_WORK(struct work_struct*work,work_func_t func);

初始化廷迟工作队列: INIT_DELAYED_WORK(struct delayed_work*work,wrok_func_t func);

调度工作:int schedule_work(struct work_struct*work);//把工作提交给缺省的工作者线程处理;内部是queue_work(system_wq, work);
int schedule_delayed_work(struct delayed_work*work,unsigned long delay);把工作提交给缺省的工作者线程处理,并指定延迟时间。

刷新工作队列:(唤配工作线程)
void flush_scheduled_work(void);
//刷新缺省的工作队列,此函数一直等待,直到队列中的所有工作完成。
bool flush_delayed_work(struc delayed_work*dwork);
bool flush_work(struct work_struct*work);
取消延迟工作:int cancel_delayed_work(struct delayed_work*work);
//取消缺省工作队列中处于等待状态的延迟工作。
bool cancel_delayed_work_sync(struct delayed_work*work);

取消工作:
int cancel_work_sync(struct work_struct*work);
//取消缺省工作队列中处于等待状态的工作,如果工作已经开始执行,该函数会阻塞直到工作处理函数完成。

2.3.2,工作者线程:默认情况下,每个CPU均有一个events/n的工作者线程,当调用schedule_work()时,这个工作者线会被唤醒去执行工作链表上的所有工作。

工作队列数据类型:
struct workqueue_struct{
......
const char*name;
struct list_head list;
......
}

创建工作队列:struct workqueue_struct*create_workqueue(const char*name);

stuct workqueue_struct *create_singlethread_workqueue(const char* name);

(3.x内核API) struct workqueue_struct *alloc_workqueue(fmt, flags, max_active,args...); //3.9内核中工作队列使用线程池,全部由kworker/n 执行

//创建新的工作队列和相应的工作者线程,
调度工作:int queue_work(struct workqueue_struct*wq,struct work_struct*work);
//调度工作,类似于schedule_work()函数,将指定的工作work加入到工作队列wq.
调度延迟工作:
int queue_delayed_work(struct workqueue_struct*wq,struct delayed_work*dwork,unsigned long delay);
//调度工作,类似于schedule_work()函数,将延迟工作work提交给工作队列wq,并指定延迟时间.
刷新工作队列:
void flush_workqueue(struct workqueue_struct*wq);
//刷新工作队列wq,此函数一直等待,直到队列中的所有工作完成。
销毁工作队列:
void destroy_workqueue(struct workqueue_struct*wq);//销毁工作队列wq

 3,内核定时器

 struct timer_list {
struct list_head entry;链表接入件
unsigned long expires;到期时间
struct tvec_base *base;
void (*function)(unsigned long);超时处理函数
unsigned long data;超时处理函数参数
int slack;
}
struct timer_list mytimer;
DEFINE_TIMER(name,function,expires,data);
TIMER_INITIALIZER(function,expires,data);
初始化计时器: void init_timer(struct timer_list *timer);
安装计时器: setup_timer(timer, fn, data)
添加定时器: add_timer(timer);//修改expieres+n*jiffies,再次添加。
修改计时器: mod_timer(struct timer_list*timer,unsigned long expires);
删除计时器: del_timer(struct timer_list*timer);
检测计时器是否在等待(还没发出):int timer_pending(struct timer_list*timer);

4,高精度计时器hrtimer-->基于ktime_t

void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,enum hrtimer_mode mode);
int
hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode);
int hrtimer_cancel(struct hrtimer *timer);
int hrtimer_try_to_cancel(struct hrtimer *timer)

5,等待队列wait queue


定义等待队列头:wait_queue_head_t wqh;
初始化等待队列头:init_waitqueue_head(wait_queue_head_t*wqh);
定义并初始化等待队列头: DECLARE_WAIT_QUEUE_HEAD(name);

定义等待队列: wait_queue_t name;
初始化等待队列: init_wait(name);
定义并初始化等待队列:DECLARE_WAITQUEUE(name, tsk);

添加等待队列:add_wait_queue(wait_queue_head_t*q,wait_queue_t*wait);
//将等待队列wait加入到等待队列头q执行的等待队列链表中去。或者从中删除。
移除等待队列:remove_wait_queue(wait_queue_head_t*q,wait_queue_t*wait);

等待事情:
wait_event(qhead,condition);//当条件为真时,立即返回,
否则进入TASK_UNINTERRUPTIBLE的睡眠状态,并挂在queue指定的等待队列上。
wait_event_interruptible(qhead,condition);
add_wait_queue(qhead,condition);//当条件为真时,立即返回,
否则进入TASK_INTERRUPTIBLE的睡眠状态,并挂在queue指定的等待队列上。
wait_event_killable(qhead,condition);//当条件为真时,立即返回,
否则进入TASK_KILLABLE的睡眠状态,并挂在queue指定的等待队列上。
wait_event_timeout(qhead,condition,timeout);//当条件为真时,立即返回,
否则进入TASK_UNINTERRUPTIBLE的睡眠状态,并挂在queue指定的等待队列上,当阻塞时间timeout超时后,立即返回。
wait_event_interruptible_timeout(qhead,condition,timeout);//当条件为真时,立即返回,
否则进入TASK_INTERRUPTIBLE的睡眠状态,并挂在queue指定的等待队列上,当阻塞时间timeout超时后,立即返回。

唤醒队列:
wake_up(wait_queue_head_t*queue);
wake_up_interruptible(wait_queue_head_t*queue);
//唤醒由queue指向的等待队列头链表中所有等待队列对应的进程。
在等待队列中睡眠:
sleep_on(wait_queue_head_t*q);//让进程进入不可中断的睡眠,并将它放入等待队列。
interruptible_sleep_on(wait_queue_head_t*q);//让进程进入可中断的睡眠,并将它放入等待队列。

等待队列头使用:
init_waitqueue_head(wait_queue_head_t*wqh);
wait_event_interruptible(qhead,condition);
wake_up_interruptible(wait_queue_head_t*queue);
等待队列使用:
init_waitqueue_head(wait_queue_head_t*qhead);
DECLARE_WAITQUEUE(name, current);
add_wait_queue(&qhead,&name);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
wake_up_interruptible(*qhead);
remove_wait_queue(&qhead,&name);
set_current_state(TASK_RUNNING);
内核等待队列一般使用方法:
1.定义和初始化等待队列,将进程状态改变,并将等待队列放入等待队列数据链中
2.改变进程状态
a>,set_current_state(state_value);
b>,set_task_state(task,state_value);
c>,current->state=TASK_INTERRUPTIBLE;
3.放弃CPU,调度其它进程执行:schedule();//schedule_timeout();
4.进程被其它地方唤醒,将等待队列移出等待队列头指向的数据链。

6,内核链表

struct mydata{
...
struct list_head list
}
struct list_head*pos,*q;
struct mydata*obj;
创建并初始化链表头:LIST_HEAD(myhead);
初始化链表头: struct list_head myhead = LIST_HEAD_INIT(myhead);
obj=kmalloc(sizeof(struct mydata),GFP_KERNEL);
obj->...对象成员操作
list_add(&obj->list,&myhead)
插入链表前面:list_add(struct list_head*new,struct list_head*head);

插入链表后面:list_add_tail(struct list_head*new,struct list_head*head);

for循环获取链表中的链入件: list_for_each(pos,&myhead){
从链入件得到对象:list_entry(pos,struct mydata,list); //list_entry(ptr,type,member)=container_of(ptr, type, member)
}
for循环获取链表中的对象:list_for_each_entry(obj,&myhead,list){};//list_for_each_entry(pos,head,member){}

(删除时)for循环取链表中的链入件:list_for_each_safe(pos,q,&myhead){
struct mydata*tmp;
tmp=list_entry(pos,struct mydata,list);
删除一个链接件: list_del(pos);
kfree(tmp);
}

把一个链表插入一个链表前:list_splice(struct list_head*list,struct list_head*head);
list_splice_tail(list,head);
判断链表是否为空:list_empty(struct list_head*head);

7,轮询:(字符轮询 驱动实现)

pull_wait();

8,异步通知队列:(支持异步通知机制设备驱动实现)

struct fasync_struct {
spinlock_t fa_lock;
int magic;
int fa_fd;
struct fasync_struct*fa_next; /* singly linked list */
struct file *fa_file;
struct rcu_headfa_rcu;
};
常见用法:
设备驱动中声明一个该类型变量:
struct xxx_cdev{
struct cdev chrdev;
...
struct fasync_struct *async_queue;
}
设备驱动中实现fasync():
static int xxx_fasync(){
struct xxx_cdev*dev=filep->private_data;
return fasync_helper(fd,filp,mode,&dev->async_queue);
}
fasync_helper()函数;初始化异步事件通知队列,包括分配内存和设置属性;释放初始化时分配的内存。
kill_fasync(struct fasync_struct**fp,int sig,int band)函数;
在设备资源可以获得时,应该调用此函数释放SIGIO信号,(band)可读时为POLL_IN,可写时为POLL_OUT;

你可能感兴趣的:(linux内核开发总结----内核同步与异步)