||
Tasklet ,工作队列,软中断为底半部调度机制。他们的使用方式都是如下:
先定义Tasklet 工作队列 软中断和相关的处理函数,再初始化相关结构体,最后在顶半部开启调度函数即可。?
Tasklet 软中断的实质都是软中断:即是硬件中断函数对内核的中断,故而运行于中断上下文;而工作队列本质是使用守护线程 运行于进程上下文;所以前者服务函数中不能有睡眠,而后者可以。?
struct tasklet_struct ?
{ ?
struct tasklet_struct *next;//next tasklet ??
unsigned long state;
//tasklet”s state TASKLET_STATE_SCHED被调度 TASKLET_STATE_RUN已运行?
atomic_t count;//use”s counter 不为0时表示被禁止,为0时不为激活
void (*func)(unsigned long); //bottom half FUNC
unsigned long data;// func”S parament?
};
tasklet_schedule函数为如下:检查state变量是否为TASKLET_STATE_SCHED,是则保存中断状态屏蔽本地中断源,把需要调度的tasklet加到每个处理器的tasklist_vec链表表头上去,唤醒tasklet_softirq软中断这样下一次do_softirq()调用时就会执行该tasklet,恢复中断状态回.??Workqueue机制就是为了简化内核线程的创建,内核开始为用户分配一个workqueue对象,并且将其链到一个全局的workqueue队列中,然后Linux根据当前CPU的情况为workqueue对象分配与CPU个数相同的cpu_workqueue_struct对象,每个cpu_workqueue_struct对象都会存在一条任务队列(work_struct链成的链表),Linux为每个cpu_workqueue_struct对象分配一个内核thread(daemon守护进程)去处理每个任务队列中的任务,内核线程工作比较简单,就是不断的扫描对应cpu_workqueue_struct中的任务队列,从中获取一个有效任务,然后执行该任务。所以如果任务队列为空,那么内核daemon就在cpu_workqueue_struct中的等待队列上睡眠,直到有人唤醒daemon去处理任务队列。?
在Workqueue机制中,提供了一个系统默认的workqueue队列——keventd_wq,这个队列是Linux系统在初始化的时候就创建的。用户可以直接初始化一个work_struct对象,然后在该队列中进行调度,使用更加方便(中断只是定义一个工作节点work_struct对象和他的处理函数后加入到 keventd_wq结构中的任务队列中,使用schedule_work唤醒daemon去处理任务队列).??
struct workqueue_struct { ??
struct cpu_workqueue_struct *cpu_wq; //每个CPU的工作队列结构??
const char *name; ??
struct list_head list;
/* 工作队列所有的线程链表,根据系统CPU个数创建线程数量.创造一个线程时为空/??
}; ??
工作节点结构是对任务的抽象 ??
struct work_struct { ??
unsigned long pending; //等待时间??
struct list_head entry; /* 任务挂载链表*/??
void (*func)(void *);/* 处理函数 */??
void *data; /* 任务处理的数据*/?
void *wq_data;/* work的属主cpu_workqueue_struct*/??
struct timer_list timer;/* 任务延时处理定时器 */??
};??
struct cpu_workqueue_struct { ?
spinlock_t lock; ?
long remove_sequence; // 下一个要执行的节点序号?
long insert_sequence; // 下一个要插入节点的序号?
struct list_head worklist; // 工作节点链表?
wait_queue_head_t more_work; // 要进行处理的等待队列?
wait_queue_head_t work_done; // 处理完的等待队列?
//以上两个等待队列用于daemon进程,当没有任务时则链接到 more_work睡眠等待,处理完后链接
到work_done只等到新的任务节点加入时重新链接到more_work睡眠等待?
struct workqueue_struct *wq; // 工作队列节点??
struct task_struct *thread; // 进程指针??
int run_depth; /* Detect run_workqueue() recursion depth */ ?
} ____cacheline_aligned;
?? ??
图中 workqueue_struct ->cpu_wq=&cpu_workqueue_struct ??
workqueue_struct -> list由daemon0--2链接而成??
cpu_workqueue_struct -> wq=& workqueue_struct表示属于该工作队列??
cpu_workqueue_struct -> thread=&daemon(0--2)??
work_struct -> wq_data=& cpu_workqueue_struct表示属于该cpu处理??
第一个工作节点有 work_struct.entry.pre= &cpu_workqueue_struct .worklist.next??
最后一个工作节点有 work_struct.entry.next=&cpu_workqueue_struct .worklist.pre??
其余的工作节点 work_struct.entry均互相链接成一个任务链表 (----work-work-work----)