网站开发者工具的网络选项大连模板网站制作公司

news/2025/9/24 21:22:21/文章来源:
网站开发者工具的网络选项,大连模板网站制作公司,wordpress官方下载,帝国cms小说阅读网站模板http://blog.csdn.net/sailor_8318/archive/2008/06/30/2599357.aspx【摘要】本文分析了内核的同步及互斥的几种机制#xff1a;原子运算符(atomicoperator)、自旋锁Spinlock、等待队列Waitqueue、事件Event、completion、信号量Semaphore及其优化版互斥锁#xff0c;详细分析…http://blog.csdn.net/sailor_8318/archive/2008/06/30/2599357.aspx【摘要】本文分析了内核的同步及互斥的几种机制原子运算符(atomicoperator)、自旋锁Spinlock、等待队列Waitqueue、事件Event、completion、信号量Semaphore及其优化版互斥锁详细分析了其实现流程。Event及Semaphore本质上都是基于Waitqueue和自旋锁实现的。本文还探讨了每种机制最适合应用到哪些地方以及如何构建安全高效的内核及驱动代码。【关键词】原子操作SpinlockWaitqueuecompletionEventSemaphore------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------1      休眠与同步一个驱动当它无法立刻满足请求应当如何响应? 一个对 read 的调用可能当没有数据时到来, 而以后会期待更多的数据。或者一个进程可能试图写,但是你的设备没有准备好接受数据, 因为你的输出缓冲满了。调用进程往往不关心这种问题; 程序员只希望调用 read 或 write并且使调用返回, 在必要的工作已完成后. 这样, 在这样的情形中。驱动应当(缺省地)阻塞进程, 使它进入睡眠直到请求可继续。进程被置为休眠意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移走。直到发生某些事情改变了那个状态否则这个进程将不被任何 CPU调度运行。安全地进入休眠的两条规则1)       永远不要在原子上下文中进入休眠。当驱动在持有一个自旋锁、seqlock或者 RCU 锁时不能睡眠关闭中断也不能睡眠持有一个信号量时休眠是合法的但你应当仔细查看代码如果代码在持有一个信号量时睡眠任何其他的等待这个信号量的线程也会休眠。因此发生在持有信号量时的休眠必须短暂而且决不能阻塞那个将最终唤醒你的进程。2)       当进程被唤醒重新检查其所需资源。它并不知道休眠了多长时间以及休眠时发生什么也不知道是否另有进程也在休眠等待同一事件且那个进程可能在它之前醒来并获取了所等待的资源。所以不能对唤醒后的系统状态做任何的假设并必须重新检查等待条件来确保正确的响应。除非确信其他进程会在其他地方唤醒休眠的进程否则也不能睡眠。使进程可被找到意味着需要维护一个称为等待队列的数据结构。它是一个进程链表其中饱含了等待某个特定事件的所有进程。在 Linux 中 一个等待队列由一个wait_queue_head_t 结构体来管理。2      休眠的基础2.1   wait_queue系列数据结构2.1.1      wait_queue_head_t\include\linux\wait.hstruct __wait_queue_head {spinlock_t lock;struct list_head task_list;};typedef struct __wait_queue_head  wait_queue_head_t;它包含一个自旋锁和一个链表。这个链表是一个等待队列入口。关于自定义结构体的风格若需要提供别名则原始类型前面加”__”或者“tag_”表示其为内部数据类型对外是不可见的。typedef之后的类型为了和原始类型分开一般会在后面添加“_t”表示是typedef的对外使用。#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                           \.lock          __SPIN_LOCK_UNLOCKED(name.lock)              \.task_list    { (name).task_list (name).task_list } \}因为Linux内核对于链表的遍历方式的问题通常一个双向循环链表中有一个头节点其与其他节点的结构不一样并且通常无有效信息。此处的等待队列头有两个域1)       操作循环链表的互斥锁2)       嵌入到等待队列头中的链表头。为了用“.”域的形式初始化成员不能采用单独的初始化锁和链表头部的宏但可以采用声明一个结构体类型的宏如__SPIN_LOCK_UNLOCKED(name.lock).task_list的初始化应该采用LIST_HEAD_INIT宏的这样提高了可移植性。定义等待队列头部的同时初始化了其成员尤其是链表头的初始化是添加后续等待队列的前提。#define DECLARE_WAIT_QUEUE_HEAD(name) \wait_queue_head_t name __WAIT_QUEUE_HEAD_INITIALIZER(name)定义一个等待队列头同时分配内存并进行初始化。对外的接口。extern void init_waitqueue_head(wait_queue_head_t *q);void init_waitqueue_head(wait_queue_head_t *q){spin_lock_init(q-lock);INIT_LIST_HEAD(q-task_list);}动态初始化一个已经分配了内存的wait_queue_head_t结构。当wait_queue_head_t类型成员内嵌到其他结构体中时需要采用此方法而不能采用DECLARE_WAIT_QUEUE_HEAD全局或在栈中定义初始化一个wait_queue_head_t结构。2.1.2      wait_queue_tstruct __wait_queue {unsigned int flags; //在等待队列上唤醒时是否具备排他性#define WQ_FLAG_EXCLUSIVE  0x01void *private;wait_queue_func_t func; //从等待队列中唤醒进程时执行的统一操作将进程更改为可运行状态具体谁运行将由调度策略决定struct list_head task_list; //与该等待队列对应的链表};/*Macros for declaration and initialisaton of the datatypes*/#define __WAITQUEUE_INITIALIZER(name tsk) {                               \.private      tsk                                           \.func                default_wake_function                      \.task_list    { NULL NULL } \}GNU语法中对于结构体成员赋初值采用了域的形式如“.private   ”其好处在于a)       可以选择性的对部分成员赋值。当结构体成员变量较多而大部分无须初始值时此方法显得尤为重要因此减少了不必要的赋值。b)       赋值顺序与数据结构定义中成员的顺序无关因此若结构体成员顺序变化初始化部分不会受到任何影响。c)       各个域之间用“”分隔最后一个无“”。#define DECLARE_WAITQUEUE(name tsk)                                   \wait_queue_t name __WAITQUEUE_INITIALIZER(name tsk)全局或者在栈中定义一个wait_queue_t类型变量的同时对其初始化这保证了系统的可靠性避免因用户忘记初始化时导致的问题。通用的初始化宏tsk为任意指针。分两步1)       内部宏__WAITQUEUE_INITIALIZER初始化相应成员当wq内嵌在别的结构体中时此宏很重要提高了可移植性2)       提供给外部的接口定义一个变量并将第一步的结果赋值给该变量。static inline void init_waitqueue_entry(wait_queue_t *q struct task_struct *p){q-flags 0;q-private p;q-func default_wake_function;}动态初始化一个等待队列入口项将其和当前进程关联起来以便唤醒当前进程。2.1.3      数据结构设计规则今后凡遇到新设计一类结构体若此类结构体变量必须初始化且有相对集中的操作则应提供以下两个操作接口a)       定义新建一个结构体变量并初始化之b)       动态初始化一个已经分配内存的该类变量为了适应在堆栈及全局等任意地方分配的该变量其应该接收指向该类变量的指针。2.2   陈旧sleep_on系列//初始化一个wait_queue_t#define     SLEEP_ON_VAR                                  \unsigned long flags;                          \wait_queue_t wait;                          \init_waitqueue_entry(wait current);//添加到队列中#define SLEEP_ON_HEAD                                   \spin_lock_irqsave(q-lockflags);               \__add_wait_queue(q wait);                      \spin_unlock(q-lock);//从队列中删除#define     SLEEP_ON_TAIL                                 \spin_lock_irq(q-lock);                 \__remove_wait_queue(q wait);                        \spin_unlock_irqrestore(q-lock flags);SLEEP_ON_VAR、SLEEP_ON_HEAD及SLEEP_ON_TAIL总是同时出现不可分割。上述宏不是函数只是连续的表达式而已因为函数就将他们隔离开来了函数他退出后变量就无意义了。也不能写成do――while的多语句宏变量定义离开“{}”后就没有意义了。是为了编写代码更清晰明了同时避免多写字实际上就是代码封装复用。void fastcall __sched interruptible_sleep_on(wait_queue_head_t *q){SLEEP_ON_VAR //注意没“”current-state TASK_INTERRUPTIBLE;SLEEP_ON_HEADschedule(); //此处可能出问题SLEEP_ON_TAIL}EXPORT_SYMBOL(interruptible_sleep_on);添加到队列和从队列中删除由同一个模块做符合模块设计规则减小了耦合性。唤醒的wakeup只负责改变进程状态进程重新获得cpu后从队列中删除。long fastcall __schedinterruptible_sleep_on_timeout(wait_queue_head_t *q long timeout){SLEEP_ON_VARcurrent-state TASK_INTERRUPTIBLE;SLEEP_ON_HEADtimeout schedule_timeout(timeout);SLEEP_ON_TAILreturn timeout;}EXPORT_SYMBOL(interruptible_sleep_on_timeout);void fastcall __sched sleep_on(wait_queue_head_t *q){SLEEP_ON_VARcurrent-state TASK_UNINTERRUPTIBLE;SLEEP_ON_HEADschedule();SLEEP_ON_TAIL}EXPORT_SYMBOL(sleep_on);long fastcall __sched sleep_on_timeout(wait_queue_head_t *q long timeout){SLEEP_ON_VARcurrent-state TASK_UNINTERRUPTIBLE;SLEEP_ON_HEADtimeout schedule_timeout(timeout);SLEEP_ON_TAILreturn timeout;}EXPORT_SYMBOL(sleep_on_timeout);在决定调用sleep_on系列函数到真正调用schedule系列函数期间若等待的条件为真若此时继续schedule相当于丢失了一次唤醒机会。因此sleep_on系列函数会引入竞态导致系统的不安全。另外对于interruptible系列函数其返回时并不能确定是因为资源可用返回还是遇到了signal因此在程序中用户需要再次判断资源是否可用。如static ssize_t at91_mcp2510_read(struct file *filp, char *buffer, size_t count, loff_t *ppos){CanData candata_ret;retry:if (mcp2510dev.nCanReadpos !  mcp2510dev.nCanRevpos) {// 需求的资源int count;count MCP2510_RevRead(candata_ret);if (count) copy_to_user(buffer, (char *)candata_ret, count);return count;} else { //不可用if (filp-f_flags O_NONBLOCK)return -EAGAIN;interruptible_sleep_on((mcp2510dev.wq));if (signal_pending(current)) // 遇到信号返回return -ERESTARTSYS;goto retry; //重新判断资源是否可用}DPRINTK(read data size%d\n, sizeof(candata_ret));return sizeof(candata_ret);}综合上述两个因素sleep_on系列函数应避免在驱动程序中出现未来的2.7版内核中将删除此类函数。2.3   等待队列的接口2.3.1      初始化等待队列#define DEFINE_WAIT(name)                                              \wait_queue_t name {                                           \.private      current,                         \.func                autoremove_wake_function,          \.task_list    LIST_HEAD_INIT((name).task_list),   \}#define init_wait(wait)                                                    \do {                                                        \(wait)-private current;                         \(wait)-func autoremove_wake_function;             \INIT_LIST_HEAD((wait)-task_list);                 \} while (0)专用的初始化等待队列函数将当前进程添加到等待队列中注意和通用的接口DECLARE_WAITQUEUE及init_waitqueue_entry区别。同时唤醒处理不一样autoremove_wake_function在default_wake_function的基础之上将当前进程从等待队列中删除。2.3.2      添加或从等待队列中删除添加删除的原始接口static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new){list_add(new-task_list, head-task_list);}static inline void __add_wait_queue_tail(wait_queue_head_t *head,wait_queue_t *new){list_add_tail(new-task_list, head-task_list);}static inline void __remove_wait_queue(wait_queue_head_t *head,wait_queue_t *old){list_del(old-task_list);}等待队列是公用资源但上述接口没有加任何保护措施适用于已经获得锁的情况下使用。带锁并设置属性的添加删除void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait){unsigned long flags;wait-flags ~WQ_FLAG_EXCLUSIVE;spin_lock_irqsave(q-lock, flags);__add_wait_queue(q, wait);spin_unlock_irqrestore(q-lock, flags);}EXPORT_SYMBOL(add_wait_queue);void fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait){unsigned long flags;wait-flags | WQ_FLAG_EXCLUSIVE;spin_lock_irqsave(q-lock, flags);__add_wait_queue_tail(q, wait);spin_unlock_irqrestore(q-lock, flags);}EXPORT_SYMBOL(add_wait_queue_exclusive);void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait){unsigned long flags;spin_lock_irqsave(q-lock, flags);__remove_wait_queue(q, wait);spin_unlock_irqrestore(q-lock, flags);}EXPORT_SYMBOL(remove_wait_queue);此三对函数的特点是调用同名的内部函数同时添加一些保障安全特性的代码。WQ_FLAG_EXCLUSIVE属性的进程添加到队尾而非WQ_FLAG_EXCLUSIVE从队头添加。这样可以保证每次都能唤醒所有的非WQ_FLAG_EXCLUSIVE进程。无锁但设置属性的添加删除static inline void add_wait_queue_exclusive_locked(wait_queue_head_t *q,wait_queue_t * wait){wait-flags | WQ_FLAG_EXCLUSIVE;__add_wait_queue_tail(q,  wait);}/* Must be called with the spinlock in the wait_queue_head_t held.*/static inline void remove_wait_queue_locked(wait_queue_head_t *q,wait_queue_t * wait){__remove_wait_queue(q,  wait);}Locked系列适用于在已经获得锁的情况下调用通常用于信号量后者complete系列函数中。上述三组函数__add_wait_queue是内部函数无任何保护无任何属性设置而另外两组分别适用于当前是否加锁的两种场合是对外的接口函数。这种根据适用场合不同提供不同的接口函数的方法值得借鉴。2.3.3      prepare_to_wait和finish_wait/** Used to distinguish between sync and async io wait context:* sync i/o typically specifies a NULL wait queue entry or a wait* queue entry bound to a task (current task) to wake up.* aio specifies a wait queue entry with an async notification* callback routine not associated with any task.*/#define is_sync_wait(wait)       (!(wait) || ((wait)-private))同步io等待将唤醒当前进程异步io等待和当前进程无关时间到后执行安装的回调函数void fastcallprepare_to_wait(wait_queue_head_t *q wait_queue_t *wait int state){unsigned long flags;wait-flags ~WQ_FLAG_EXCLUSIVE; //非排它性的等待将都被唤醒spin_lock_irqsave(q-lock flags);//等待节点尚未添加到任何等待队列中防止重复加入if (list_empty(wait-task_list))__add_wait_queue(q wait);if (is_sync_wait(wait))set_current_state(state);spin_unlock_irqrestore(q-lock flags);}prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)wait-flags | WQ_FLAG_EXCLUSIVE;排他性等待其余和prepare_to_wait一样void fastcall finish_wait(wait_queue_head_t *q wait_queue_t *wait){unsigned long flags;__set_current_state(TASK_RUNNING); //确保进程状态为running//若有等待进程则将其从等待队列中删除if (!list_empty_careful(wait-task_list)) {spin_lock_irqsave(q-lock flags);list_del_init(wait-task_list);spin_unlock_irqrestore(q-lock flags);}}3      等待事件eventLinux 内核中最简单的休眠方式是称为 wait_event的宏(及其变种)它实现了休眠和进程等待的条件的检查。形式如下wait_event(queue,condition)/*不可中断休眠,不推荐*/ wait_event_interruptible(queue,condition)/*推荐返回非零值意味着休眠被中断,且驱动应返回 -ERESTARTSYS*/wait_event_timeout(queue, condition, timeout)wait_event_interruptible_timeout(queue, condition, timeout)/*有限的时间的休眠若超时则不管条件为何值返回0*/上述四个宏函数为内核对外的接口其他的休眠函数应避免使用。因为宏并不是函数参数所做的任何修改对调用环境仍然有效所以queue都是“值传递”在宏内部会调用底层函数采用的指针传递。Linux内核中存在大部分这样的宏其都在接口上都是值传递。3.1   wait_event认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue,condition)底层源码会发现其实他们只是手工休眠中的函数的组合。因此在驱动程序中应避免使用手动休眠代码而应该采用内核已经封装好的四个wait_event系列函数。\include\linux\wait.h#define  __wait_event(wqcondition)                \do {                           \DEFINE_WAIT(__wait);                       \\for(;;) {                      \prepare_to_wait(wq__waitTASK_UNINTERRUPTIBLE);   \/// 添加到等待队列中同时更改进程状态若已经加入则不会重复添加if (condition)                        \break;                          \schedule();   //何时返回呢                       \}                               \finish_wait(wq__wait);                    \} while (0)// “__”表示内部函数默认为condition不满足添加至等待队列调度注意prepare_to_wait和finish_wait的匹配关系#define  wait_event(wqcondition)                    \do {                                   \if(condition)                              \break;                               \__wait_event(wqcondition);                    \}while (0)//对外的接口函数需要判断condition若假则等待若真则直接退出--------------------------------------------------------------------------------------------------------------等待系列函数架构设计:3.2   wait_event_timeout#define __wait_event_timeout(wq condition ret)                     \do {                                                               \DEFINE_WAIT(__wait);                                       \\for (;;) {                                                  \prepare_to_wait(wq __wait TASK_UNINTERRUPTIBLE);   \if (condition)                                            \break;                                             \ret schedule_timeout(ret);                             \if (!ret)                                            \break;              //延时到退出                              \}                                                            \finish_wait(wq __wait);                                \} while (0)#define  wait_event_timeout(wqconditiontimeout)      \({        \long   __rettimeout;     \if( !(condition) )             \__wait_event_timeout( wqcondition__ret);     \__ret;     \})3.3   wait_event_interruptible#define __wait_event_interruptible(wq, condition, ret)                      \do {                                                                \DEFINE_WAIT(__wait);                                       \\for (;;) {                                                  \prepare_to_wait(wq, __wait, TASK_INTERRUPTIBLE); \if (condition)                                            \break;                                             \if (!signal_pending(current)) {                          \schedule();                                      \continue;                                 \}                                                     \ret -ERESTARTSYS;                                   \break;                                                     \}                                                            \finish_wait(wq, __wait);                                    \} while (0)/*** wait_event_interruptible - sleep until a condition gets true* wq: the waitqueue to wait on* condition: a C expression for the event to wait for** The process is put to sleep (TASK_INTERRUPTIBLE) until the* condition evaluates to true or a signal is received.* The condition is checked each time the waitqueue wq is woken up.** wake_up() has to be called after changing any variable that could* change the result of the wait condition.** The function will return -ERESTARTSYS if it was interrupted by a* signal and 0 if condition evaluated to true.*/#define wait_event_interruptible(wq, condition)                        \({                                                                   \int __ret 0;                                                   \if (!(condition))                                        \__wait_event_interruptible(wq, condition, __ret);      \__ret;                                                             \})3.4   wait_event_interruptible_timeout#define   __wait_event_interruptible_timeout(wqconditionret)     \do {                               \DEFINE_WAIT(__wait);                     \\for (;;) {                          \prepare_to_wait(wq__waitTASK_INTERRUPTIBLE);   \if (condition)                   \break;                     \if(!signal_pending(current)) {                 \// 当前进程无信号需要处理ret schedule_timeout(ret);            \if(!ret)                   \break; //时间片用完唤醒                     \continue;                 \              .}                            \ret _ERESTARTSYS;   //被信号唤醒                  \break;                            \}                            \finish_wait(wq__wait);                   \} while (0)#define   wait_event_interruptible_timeout(wqconditiontimeout)    \( {                                 \long__ret timeout;                      \if(!(condition))                       \__wait_event_interruptible_timeout(wqcondition__ret);  \__ret;                               \})wait_event_interruptible_timeout()类架构:4      唤醒系列wake_up4.1   wake_up 的API惯例用 wake_up 唤醒 wait_event用 wake_up_interruptible唤醒wait_event_interruptible。很少会需要调用wake_up_interruptible之外的唤醒函数但为完整起见这里是整个集合wake_up(wait_queue_head_t *queue);wake_up_interruptible(wait_queue_head_t *queue); /*wake_up唤醒队列中的每个非独占等待进程和一个独占等待进程。wake_up_interruptible 同样,但它跳过处于不可中断休眠的进程。它们在返回之前, 使一个或多个进程被唤醒、被调度(如果它们被从一个原子上下文调用, 这就不会发生).*/--------------------------------------------------------------------------------wake_up_nr(wait_queue_head_t*queue, int nr); wake_up_interruptible_nr(wait_queue_head_t *queue, intnr); /*这些函数类似 wake_up, 除了它们能够唤醒多达 nr 个独占等待者, 而不只是一个. 注意传递 0被解释为请求所有的互斥等待者都被唤醒*/--------------------------------------------------------------------------------wake_up_all(wait_queue_head_t*queue); wake_up_interruptible_all(wait_queue_head_t *queue); /*这种wake_up 唤醒所有的进程, 不管它们是否进行独占等待(可中断的类型仍然跳过在做不可中断等待的进程)*/--------------------------------------------------------------------------------wake_up_interruptible_sync(wait_queue_head_t*queue); /*一个被唤醒的进程可能抢占当前进程, 并且在 wake_up 返回之前被调度到处理器。 但是,如果你需要不要被调度出处理器时可以使用 wake_up_interruptible 的同步变体.这个函数最常用在调用者首先要完成剩下的少量工作且不希望被调度出处理器时。*/4.2   wake_up 的实现细节\kernel \sched.c/** The core wakeup function.  Non-exclusive wakeups (nr_exclusive 0) just* wake everything up.  If its an exclusive wakeup (nr_exclusive small ve* number) then we wake all the non-exclusive tasks and one exclusive task.** There are circumstances in which we can try to wake a task which has already* started to run but is not in state TASK_RUNNING.  try_to_wake_up() returns* zero in this (rare) case and we handle it by continuing to scan the queue.*/static void __wake_up_common(wait_queue_head_t *q unsigned int modeint nr_exclusive int sync void *key){struct list_head *tmp *next;list_for_each_safe(tmp next q-task_list) {wait_queue_t *curr list_entry(tmp wait_queue_t task_list);unsigned flags curr-flags;if (curr-func(curr mode sync key) (flags WQ_FLAG_EXCLUSIVE) !--nr_exclusive)break;}}//循环结束的条件包括list_for_each_safe本身、排他性(flags WQ_FLAG_EXCLUSIVE)以及唤醒非0个进程!--nr_exclusive非常巧妙当传入0时!--nr_exclusive的结果总是0if条件不可能成立就无法break即表示唤醒所有的进程。对于非WQ_FLAG_EXCLUSIVE进程由于(flags WQ_FLAG_EXCLUSIVE)为0后就不计算!--nr_exclusive因此这个过程可以唤醒所有的非WQ_FLAG_EXCLUSIVE进程。但遇到WQ_FLAG_EXCLUSEVE之后的任意进程无法唤醒。最终哪个进程运行是由schedule决定的。/*** __wake_up - wake up threads blocked on a waitqueue.* q: the waitqueue* mode: which threads* nr_exclusive: how many wake-one or wake-many threads to wake up* key: is directly passed to the wakeup function*/void fastcall __wake_up(wait_queue_head_t *q unsigned int mode int nr_exclusive void *key){unsigned long flags;spin_lock_irqsave(q-lock flags);__wake_up_common(q mode nr_exclusive 0 key);//通用的wakeup不可重入的需要__wake_up对之进行封装spin_unlock_irqrestore(q-lock flags);}EXPORT_SYMBOL(__wake_up);根据int nr_exclusive值唤醒对应的进程只是更改了进程的状态具体何时运行由schedule决定。并没有将唤醒的进程从等待队列中删除只有当schedule获得cpu时才从等待队列中删除。\include\linux\wait.hvoid FASTCALL(__wake_up(wait_queue_head_t *q unsigned int mode int nr void *key));extern void FASTCALL(__wake_up_locked(wait_queue_head_t *q unsigned int mode));extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q unsigned int mode int nr));#define wake_up(x)                 __wake_up(x TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE 1 NULL)#define wake_up_nr(x nr)           __wake_up(x TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE nr NULL)#define wake_up_all(x)                   __wake_up(x TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE 0 NULL)#define wake_up_interruptible(x)     __wake_up(x TASK_INTERRUPTIBLE 1 NULL)#define wake_up_interruptible_nr(x nr)       __wake_up(x TASK_INTERRUPTIBLE nr NULL)#define wake_up_interruptible_all(x)        __wake_up(x TASK_INTERRUPTIBLE 0 NULL)各种wakeup通过宏定义的形式本质上就是一个函数__wake_up但对外的接口不一样这样对外的意义明确相当于采用了默认参数而不是在各个wakeup内部调用函数省掉了函数调用的开销。在实现代码复用的同时保证了对外的明确接口值得借鉴。#define     wake_up_locked(x)          __wake_up_locked((x) TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)#define wake_up_interruptible_sync(x)   __wake_up_sync((x)TASK_INTERRUPTIBLE 1)5      独占等待和高级休眠5.1   独占等待当一个进程调用 wake_up 在等待队列上所有的在这个队列上等待的进程被置为可运行的。这在许多情况下是正确的做法。但有时可能只有一个被唤醒的进程将成功获得需要的资源而其余的将再次休眠。这时如果等待队列中的进程数目大这可能严重降低系统性能。为此内核开发者增加了一个“独占等待”选项。它与一个正常的睡眠有 2 个重要的不同:2      当等待队列入口设置了 WQ_FLAG_EXCLUSEVE 标志它被添加到等待队列的尾部否则添加到头部。因为唤醒一个WQ_FLAG_EXCLUSEVE标志的进程后就不再唤醒其他任意类型的进程。添加在尾部可以保证FIFO。2      当 wake_up 被在一个等待队列上调用, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止唤醒。但内核仍然会唤醒所有的非独占等待进程因为所有的非WQ_FLAG_EXCLUSIVE进程在队头。采用独占等待要满足以下条件:2      希望对资源进行有效竞争2      当资源可用时唤醒一个进程就足够来完全消耗资源2      所有使用该资源的进程都应采用统一的独占等待规则。使一个进程进入独占等待可调用void prepare_to_wait_exclusive(wait_queue_head_t *queue, wait_queue_t *wait, int state);注意无法使用通用的wait_event 和它的变体宏函数来进行独占等待。此时需要使用休眠的高级特性利用等待队列的prepare_to_wait_exclusive和finish_wait接口函数手动编写相关代码5.2   高级休眠的基本步骤(1)分配和初始化一个 wait_queue_t 结构 随后将其添加到正确的等待队列。(2)设置进程状态标记为休眠。TASK_RUNNING 意思是进程能够运行。有 2 个状态指示一个进程是在睡眠:TASK_INTERRUPTIBLE 和 TASK_UNTINTERRUPTIBLE。2.6内核的驱动代码通常不需要直接操作进程状态。但如果需要这样做使用的代码是:void set_current_state(int new_state);在老的代码中, 你常常见到如此的东西:current-state TASK_INTERRUPTIBLE; 但是象这样直接改变current 是不推荐的当数据结构改变时这样的代码将会失效。通过改变 current状态只改变了调度器对待进程的方式但进程还未让出处理器。(3)最后一步是放弃处理器。 但必须先检查进入休眠的条件。如果不做检查会引入竞态 如果决定休眠后在做上述准备工作到真正调用schedule时若等待的条件变为真不对条件重新进行判断则你可能错过唤醒且长时间休眠。因此典型的代码下if (!condition)     schedule();在调用schedule前应对条件再次进行检查。(4)更改进程状态并将进程从等待队列中删除。如果代码是从 schedule 返回则进程肯定处于TASK_RUNNING 状态。 如果不需睡眠而跳过对 schedule的调用必须将任务状态重置为 TASK_RUNNING。无论是否调用过schedule都需要从等待队列中去除这个进程否则它可能被多次唤醒。5.3   手工休眠的具体函数执行流特殊睡眠要求程序员手动处理所有上面的步骤. 它是一个繁琐的过程, 包含相当多的易出错的样板式的代码. 程序员如果愿意还是可能用那种方式手动睡眠。(1)创建和初始化一个等待队列。常由宏定义完成: DEFINE_WAIT(my_wait); /*name 是等待队列入口项的名字.也可以用2步来做:*/ wait_queue_t my_wait; init_wait(my_wait); /*常用的做法是放一个DEFINE_WAIT 在循环的顶部来实现休眠。*/(2)添加等待队列入口到队列并设置进程状态: voidprepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, intstate); /*queue 和 wait 分别地是等待队列头和进程入口。state是进程的新状态TASK_INTERRUPTIBLE(可中断休眠推荐)或TASK_UNINTERRUPTIBLE(不可中断休眠不推荐)。*/prepare_to_wait_exclusive(3)在检查确认仍然需要休眠之后调用 scheduleschedule();(4)schedule 返回重新判断等待条件若为真则退出否则继续schedule(5)条件满足退出后确保状态为running同时将进程从等待队列中删除。void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);5.4   wait_event_interruptible_exclusive为了避免手工编写上述复杂代码内核提供了最常见的interruptible类型排他性等待函数。而对于非排他性的可以直接利用等待事件event系列函数。#define __wait_event_interruptible_exclusive(wq, condition, ret)      \do {                                                                \DEFINE_WAIT(__wait);                                       \\for (;;) {                                                  \prepare_to_wait_exclusive(wq, __wait,                     \TASK_INTERRUPTIBLE);            \if (condition)                                            \break;                                             \if (!signal_pending(current)) {                          \schedule();                                      \continue;                                 \}                                                     \ret -ERESTARTSYS;                                  \break;                                                     \}                                                            \finish_wait(wq, __wait);                                    \} while (0)#define wait_event_interruptible_exclusive(wq, condition)        \({                                                                   \int __ret 0;                                                   \if (!(condition))                                        \__wait_event_interruptible_exclusive(wq, condition, __ret);\__ret;                                                             \})6      Completioncompletion是一种轻量级的机制它允许一个线程告诉另一个线程某个工作已经完成。代码必须包含。使用的代码如下DECLARE_COMPLETION(my_completion);/*创建completion(声明初始化) */ struct completion my_completion;/*动态声明completion 结构体*/ static inline voidinit_completion(my_completion);/*动态初始化completion*/ voidwait_for_completion(struct completion *c);/* 等待completion */ voidcomplete(struct completion *c);/*唤醒一个等待completion的线程*/ voidcomplete_all(struct completion *c);/*唤醒所有等待completion的线程*//*如果未使用completion_allcompletion可重复使用否则必须使用以下函数重新初始化completion*/INIT_COMPLETION(struct completion c);/*快速重新初始化completion*/completion的典型应用是模块退出时的内核线程终止。在这种模式某些驱动程序的内部工作有一个内核线程在while(1)循环中完成。当内核准备清除该模块时exit函数会告诉该线程退出并等待completion。为此内核包含了用于这种线程的一个特殊函数void complete_and_exit(struct completion *c, long retval);本文来自ChinaUnix博客如果查看原文请点http://blog.chinaunix.net/u3/105293/showart_2132308.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/916260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

