中断管理
实验:tasklet
在 linux 内核中,tasklet 主要用于处理那些从硬件中断处理程序中分离出来的任务,并将这些任务的执行延迟到后续时间点。即 tasklet 不是立即执行,而是在中断处理完成后,当内核认为合适的时候执行。
tasklet 在执行时不会被其他 tasklet 打断,但可以被硬件中断打断。实际上,tasklet 是建立在软中断之上的,是 linux 软中断机制的一部分。
"-let" 后缀表示小型的意思,tasklet 就是“小任务”的意思。
我们看到代码清单 1,来学习 tasklet 的用法。代码虽然长,但都是之前的 FIFO 内核。
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/uaccess.h>
- #include <linux/init.h>
- #include <linux/miscdevice.h>
- #include <linux/device.h>
- #include <linux/slab.h>
- #include <linux/kfifo.h>
- #include <linux/wait.h>
- #include <linux/sched.h>
- #include <linux/cdev.h>
- #include <linux/poll.h>
- #include <linux/interrupt.h>
- #define DEMO_NAME "mydemo_dev"
- #define MYDEMO_FIFO_SIZE 64
- static dev_t dev;
- static struct cdev* demo_cdev;
- static struct class* mydemo_class;
- struct mydemo_device
- {
- char name[64];
- struct device* dev;
- wait_queue_head_t read_queue;
- wait_queue_head_t write_queue;
- struct kfifo mydemo_fifo;
- struct fasync_struct* fasync;
- struct mutex lock;
- };
- struct mydemo_private_data
- {
- struct mydemo_device* device;
- char name[64];
- struct tasklet_struct tasklet;
- };
- #define MYDEMO_MAX_DEVICES 8
- static struct mydemo_device* mydemo_device[MYDEMO_MAX_DEVICES];
- static void do_tasklet(unsigned long data)
- {
- struct mydemo_device* device = (struct mydemo_device*)data;
- dev_info(device->dev, "%s: trigger a tasklet\n", __func__);
- }
- static int demodrv_open(struct inode* inode, struct file* file)
- {
- unsigned int minor = iminor(inode);
- struct mydemo_private_data* data;
- struct mydemo_device* device = mydemo_device[minor];
- dev_info(device->dev, "%s: major=%d,minor=%d,device=%s\n", __func__,
- MAJOR(inode->i_rdev), MINOR(inode->i_rdev), device->name);
- data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- sprintf(data->name, "private_data_%d", minor);
- tasklet_init(&data->tasklet, do_tasklet, (unsigned long)device);
- data->device = device;
- file->private_data = data;
- return 0;
- }
- static int demodrv_release(struct inode* inode, struct file* file)
- {
- struct mydemo_private_data* data = file->private_data;
- tasklet_kill(&data->tasklet);
- kfree(data);
- return 0;
- }
- static ssize_t demodrv_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
- {
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- int actual_readed;
- int ret;
- if (kfifo_is_empty(&device->mydemo_fifo))
- {
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
- dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
- ret = wait_event_interruptible(device->read_queue, !kfifo_is_empty(&device->mydemo_fifo));
- if (ret)
- return ret;
- }
- mutex_lock(&device->lock);
- ret = kfifo_to_user(&device->mydemo_fifo, buf, count, &actual_readed);
- if (ret)
- return -EIO;
- tasklet_schedule(&data->tasklet);
- mutex_unlock(&device->lock);
- if (!kfifo_is_full(&device->mydemo_fifo))
- {
- wake_up_interruptible(&device->write_queue);
- kill_fasync(&device->fasync, SIGIO, POLL_OUT);
- }
- dev_info(device->dev, "%s:%s,pid=%d,actual_readed=%d,pos=%lld\n", __func__,
- device->name, current->pid, actual_readed, *ppos);
- return actual_readed;
- }
- static ssize_t demodrv_write(struct file* file, const char __user* buf, size_t count, loff_t* ppos)
- {
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- unsigned int actual_write;
- int ret;
- if (kfifo_is_full(&device->mydemo_fifo))
- {
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
- dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
- ret = wait_event_interruptible(device->write_queue, !kfifo_is_full(&device->mydemo_fifo));
- if (ret)
- return ret;
- }
- mutex_lock(&device->lock);
- ret = kfifo_from_user(&device->mydemo_fifo, buf, count, &actual_write);
- if (ret)
- return -EIO;
- mutex_unlock(&device->lock);
- if (!kfifo_is_empty(&device->mydemo_fifo))
- {
- wake_up_interruptible(&device->read_queue);
- kill_fasync(&device->fasync, SIGIO, POLL_IN);
- printk("%s kill fasync\n", __func__);
- }
- dev_info(device->dev, "%s:%s,pid=%d,actual_write=%d,pos=%lld\n", __func__,
- device->name, current->pid, actual_write, *ppos);
- return actual_write;
- }
- static unsigned int demodrv_poll(struct file* file, poll_table* wait)
- {
- int mask = 0;
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- mutex_lock(&device->lock);
- poll_wait(file, &device->read_queue, wait);
- poll_wait(file, &device->write_queue, wait);
- if (!kfifo_is_empty(&device->mydemo_fifo))
- mask |= POLLIN | POLLRDNORM;
- if (!kfifo_is_full(&device->mydemo_fifo))
- mask |= POLLOUT | POLLWRNORM;
- mutex_unlock(&device->lock);
- return mask;
- }
- static int demodrv_fasync(int fd, struct file* file, int on)
- {
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- int ret;
- mutex_lock(&device->lock);
- ret = fasync_helper(fd, file, on, &device->fasync);
- mutex_unlock(&device->lock);
- return ret;
- }
- static const struct file_operations demodrv_fops = {
- .owner = THIS_MODULE,
- .open = demodrv_open,
- .release = demodrv_release,
- .read = demodrv_read,
- .write = demodrv_write,
- .poll = demodrv_poll,
- .fasync = demodrv_fasync,
- };
- static int __init simple_char_init(void)
- {
- int ret;
- int i;
- struct mydemo_device* device;
- ret = alloc_chrdev_region(&dev, 0, MYDEMO_MAX_DEVICES, DEMO_NAME);
- if (ret)
- {
- printk("failed to allocate char device region");
- return ret;
- }
- demo_cdev = cdev_alloc();
- if (!demo_cdev)
- {
- printk("cdev_alloc failed\n");
- goto unregister_chrdev;
- }
- cdev_init(demo_cdev, &demodrv_fops);
- ret = cdev_add(demo_cdev, dev, MYDEMO_MAX_DEVICES);
- if (ret)
- {
- printk("cdev_add failed\n");
- goto cdev_fail;
- }
- mydemo_class = class_create(THIS_MODULE, "my_class");
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- {
- device = kzalloc(sizeof(struct mydemo_device), GFP_KERNEL);
- if (!device) {
- ret = -ENOMEM;
- goto free_device;
- }
- sprintf(device->name, "%s%d", DEMO_NAME, i);
- mutex_init(&device->lock);
- device->dev = device_create(mydemo_class, NULL, MKDEV(dev, i), NULL, "mydemo:%d:%d", MAJOR(dev), i);
- dev_info(device->dev, "create device: %d:%d\n", MAJOR(dev), MINOR(i));
- mydemo_device[i] = device;
- init_waitqueue_head(&device->read_queue);
- init_waitqueue_head(&device->write_queue);
- ret = kfifo_alloc(&device->mydemo_fifo,
- MYDEMO_FIFO_SIZE,
- GFP_KERNEL);
- if (ret) {
- ret = -ENOMEM;
- goto free_kfifo;
- }
- printk("mydemo_fifo=%p\n", &device->mydemo_fifo);
- }
- printk("succeeded register char device: %s\n", DEMO_NAME);
- return 0;
- free_kfifo:
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- if (&device->mydemo_fifo)
- kfifo_free(&device->mydemo_fifo);
- free_device:
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- if (mydemo_device[i])
- kfree(mydemo_device[i]);
- cdev_fail:
- cdev_del(demo_cdev);
- unregister_chrdev:
- unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
- return ret;
- }
- static void __exit simple_char_exit(void)
- {
- int i;
- printk("removing device\n");
- if (demo_cdev)
- cdev_del(demo_cdev);
- unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- {
- if (mydemo_device[i])
- {
- device_destroy(mydemo_class, MKDEV(dev, i));
- kfree(mydemo_device[i]);
- }
- }
- class_destroy(mydemo_class);
- }
- module_init(simple_char_init);
- module_exit(simple_char_exit);
- MODULE_AUTHOR("rlk");
- MODULE_LICENSE("GPL v2");
见代码清单 1 的第 63 行,进行初始化 tasklet。tasklet_init 定义为
- void tasklet_init(struct tasklet_struct* t,
- void (*func)(unsigned long), unsigned long data);
其中,t 是指向 tasklet_struct 结构的指针;func 是 tasklet 的处理函数,当 tasklet 被执行时会被调用;data 是传递给 tasklet 处理函数的数据,每次 tasklet 被执行调度,都会传递这个 data 值。
见代码清单 1 的第 103 行,安排 tasklet 执行。tasklet_schedule 将指定的 tasklet 加入到内核的调度队列。tasklet_schedule 的定义为:
- void tasklet_schedule(struct tasklet_struct* t);
代码清单 1 在设备被读取的时候,会安排 tasklet 执行。这边的 tasklet 执行的任务就是打印,见第 43 行的 do_tasklet 函数。
我们做以下实验:
- root@root:/# insmod mydemo.ko
- root@root:/# ls /sys/class/my_class
- root@root:/# mknod /dev/mydemo0 c 236 0
- root@root:/# echo "Hello" > /dev/mydemo0
- root@root:/# cat /dev/mydemo0
- root@root:/# dmesg
在内核日志里,可以看到读取设备的时候,do_tasklet 函数被成功调用。
实验:工作队列
工作队列和 tasklet 一样,也是用于处理异步任务的机制。但 tasklet 在软中断上下文中执行,不允许睡眠;而工作队列在进程上下文中执行,即可以睡眠,更适合执行需要阻塞的操作。
如代码清单 2 所示,工作队列在使用方面和 tasklet 很类似。
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/uaccess.h>
- #include <linux/init.h>
- #include <linux/miscdevice.h>
- #include <linux/device.h>
- #include <linux/slab.h>
- #include <linux/kfifo.h>
- #include <linux/wait.h>
- #include <linux/sched.h>
- #include <linux/cdev.h>
- #include <linux/poll.h>
- #include <linux/interrupt.h>
- #include <linux/workqueue.h>
- #define DEMO_NAME "mydemo_dev"
- #define MYDEMO_FIFO_SIZE 64
- static dev_t dev;
- static struct cdev* demo_cdev;
- static struct class* mydemo_class;
- struct mydemo_device
- {
- char name[64];
- struct device* dev;
- wait_queue_head_t read_queue;
- wait_queue_head_t write_queue;
- struct kfifo mydemo_fifo;
- struct fasync_struct* fasync;
- struct mutex lock;
- };
- struct mydemo_private_data
- {
- struct mydemo_device* device;
- char name[64];
- struct tasklet_struct tasklet;
- struct work_struct my_work;
- };
- #define MYDEMO_MAX_DEVICES 8
- static struct mydemo_device* mydemo_device[MYDEMO_MAX_DEVICES];
- static void do_tasklet(unsigned long data)
- {
- struct mydemo_device* device = (struct mydemo_device*)data;
- dev_info(device->dev, "%s: trigger a tasklet\n", __func__);
- }
- static void do_work(struct work_struct* work)
- {
- struct mydemo_private_data* data;
- struct mydemo_device* device;
- data = container_of(work, struct mydemo_private_data, my_work);
- device = data->device;
- dev_info(device->dev, "%s:trigger a work\n", __func__);
- }
- static int demodrv_open(struct inode* inode, struct file* file)
- {
- unsigned int minor = iminor(inode);
- struct mydemo_private_data* data;
- struct mydemo_device* device = mydemo_device[minor];
- dev_info(device->dev, "%s: major=%d,minor=%d,device=%s\n", __func__,
- MAJOR(inode->i_rdev), MINOR(inode->i_rdev), device->name);
- data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- sprintf(data->name, "private_data_%d", minor);
- tasklet_init(&data->tasklet, do_tasklet, (unsigned long)device);
- INIT_WORK(&data->my_work, do_work);
- data->device = device;
- file->private_data = data;
- return 0;
- }
- static int demodrv_release(struct inode* inode, struct file* file)
- {
- struct mydemo_private_data* data = file->private_data;
- tasklet_kill(&data->tasklet);
- kfree(data);
- return 0;
- }
- static ssize_t demodrv_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
- {
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- int actual_readed;
- int ret;
- if (kfifo_is_empty(&device->mydemo_fifo))
- {
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
- dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
- ret = wait_event_interruptible(device->read_queue, !kfifo_is_empty(&device->mydemo_fifo));
- if (ret)
- return ret;
- }
- mutex_lock(&device->lock);
- ret = kfifo_to_user(&device->mydemo_fifo, buf, count, &actual_readed);
- if (ret)
- return -EIO;
- schedule_work(&data->my_work);
- mutex_unlock(&device->lock);
- if (!kfifo_is_full(&device->mydemo_fifo))
- {
- wake_up_interruptible(&device->write_queue);
- kill_fasync(&device->fasync, SIGIO, POLL_OUT);
- }
- dev_info(device->dev, "%s:%s,pid=%d,actual_readed=%d,pos=%lld\n", __func__,
- device->name, current->pid, actual_readed, *ppos);
- return actual_readed;
- }
- static ssize_t demodrv_write(struct file* file, const char __user* buf, size_t count, loff_t* ppos)
- {
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- unsigned int actual_write;
- int ret;
- if (kfifo_is_full(&device->mydemo_fifo))
- {
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
- dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
- ret = wait_event_interruptible(device->write_queue, !kfifo_is_full(&device->mydemo_fifo));
- if (ret)
- return ret;
- }
- mutex_lock(&device->lock);
- ret = kfifo_from_user(&device->mydemo_fifo, buf, count, &actual_write);
- if (ret)
- return -EIO;
- mutex_unlock(&device->lock);
- if (!kfifo_is_empty(&device->mydemo_fifo))
- {
- wake_up_interruptible(&device->read_queue);
- kill_fasync(&device->fasync, SIGIO, POLL_IN);
- printk("%s kill fasync\n", __func__);
- }
- dev_info(device->dev, "%s:%s,pid=%d,actual_write=%d,pos=%lld\n", __func__,
- device->name, current->pid, actual_write, *ppos);
- return actual_write;
- }
- static unsigned int demodrv_poll(struct file* file, poll_table* wait)
- {
- int mask = 0;
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- mutex_lock(&device->lock);
- poll_wait(file, &device->read_queue, wait);
- poll_wait(file, &device->write_queue, wait);
- if (!kfifo_is_empty(&device->mydemo_fifo))
- mask |= POLLIN | POLLRDNORM;
- if (!kfifo_is_full(&device->mydemo_fifo))
- mask |= POLLOUT | POLLWRNORM;
- mutex_unlock(&device->lock);
- return mask;
- }
- static int demodrv_fasync(int fd, struct file* file, int on)
- {
- struct mydemo_private_data* data = file->private_data;
- struct mydemo_device* device = data->device;
- int ret;
- mutex_lock(&device->lock);
- ret = fasync_helper(fd, file, on, &device->fasync);
- mutex_unlock(&device->lock);
- return ret;
- }
- static const struct file_operations demodrv_fops = {
- .owner = THIS_MODULE,
- .open = demodrv_open,
- .release = demodrv_release,
- .read = demodrv_read,
- .write = demodrv_write,
- .poll = demodrv_poll,
- .fasync = demodrv_fasync,
- };
- static int __init simple_char_init(void)
- {
- int ret;
- int i;
- struct mydemo_device* device;
- ret = alloc_chrdev_region(&dev, 0, MYDEMO_MAX_DEVICES, DEMO_NAME);
- if (ret)
- {
- printk("failed to allocate char device region");
- return ret;
- }
- demo_cdev = cdev_alloc();
- if (!demo_cdev)
- {
- printk("cdev_alloc failed\n");
- goto unregister_chrdev;
- }
- cdev_init(demo_cdev, &demodrv_fops);
- ret = cdev_add(demo_cdev, dev, MYDEMO_MAX_DEVICES);
- if (ret)
- {
- printk("cdev_add failed\n");
- goto cdev_fail;
- }
- mydemo_class = class_create(THIS_MODULE, "my_class");
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- {
- device = kzalloc(sizeof(struct mydemo_device), GFP_KERNEL);
- if (!device) {
- ret = -ENOMEM;
- goto free_device;
- }
- sprintf(device->name, "%s%d", DEMO_NAME, i);
- mutex_init(&device->lock);
- device->dev = device_create(mydemo_class, NULL, MKDEV(dev, i), NULL, "mydemo:%d:%d", MAJOR(dev), i);
- dev_info(device->dev, "create device: %d:%d\n", MAJOR(dev), MINOR(i));
- mydemo_device[i] = device;
- init_waitqueue_head(&device->read_queue);
- init_waitqueue_head(&device->write_queue);
- ret = kfifo_alloc(&device->mydemo_fifo,
- MYDEMO_FIFO_SIZE,
- GFP_KERNEL);
- if (ret) {
- ret = -ENOMEM;
- goto free_kfifo;
- }
- printk("mydemo_fifo=%p\n", &device->mydemo_fifo);
- }
- printk("succeeded register char device: %s\n", DEMO_NAME);
- return 0;
- free_kfifo:
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- if (&device->mydemo_fifo)
- kfifo_free(&device->mydemo_fifo);
- free_device:
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- if (mydemo_device[i])
- kfree(mydemo_device[i]);
- cdev_fail:
- cdev_del(demo_cdev);
- unregister_chrdev:
- unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
- return ret;
- }
- static void __exit simple_char_exit(void)
- {
- int i;
- printk("removing device\n");
- if (demo_cdev)
- cdev_del(demo_cdev);
- unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
- for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
- {
- if (mydemo_device[i])
- {
- device_destroy(mydemo_class, MKDEV(dev, i));
- kfree(mydemo_device[i]);
- }
- }
- class_destroy(mydemo_class);
- }
- module_init(simple_char_init);
- module_exit(simple_char_exit);
- MODULE_AUTHOR("rlk");
- MODULE_LICENSE("GPL v2");
使用 INIT_WORK 宏初始化工作队列,它将工作队列和指定的处理函数关联。宏定义如下:
- INIT_WORK(_work, _func)
其中,_work 是指向 work_struct 的指针;_func 是工作队列被执行时调用的函数。
schedule_work 用于排队工作项,以便调度异步执行想要的函数。其定义如下:
- bool schedule_work(struct work_struct* work);
这个实验实现的功能和上一个实验是一样的,验证手段也是一样的。
实验:定时器和内核线程
内核线程我们之前已经遇到过,使用 kthread_run 创建。本节实验我们通过代码清单 3 来了解一下 linux 内核中的定时器和内核线程。
定时器
在 linux 内核中,定时器用于在指定时间后执行一个函数。可以使用 DEFINE_TIMER 宏定义定时器。
- DEFINE_TIMER(_name, _function)
_name 是要创建的定时器变量的名称;_function 是定时器到期时被调用的函数。
add_timer 函数用于添加一个新的定时器;del_timer 函数用来删除定时器。
mod_timer 函数用于设置定时器的到期时间。如果该定时器尚未激活,mod_timer 会自动激活它;如果定时器已经激活并且正在计时,mod_timer 会更新它的到期时间。
- int mod_timer(struct timer_list* timer, unsigned long expires);
timer 是已经初始化的 timer_list 指针。expires 指定新的到期时间,通常是当前时间(jiffies 值)加上一个延迟时间。
等待队列
等待队列用于管理多个进程或线程之间的睡眠和唤醒。我们在之前已经了解过 wait_event_interruptible 和 wake_up_interruptible 的使用。这边再介绍一下 prepare_to_wait、schedule 和 finish_wait 的组合使用方式。
使用 prepare_to_wait 可以将指定进程添加到等待队列,并设置其在等待过程中的任务状态。
- void prepare_to_wait(struct wait_queue_head* wq_head, struct wait_queue_entry* wq_entry, int state);
wq_head 指向等待队列头的指针;wq_entry 指向进程的等待队列条目。可以使用 DEFINE_WAIT 宏创建当前执行进程的等待队列条目;state 设置等待过程中进程应处于的状态,常见的状态有 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE。
可以使用 DEFINE_WAIT 宏创建当前执行进程的等待队列条目。
prepare_to_wait 只是信息管理,不会执行进程调度。进程调用使用 schedule 函数,它会将当前进程从运行队列中移出,允许内核选择另一个进程来执行。
当进程不再需要等待特定条件时,使用 finish_wait 函数把指定的进程从等待队列中移除,并执行必要的清理操作。
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/kthread.h>
- #include <linux/freezer.h>
- #include <linux/mutex.h>
- #include <linux/delay.h>
- static void my_timefunc(struct timer_list* unused);
- static DEFINE_TIMER(my_timer, my_timefunc);
- static atomic_t flags;
- wait_queue_head_t wait_head;
- static void my_timefunc(struct timer_list* unused)
- {
- atomic_set(&flags, 1);
- wake_up_interruptible(&wait_head);
- mod_timer(&my_timer, jiffies + msecs_to_jiffies(2000));
- }
- static void my_try_to_sleep(void)
- {
- DEFINE_WAIT(wait);
- if (freezing(current) || kthread_should_stop())
- return;
- prepare_to_wait(&wait_head, &wait, TASK_INTERRUPTIBLE);
- if (!atomic_read(&flags))
- schedule();
- finish_wait(&wait_head, &wait);
- }
- static void show_reg(void)
- {
- unsigned int spsr, sp;
- struct task_struct* task = current;
- asm("mrs %0, spsr_el1" : "=r" (spsr) : : "cc");
- asm("mov %0, sp" : "=r" (sp) : : "cc");
- printk("%s: %s, pid:%d\n", __func__, task->comm, task->pid);
- printk("cpsr:0x%x, sp:0x%x\n", spsr, sp);
- }
- static int my_thread(void* nothing)
- {
- set_freezable();
- set_user_nice(current, 0);
- while (!kthread_should_stop())
- {
- my_try_to_sleep();
- atomic_set(&flags, 0);
- show_reg();
- }
- return 0;
- }
- static struct task_struct* thread;
- static int __init my_init(void)
- {
- printk("ben: my lockdep module init\n");
- thread = kthread_run(my_thread, NULL, "ktest");
- my_timer.expires = jiffies + msecs_to_jiffies(500);
- add_timer(&my_timer);
- init_waitqueue_head(&wait_head);
- return 0;
- }
- static void __exit my_exit(void)
- {
- printk("goodbye\n");
- kthread_stop(thread);
- }
- MODULE_LICENSE("GPL");
- module_init(my_init);
- module_exit(my_exit);
现在我们看代码清单 3 的功能,就会很清楚。首先在模块加载时,会创建一个内核进程,执行想要的功能。具体的功能是定时打印寄存器信息,见 show_reg 函数。实现定时的机制是:通过原子变量 flags 标识是否到时间打印信息了,如果没到,则线程进行调度睡眠;定时器到时时,设置标识原子变量 flags,唤醒等待中的线程。
为什么不直接在定时器函数中执行具体的功能?因为定时器函数是在中断上下文中执行的,需要尽可能快的返回。