1.DECLARE_WAITQUEUE(wait, current);
//通过DECLARE_WAITQUEUE宏将等待队列项初始化成对应的任务结构,并且用于连接的相关指针均设置为空。
其函数宏定义在wait.h中
#define __WAITQUEUE_INITIALIZER(name, tsk) { \ .private = tsk, \ .func = default_wake_function, \ .task_list = { NULL, NULL } } #define DECLARE_WAITQUEUE(name, tsk) \ wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
但还包括两个参数:wait 和current
参数current定义在#include <asm/current.h>中
#define get_current() (current_thread_info()->task) #define current get_current()
#define current_thread_info() ((struct thread_info *)current) //在<linux/thread_info.h>中
arch\arm\include\asm\thread_info.h
struct thread_info { unsigned long flags; /* low level flags */ int preempt_count; /* 0 => preemptable, <0 => bug */ mm_segment_t addr_limit; /* address limit */ struct task_struct *task; /* main task structure */ __u32 cpu; /* cpu */ __u32 cpu_domain; /* cpu domain */ struct cpu_context_save cpu_context; /* cpu context */ __u32 syscall; /* syscall number */ __u8 used_cp[16]; /* thread used copro */ unsigned long tp_value[2]; /* TLS registers */
而wait应该是一个赋值的参数,结果赋给wait,其类型为__wait_queue结构体类型:
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
所以DECLARE_WAITQUEUE(wait, current);这行代码的我理解的意思就是定义一个名为wait类型为wait_queue_t(__wait_queue结构体类型的别名)并将宏参current赋值给wait的private属性。
2.__set_current_state(TASK_INTERRUPTIBLE);//改变进程状态为睡眠
#define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 /* Used in tsk->exit_state: */ #define EXIT_DEAD 16 #define EXIT_ZOMBIE 32 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) /* Used in tsk->state again: */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_NOLOAD 1024 #define TASK_NEW 2048 #define TASK_STATE_MAX 4096 #define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWPNn"TASK_RUNNING : 进程处于可运行状态,但并不意味着进程已经实际上已分配到 CPU ,它可能会一直等到调度器选中它。该状态只是确保进程一旦被 CPU 选中时立马可以运行,而无需等待外部事件。
TASK_INTERRUPTIBLE : 这是针对等待某事件或其他资源而睡眠的进程设置的。在内核发送信号给该进程时表明等待的事件已经发生或资源已经可用,进程状态变为 TASK_RUNNING,此时只要被调度器选中就立即可恢复运行。
TASK_UNINTERRUPTIBLE : 处于此状态,不能由外部信号唤醒,只能由内核亲自唤醒。
TASK_STOPPED : 表示进程特意停止运行。比如在调试程序时,进程被调试器暂停下来。
TASK_TRACED : 本来不属于进程状态,用于从停止的进程中,将当前被调试的那些进程与常规进程区分开来。
在驱动程序中,进程睡眠往往通过 3 个步骤进行:
1. 将进程加入等待队列中。
2. 然后使用 set_current_state() 来设置进程的状态,设置的状态为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUTIBLE 。
3. 上面的设置完后,我们就要放弃处理器了。但在放弃处理器之前,还有一件重要的事情需要做:检查睡眠等待的条件。如果不检查,如果此时条件正好变为真,那么就漏掉了继续运行的机会,从而会睡眠更长的时间。就好比如,你在等一辆车,你觉得车还没来,你很困并就打算先睡一会儿,此时有一辆车刚好过来了,你睡眼朦胧的并没打算睁开眼睛去看一下,结果得花更长的时间来等下一趟。所以,一般在睡前需要类似的动作:
1. 将进程加入等待队列中。
2. 然后使用 set_current_state() 来设置进程的状态,设置的状态为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUTIBLE 。
3. 上面的设置完后,我们就要放弃处理器了。但在放弃处理器之前,还有一件重要的事情需要做:检查睡眠等待的条件。如果不检查,如果此时条件正好变为真,那么就漏掉了继续运行的机会,从而会睡眠更长的时间。就好比如,你在等一辆车,你觉得车还没来,你很困并就打算先睡一会儿,此时有一辆车刚好过来了,你睡眼朦胧的并没打算睁开眼睛去看一下,结果得花更长的时间来等下一趟。所以,一般在睡前需要类似的动作:
set_current_state(TASK_UNINTERRUPTIBLE);
if (do_i_need_to_sleep())
schedule();//调度进程执行
关于进程调度函数 schedule()参考https://blog.csdn.net/dong_zhihong/article/details/7990560
3.signal_pending(current)解析
检查当前进程是否有信号处理,返回不为0表示有信号需要处理。
返回 -ERESTARTSYS 表示信号函数处理完毕后重新执行信号函数前的某个系统调用。也就是说,如果信号函数前有发生系统调用,在调度信号处理函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.
如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用
4.signal_pending()函数不存在
经查是版本的问题我的版本( 4.13.0-43-generic)较教程上的要新。此函数的定义位置发生变化,因此要导入它的新包
进入include目录通过grep -nr "signal_pending" * 命令来查找
在驱动模块文件即.c文件中导入#inclue <linux/sched/signal.h>问题解决,编译成功。