个人网站 虚拟主机米绘花型设计师服务平台

雷军:共建一个更良性包容的汽车市场舆论环境 Figure 与 OpenAI 联手推出新机器人 亚马逊和 Google 悄悄降低对生成式 AI 的预期 小米生态链模式大改革,将进行分级管理 掌阅科技:致力打造国内首款真正 AI 阅读应用 荣耀称已投入 100 亿用于 AI…

深圳工信部网站备案信息查询中小企业 网站建设

题目描述 一个 NM 的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻 8个格子中的一个即认为这两个数字相邻),求取出数字和最大是多少。 输入格式 第…

完整教程:【力扣LeetCode】 1413_逐步求和得到正数的最小值

完整教程:【力扣LeetCode】 1413_逐步求和得到正数的最小值2025-09-24 21:16 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !importa…

mysql慢sql配置

mysql中my.ini或my.cfg文件

Linux zdb -C (zfs Debugger调试器)

Linux zdb -C (zfs Debugger调试器)zdb -C是 ZFS 调试器(ZFS Debugger)中一个用于深入检查存储池配置和元数据的强大命令。它主要用于​​诊断和解决一些非常棘手的问题​​。由于它直接操作存储池的元数据,使用前请…

从零开始实现简易版Netty(八) MyNetty 实现Small规格的池化内存分配

