本实例主要测试的是:内核中如何实现周期性的任务执行。
如果驱动程序中需要执行周期性的动作,可使用以下三种方式之一。
基本思路为:创建work,让创建的work调度到linux系统内核queue还是自己创建的queue。
默认的工作队列是把创建的任务提交给特定的内核线程来执行,这个特定的内核线程是:event/n,n是处理器的编号,单处理器中n为0,
即为event/0这样一个内核线程。这个event的workqueue的实现是在 Init_workqueues()中实现的,可以参看workqueue.c。
*/
/*有俩种work类型
#define DECLARE_WORK(n, f) struct work_struct n = __WORK_INITIALIZER(n, f)
#define DECLARE_DELAYED_WORK(n, f) struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f)
创建工作队列的方法如下:
#define create_workqueue(name) __create_workqueue((name), 0, 0, 0)
#define create_rt_workqueue(name) __create_workqueue((name), 0, 0, 1)
#define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1, 0)
#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0, 0)
下面是在特定的workqueue上面对work的调度,
extern int queue_work(struct workqueue_struct *wq, struct work_struct *work);
extern int queue_work_on(int cpu, struct workqueue_struct *wq,struct work_struct *work);
extern int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *work, unsigned long delay);
extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,struct delayed_work *work, unsigned long delay);
下面是在默认的workqueue即event上面对work进行调度。
extern int schedule_work(struct work_struct *work);
extern int schedule_work_on(int cpu, struct work_struct *work);
extern int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
extern int schedule_delayed_work_on(int cpu, struct delayed_work *work,unsigned long delay);
对应,可以从其中可以看出,有俩种不同的 work结构体,(struct work_struct)和(struct delayed_work)。
都是针对work的调度。
extern void destroy_workqueue(struct workqueue_struct *wq);
*/
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
#include <linux/workqueue.h> /* We scheduale tasks here */
#include <linux/sched.h> /* We need to put ourselves to sleep and wake up later */
#include <linux/init.h> /* For __init and __exit */
#include <linux/interrupt.h> /* For irqreturn_t */
#include <asm/delay.h>
#include <linux/delay.h>
struct proc_dir_entry *Our_Proc_File;
#define PROC_ENTRY_FILENAME "testsched"
#define MY_WORK_QUEUE_NAME "WQsched.c"
/*有俩种work类型
#define DECLARE_WORK(n, f) struct work_struct n = __WORK_INITIALIZER(n, f)
#define DECLARE_DELAYED_WORK(n, f) struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f)
创建工作队列的方法如下:
#define create_workqueue(name) __create_workqueue((name), 0, 0, 0)
#define create_rt_workqueue(name) __create_workqueue((name), 0, 0, 1)
#define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1, 0)
#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0, 0)
extern int queue_work(struct workqueue_struct *wq, struct work_struct *work);
extern int queue_work_on(int cpu, struct workqueue_struct *wq,struct work_struct *work);
extern int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *work, unsigned long delay);
extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,struct delayed_work *work, unsigned long delay);
与
extern int schedule_work(struct work_struct *work);
extern int schedule_work_on(int cpu, struct work_struct *work);
extern int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
extern int schedule_delayed_work_on(int cpu, struct delayed_work *work,unsigned long delay);
对应,可以从其中可以看出,有俩种不同的 work结构体,(struct work_struct)和(struct delayed_work)。
都是针对work的调度。
extern void destroy_workqueue(struct workqueue_struct *wq);
*/
/*
本实例主要测试的是:内核中如何实现周期性的任务执行。
如果驱动程序中需要执行周期性的动作,可使用以下三种方式之一。
基本思路为:创建work,让创建的work调度到linux系统内核queue还是自己创建的queue。
*/
static int TimerIntrpt = 0;
#define IS_DELAY_WORK 3
struct work_struct led_event;
static void led_event_work_func(struct work_struct **work);
void init_work()
{
INIT_WORK(&led_event, led_event_work_func);
schedule_work(&led_event);
}
void led_event_work_func(struct work_struct **work)
{
TimerIntrpt++;
// if(TimerIntrpt>2000)
if(0)
{
// cancel_delayed_work(&led_event);
printk("cancel_delayed_work\n");
}
else
{
//printk("TimerIntrpt:%d\n",TimerIntrpt);
msleep(20);
schedule_work(&led_event);
}
}
ssize_t
procfile_read(char *buffer,char **buffer_location,off_t offset, int buffer_length, int *eof, void *data)
{
int len; /* The number of bytes actually used */
static char my_buffer[80];
len = sprintf(my_buffer, "Timer called %d times so far\n", TimerIntrpt);
strcpy(buffer,my_buffer);
return len;
}
static struct workqueue_struct *led_wq;
static void led_work_func(struct work_struct *);
static DECLARE_WORK(led_task, led_work_func);
static void led_work_func (struct work_struct *unused)
{
/*
* Increment the counter
*/
TimerIntrpt++;
if(TimerIntrpt>2000)
{
// cancel_work_sync(&led_task);
printk("cancel_delayed_work\n");
}
else
{
printk("TimerIntrpt:%d\n",TimerIntrpt);
queue_work(led_wq, &led_task);
}
}
static int init_start_task(void)
{
led_wq = create_workqueue("led_wq");
queue_work(led_wq, &led_task);
return 0;
}
static void delayed_led_work_func(struct work_struct *);
static DECLARE_DELAYED_WORK(delayed_led_task, delayed_led_work_func);
static void delayed_led_work_func (struct work_struct *unused)
{
/*
* Increment the counter
*/
TimerIntrpt++;
if(TimerIntrpt>20)
{
cancel_delayed_work(&delayed_led_task);
printk("cancel_delayed_work\n");
}
else
{
printk("TimerIntrpt:%d\n",TimerIntrpt);
queue_delayed_work(led_wq, &delayed_led_task, 250);
}
}
static int init_delayed_start_task(void)
{
//led_wq = create_singlethread_workqueue("led_wq");
led_wq = create_workqueue("led_wq");
queue_delayed_work(led_wq, &delayed_led_task, 0);
return 0;
}
/*
* Initialize the module ? register the proc file
*/
int __init init_module()
{
/*
* Create our /proc file
*/
Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL);
if (Our_Proc_File == NULL)
{
remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",PROC_ENTRY_FILENAME);
return ENOMEM;
}
Our_Proc_File->read_proc = procfile_read;
printk(KERN_INFO "/proc/%s created\n", PROC_ENTRY_FILENAME);
#if IS_DELAY_WORK==1
init_delayed_start_task();
#elif (IS_DELAY_WORK==2)
init_start_task();
#else
init_work();
#endif
return 0;
}
/*
* Cleanup
*/
void __exit cleanup_module()
{
/*
* Unregister our /proc file
*/
remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROC_ENTRY_FILENAME);
#if (IS_DELAY_WORK==1)
cancel_delayed_work(&delayed_led_task);
flush_workqueue(led_wq); /* wait till all "old ones" finished */
destroy_workqueue(led_wq);
#elif(IS_DELAY_WORK==2)
cancel_delayed_work(&led_task);
flush_workqueue(led_wq); /* wait till all "old ones" finished */
destroy_workqueue(led_wq);
#else
// cancel_delayed_work(&led_event);
#endif
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("suiyuan from test");
MODULE_DESCRIPTION("shech driver for testing ");