学习ldd3--工作队列(第七章)
来源:互联网 发布:乐秀rx5数据 编辑:程序博客网 时间:2024/06/02 17:09
作者:张伟AreS
/*
* Call jiq_print from a work queue
*/
static void jiq_print_wq(void *ptr)
{
struct clientdata *data = (struct clientdata *) ptr;
if (! jiq_print (ptr))
return;
if (data->delay)
schedule_delayed_work(&jiq_work, data->delay);
else
schedule_work(&jiq_work);
}
/******************************************************************************/
学习此之前先需学习《学习ldd3--proc文件系统(第七章与第四章)》
学习此之前先需学习《学习ldd3--proc文件系统(第七章与第四章)》
时钟中断、内核定时器、Tasklet代码均在jit.c中
工作队列 (代码在D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\misc-modules\jiq.c中)
工作队列workqueue类似于tasket,但tasket的代码必须是原子的,而workqueue可以休眠
工作队列类似 taskets,允许内核代码请求在将来某个时间调用一个函数,不同在于:
(1)tasklet 在软件中断上下文中运行,所以 tasklet 代码必须是原子的。而工作队列函数在一个特殊内核进程上下文运行,有更多的灵活性,且能够休眠。
(2)tasklet 只能在最初被提交的处理器上运行,这只是工作队列默认工作方式。
(3)内核代码可以请求工作队列函数被延后一个给定的时间间隔。
(4)tasklet 执行的很快, 短时期, 并且在原子态, 而工作队列函数可能是长周期且不需要是原子的,两个机制有它适合的情形。
工作队列workqueue类似于tasket,但tasket的代码必须是原子的,而workqueue可以休眠
工作队列类似 taskets,允许内核代码请求在将来某个时间调用一个函数,不同在于:
(1)tasklet 在软件中断上下文中运行,所以 tasklet 代码必须是原子的。而工作队列函数在一个特殊内核进程上下文运行,有更多的灵活性,且能够休眠。
(2)tasklet 只能在最初被提交的处理器上运行,这只是工作队列默认工作方式。
(3)内核代码可以请求工作队列函数被延后一个给定的时间间隔。
(4)tasklet 执行的很快, 短时期, 并且在原子态, 而工作队列函数可能是长周期且不需要是原子的,两个机制有它适合的情形。
工作队列有 struct workqueue_struct 类型,在 <linux/workqueue.h> 中定义。一个工作队列必须明确的在使用前创建,宏为:
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
每个工作队列有一个或多个专用的进程("内核线程"), 这些进程运行提交给这个队列的函数。
若使用 create_workqueue, 就得到一个工作队列它在系统的每个处理器上有一个专用的线程。
在很多情况下,过多线程对系统性能有影响,如果单个线程就足够则使用 create_singlethread_workqueue 来创建工作队列。
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
每个工作队列有一个或多个专用的进程("内核线程"), 这些进程运行提交给这个队列的函数。
若使用 create_workqueue, 就得到一个工作队列它在系统的每个处理器上有一个专用的线程。
在很多情况下,过多线程对系统性能有影响,如果单个线程就足够则使用 create_singlethread_workqueue 来创建工作队列。
提交一个任务给一个工作队列,需要填充work_struct或delayed_work结构
/*若在运行时需要建立 work_struct 或 delayed_work结构, 使用下面 2 个宏定义:*/
INIT_WORK(struct work_struct *work, void (*function)(void *));
PREPARE_WORK(struct work_struct *work, void (*function)(void *));
INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
PREPARE_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
INIT_WORK(struct work_struct *work, void (*function)(void *));
PREPARE_WORK(struct work_struct *work, void (*function)(void *));
INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
PREPARE_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
/*有 2 个函数来提交工作给一个工作队列:*/
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct delayed_work *work, unsigned long delay);
/*每个都添加work到给定的workqueue。如果使用 queue_delay_work, 则实际的工作至少要经过指定的 jiffies 才会被执行。
* 这些函数若返回 1 则工作被成功加入到队列; 若为0,则意味着这个 work 已经在队列中等待,不能再次加入
*/
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct delayed_work *work, unsigned long delay);
/*每个都添加work到给定的workqueue。如果使用 queue_delay_work, 则实际的工作至少要经过指定的 jiffies 才会被执行。
* 这些函数若返回 1 则工作被成功加入到队列; 若为0,则意味着这个 work 已经在队列中等待,不能再次加入
*/
创建4个/proc只读接口,它们分别是/proc/ jiqwq、/proc/ jiqwqdelay、/proc/ jitimer、/proc/ jiqtasklet,
这4个文件对应的read_proc函数分别是jiq_read_wq、jiq_read_wq_delayed、jiq_read_run_timer、jiq_read_tasklet。
/*
* the init/clean material
*/
这4个文件对应的read_proc函数分别是jiq_read_wq、jiq_read_wq_delayed、jiq_read_run_timer、jiq_read_tasklet。
/*
* the init/clean material
*/
static int jiq_init(void)
{
{
/* this line is in jiq_init() */
INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);//初始化一个工作jiq_work,其工作函数是jiq_print_wq,jiq_data是传递给该函数的参数。
//jiq_work是work_struct类型的变量
create_proc_read_entry("jiqwq", 0, NULL, jiq_read_wq, NULL);
create_proc_read_entry("jiqwqdelay", 0, NULL, jiq_read_wq_delayed, NULL);
create_proc_read_entry("jitimer", 0, NULL, jiq_read_run_timer, NULL);
create_proc_read_entry("jiqtasklet", 0, NULL, jiq_read_tasklet, NULL);
INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);//初始化一个工作jiq_work,其工作函数是jiq_print_wq,jiq_data是传递给该函数的参数。
//jiq_work是work_struct类型的变量
create_proc_read_entry("jiqwq", 0, NULL, jiq_read_wq, NULL);
create_proc_read_entry("jiqwqdelay", 0, NULL, jiq_read_wq_delayed, NULL);
create_proc_read_entry("jitimer", 0, NULL, jiq_read_run_timer, NULL);
create_proc_read_entry("jiqtasklet", 0, NULL, jiq_read_tasklet, NULL);
return 0; /* succeed */
}
}
首先来看jiq_read_wq函数的实现:
定义全局变量 jiq_data
static struct clientdata {
int len;
char *buf;
unsigned long jiffies;
long delay;
} jiq_data;
定义全局变量 jiq_data
static struct clientdata {
int len;
char *buf;
unsigned long jiffies;
long delay;
} jiq_data;
jiq_read_wq函数中使用的是共享工作队列,而不是进程专用的工作队列,共享工作队列更节约系统资源。该函数的休眠采用的是手工休眠。
所以没有queue_work函数
static int jiq_read_wq(char *buf, char **start, off_t offset,
int len, int *eof, void *data)
{
DEFINE_WAIT(wait);//使用DEFINE_WAIT宏定义一个等待队列入口wait
/*对jiq_data进行初始化*/
jiq_data.len = 0; /* nothing printed, yet */
jiq_data.buf = buf; /* print in this place */
jiq_data.jiffies = jiffies; /* initial time */
jiq_data.delay = 0;
prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);//将wait加入到等待队列jiq_wait中,并把进程状态设置为TASK_INTERRUPTIBLE
schedule_work(&jiq_work);//将jiq_work提交到系统提供的共享工作队列中
schedule();调用schedule函数,让出处理器,此后,进程就在等待队列jiq_wait中休眠
finish_wait(&jiq_wait, &wait);//休眠被唤醒后,调用finish_wait做一些清理工作
所以没有queue_work函数
static int jiq_read_wq(char *buf, char **start, off_t offset,
int len, int *eof, void *data)
{
DEFINE_WAIT(wait);//使用DEFINE_WAIT宏定义一个等待队列入口wait
/*对jiq_data进行初始化*/
jiq_data.len = 0; /* nothing printed, yet */
jiq_data.buf = buf; /* print in this place */
jiq_data.jiffies = jiffies; /* initial time */
jiq_data.delay = 0;
prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);//将wait加入到等待队列jiq_wait中,并把进程状态设置为TASK_INTERRUPTIBLE
schedule_work(&jiq_work);//将jiq_work提交到系统提供的共享工作队列中
schedule();调用schedule函数,让出处理器,此后,进程就在等待队列jiq_wait中休眠
finish_wait(&jiq_wait, &wait);//休眠被唤醒后,调用finish_wait做一些清理工作
*eof = 1;//将*eof设置为1,表明已经读到/proc/jiqwq的结束处
return jiq_data.len;
}
return jiq_data.len;
}
上文中:INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data)
jiq_print_wq函数:
工作函数jiq_print_wq的核心代码在jiq_print函数中,如果该函数返回0,则退出。
如果返回非0值,说明还需要再次调用该工作。如果jiq_print函数返回非0值,
根据data->delay的值是否为0,分别采用schedule_delayed_work和schedule_work,以延迟和非延迟的方式重新调度工作。
jiq_print_wq函数:
工作函数jiq_print_wq的核心代码在jiq_print函数中,如果该函数返回0,则退出。
如果返回非0值,说明还需要再次调用该工作。如果jiq_print函数返回非0值,
根据data->delay的值是否为0,分别采用schedule_delayed_work和schedule_work,以延迟和非延迟的方式重新调度工作。
/*
* Call jiq_print from a work queue
*/
static void jiq_print_wq(void *ptr)
{
struct clientdata *data = (struct clientdata *) ptr;
if (! jiq_print (ptr))
return;
if (data->delay)
schedule_delayed_work(&jiq_work, data->delay);
else
schedule_work(&jiq_work);
}
jiq_print函数代码:
/*
* Do the printing; return non-zero if the task should be rescheduled.
*/
static int jiq_print(void *ptr)
{
struct clientdata *data = ptr;
int len = data->len;
char *buf = data->buf;
unsigned long j = jiffies;
/*
* Do the printing; return non-zero if the task should be rescheduled.
*/
static int jiq_print(void *ptr)
{
struct clientdata *data = ptr;
int len = data->len;
char *buf = data->buf;
unsigned long j = jiffies;
if (len > LIMIT) { //如果打印的信息已经超过了LIMIT()的限制,则唤醒等待队列jiq_wait上的进程,并返回0,表示不需要再次调度该工作
wake_up_interruptible(&jiq_wait);
return 0;
}
wake_up_interruptible(&jiq_wait);
return 0;
}
if (len == 0)
len = sprintf(buf," time delta preempt pid cpu command\n");
else
len =0;
len = sprintf(buf," time delta preempt pid cpu command\n");
else
len =0;
/* intr_count is only exported since 1.3.5, but 1.99.4 is needed anyways */
len += sprintf(buf+len, "%9li %4li %3i %5i %3i %s\n",
j, j - data->jiffies,
preempt_count(), current->pid, smp_processor_id(),
current->comm);
/*更新jiq_data的信息*/
data->len += len;
data->buf += len;
data->jiffies = j;
return 1;//返回1,表明还需要再次调度该工作
}
len += sprintf(buf+len, "%9li %4li %3i %5i %3i %s\n",
j, j - data->jiffies,
preempt_count(), current->pid, smp_processor_id(),
current->comm);
/*更新jiq_data的信息*/
data->len += len;
data->buf += len;
data->jiffies = j;
return 1;//返回1,表明还需要再次调度该工作
}
- 学习ldd3--工作队列(第七章)
- 学习ldd3--tasklet(第七章)
- LDD3 工作队列
- 学习ldd3--计时、延迟操作(第七章)
- 学习ldd3--内核定时器(第七章)
- LDD3 第七章 timer
- [LDD3速记]_tasklet、工作队列、共享队列
- 学习ldd3--proc文件系统(第七章与第四章)
- 学习ldd3--ioctl(第六章)
- 学习ldd3--poll(第六章)
- 学习ldd3--llseek(第六章)
- ldd3 5 继续学习ldd3
- ldd3 第三章学习感想(1)
- LDD3学习笔记《三》第四章
- LDD3学习笔记《三》第四章
- 学习Ldd3--字符设备驱动(第三章)
- 学习ldd3--简单休眠(第六章)
- 学习ldd3--异步通知(第六章)
- 学习ldd3--proc文件系统(第七章与第四章)
- 基于LEACH的无线传感器网络路由算法概述
- 学习ldd3--tasklet(第七章)
- 台湾-海洋过程分析研究室--相關研究資源連結
- 【枚举】教主的花园
- 学习ldd3--工作队列(第七章)
- 重庆1辆石油液化气罐车坠崖 已排除爆炸可能-石油液化气罐车-坠崖-爆炸可能
- 学习ldd3--计时、延迟操作(第七章)
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 单元测试101:你测试你的javascript吗?
- Mediator 模式
- 星星
- 学习ldd3--内核定时器(第七章)
- 编译器与解释器的区别