从零开始实现简易版Netty(八) MyNetty 实现Small规格的池化内存分配从零开始实现简易版Netty(八) MyNetty 实现Small规格的池化内存分配 1. Netty Small规格池化内存分配介绍 在上一篇博客中,lab7版本的MyNetty实现了…

域名指向另一个网站久久建筑网企业

1、准备合约 如何写合约,与编译之前的文章已经写过了,准备好.wasm文件。 2、测试程序 xwasm/wasm/tests at main XuHugo/xwasm GitHub 2.1 读取合约 let modules fs::read("./tests/wasmfile/contract.wasm").unwrap(); 2.2预编译合约…

邯郸菜鸟网站建设网站建设jnlongji

本次主要解析STM32网络通信中WebServer应用,从网页界面的编写到浏览器与STM32之间进行通信的数据来说明SSI与CGI的原理及应用,并对GET与POST指令进行应用解析。 硬件和软件环境: 1.硬件环境:STM32F407,网卡芯片LAN87…

新节点加入k8s集群命令查看 - 详解

新节点加入k8s集群命令查看 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&…

测试脚本

测试脚本$language = "python" $interface = "1.0" import sys reload(sys) sys.setdefaultencoding(utf-8) # 设置默认编码为UTF-8 global left left = "" # 新增全局变量初始化…

自动化测试脚本

自动化测试脚本$language = "python" $interface = "1.0" import sys reload(sys) sys.setdefaultencoding(utf-8) # 设置默认编码为UTF-8 import re # 必须导入正则模块 global left global ri…

建设工程合同备案网站wordpress 404页面

演讲嘉宾:易毅 总经理 广东芬蓝环境科技有限公司 演讲题目:成套低温烘干装备在发酵行业领域的节能创新应用 会议简介 “十四五”规划中提出,提高工业、能源领城智能化与信息化融合,明确“低碳经济”新的战略目标,热…

网站是哪个建站公司做的表白网站制作生成器

前言本文主要实现的功能是删除某个目录及目录下的所有子目录和文件,涉及到的知识点:File.delete()用于删除“某个文件或者空目录”!所以要删除某个目录及其中的所有文件和子目录,要进行递归删除。具体代码示例如下:imp…

解题报告-字符串(str.*)

字符串(str.*) 题目描述 Diaoyeye 正在研究字符串。nyx向他问了一个问题:有一个字符串𝑆,其中不同子串的 个数。 Diaoyeye 显然直接秒掉。他现在想问一问 nyx ,有一个字符串 \(𝑆\),从中选出两个子串 \(A\),\…

WPF Datagrid loaded 79M items in mvvm , Microsoft.Extensions.DependencyInjection

Install-Package Microsoft.Extensions.DependencyInjection; public async Task InitBooksCollection(){stopwatch.Start();BooksCollection = new ObservableCollection<Book>();List<Book> booksList =…

实用指南:python+django/flask的宠物救助及领养系统javaweb

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Linux 系统中的 /dev/disk/by-id/目录作用详解

Linux 系统中的 /dev/disk/by-id/目录作用详解Linux 系统中的 /dev/disk/by-id/目录是一个非常重要的组成部分,它能帮助咱们更稳定、更可靠地管理磁盘设备。下面我来为你详细解释它的作用和用法。 📁 一、/dev/disk…

万江专业网站快速排名个人免费网站注册

本研究的主要目的是基于Python aiortc api实现抓取本地设备媒体流&#xff08;摄像机、麦克风&#xff09;并与Web端实现P2P通话。本文章仅仅描述实现思路&#xff0c;索要源码请私信我。 1 demo-server解耦 1.1 原始代码解析 1.1.1 http服务器端 import argparse import …

glTF/glb:您需要知道的一切,怎么免费获取下载

有一种新的丰富 3D 模型格式,称为 glTF,并且一直在崛起。本文将告诉您有关 glTF 的所有信息,包括它是什么、为什么开发它以及谁在使用它。glb下载官网免费获取模型什么是glTF? GL 传输格式(简称 glTF)是一种开源…

成品网站短视频源码搭建网站建设培训 苏州

首次连接 打开装有 AirPods 的充电盒&#xff0c;并将它放在 iPhone 旁边。此时你的 iPhone 上将出现设置动画。轻点「连接」&#xff0c;然后轻点「完成」。 就这么简单&#xff0c;而且会自动设置&#xff0c;实现与已使用同一 Apple ID 登录 iCloud 的任一支持设备搭配使用…