中断管理

实验:tasklet

在 linux 内核中,tasklet 主要用于处理那些从硬件中断处理程序中分离出来的任务,并将这些任务的执行延迟到后续时间点。即 tasklet 不是立即执行,而是在中断处理完成后,当内核认为合适的时候执行。

tasklet 在执行时不会被其他 tasklet 打断,但可以被硬件中断打断。实际上,tasklet 是建立在软中断之上的,是 linux 软中断机制的一部分。

"-let" 后缀表示小型的意思,tasklet 就是“小任务”的意思。

我们看到代码清单 1,来学习 tasklet 的用法。代码虽然长,但都是之前的 FIFO 内核。

代码清单 1 tasklet
  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/uaccess.h>
  4. #include <linux/init.h>
  5. #include <linux/miscdevice.h>
  6. #include <linux/device.h>
  7. #include <linux/slab.h>
  8. #include <linux/kfifo.h>
  9. #include <linux/wait.h>
  10. #include <linux/sched.h>
  11. #include <linux/cdev.h>
  12. #include <linux/poll.h>
  13. #include <linux/interrupt.h>
  14.  
  15. #define DEMO_NAME "mydemo_dev"
  16. #define MYDEMO_FIFO_SIZE 64
  17.  
  18. static dev_t dev;
  19. static struct cdev* demo_cdev;
  20. static struct class* mydemo_class;
  21.  
  22. struct mydemo_device
  23. {
  24.     char name[64];
  25.     struct device* dev;
  26.     wait_queue_head_t read_queue;
  27.     wait_queue_head_t write_queue;
  28.     struct kfifo mydemo_fifo;
  29.     struct fasync_struct* fasync;
  30.     struct mutex lock;
  31. };
  32.  
  33. struct mydemo_private_data
  34. {
  35.     struct mydemo_device* device;
  36.     char name[64];
  37.     struct tasklet_struct tasklet;
  38. };
  39.  
  40. #define MYDEMO_MAX_DEVICES 8
  41. static struct mydemo_device* mydemo_device[MYDEMO_MAX_DEVICES];
  42.  
  43. static void do_tasklet(unsigned long data)
  44. {
  45.     struct mydemo_device* device = (struct mydemo_device*)data;
  46.     dev_info(device->dev, "%s: trigger a tasklet\n", __func__);
  47. }
  48.  
  49. static int demodrv_open(struct inode* inode, struct file* file)
  50. {
  51.     unsigned int minor = iminor(inode);
  52.     struct mydemo_private_data* data;
  53.     struct mydemo_device* device = mydemo_device[minor];
  54.  
  55.     dev_info(device->dev, "%s: major=%d,minor=%d,device=%s\n", __func__,
  56.         MAJOR(inode->i_rdev), MINOR(inode->i_rdev), device->name);
  57.  
  58.     data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);
  59.     if (!data)
  60.         return -ENOMEM;
  61.  
  62.     sprintf(data->name, "private_data_%d", minor);
  63.     tasklet_init(&data->tasklet, do_tasklet, (unsigned long)device);
  64.  
  65.     data->device = device;
  66.     file->private_data = data;
  67.  
  68.     return 0;
  69. }
  70.  
  71. static int demodrv_release(struct inode* inode, struct file* file)
  72. {
  73.     struct mydemo_private_data* data = file->private_data;
  74.  
  75.     tasklet_kill(&data->tasklet);
  76.     kfree(data);
  77.  
  78.     return 0;
  79. }
  80.  
  81. static ssize_t demodrv_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
  82. {
  83.     struct mydemo_private_data* data = file->private_data;
  84.     struct mydemo_device* device = data->device;
  85.     int actual_readed;
  86.     int ret;
  87.  
  88.     if (kfifo_is_empty(&device->mydemo_fifo))
  89.     {
  90.         if (file->f_flags & O_NONBLOCK)
  91.             return -EAGAIN;
  92.  
  93.         dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
  94.         ret = wait_event_interruptible(device->read_queue, !kfifo_is_empty(&device->mydemo_fifo));
  95.         if (ret)
  96.             return ret;
  97.     }
  98.  
  99.     mutex_lock(&device->lock);
  100.     ret = kfifo_to_user(&device->mydemo_fifo, buf, count, &actual_readed);
  101.     if (ret)
  102.         return -EIO;
  103.     tasklet_schedule(&data->tasklet);
  104.     mutex_unlock(&device->lock);
  105.  
  106.     if (!kfifo_is_full(&device->mydemo_fifo))
  107.     {
  108.         wake_up_interruptible(&device->write_queue);
  109.         kill_fasync(&device->fasync, SIGIO, POLL_OUT);
  110.     }
  111.  
  112.     dev_info(device->dev, "%s:%s,pid=%d,actual_readed=%d,pos=%lld\n", __func__,
  113.         device->name, current->pid, actual_readed, *ppos);
  114.     return actual_readed;
  115. }
  116.  
  117. static ssize_t demodrv_write(struct file* file, const char __user* buf, size_t count, loff_t* ppos)
  118. {
  119.     struct mydemo_private_data* data = file->private_data;
  120.     struct mydemo_device* device = data->device;
  121.  
  122.     unsigned int actual_write;
  123.     int ret;
  124.  
  125.     if (kfifo_is_full(&device->mydemo_fifo))
  126.     {
  127.         if (file->f_flags & O_NONBLOCK)
  128.             return -EAGAIN;
  129.  
  130.         dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
  131.         ret = wait_event_interruptible(device->write_queue, !kfifo_is_full(&device->mydemo_fifo));
  132.         if (ret)
  133.             return ret;
  134.     }
  135.  
  136.     mutex_lock(&device->lock);
  137.     ret = kfifo_from_user(&device->mydemo_fifo, buf, count, &actual_write);
  138.     if (ret)
  139.         return -EIO;
  140.     mutex_unlock(&device->lock);
  141.  
  142.     if (!kfifo_is_empty(&device->mydemo_fifo))
  143.     {
  144.         wake_up_interruptible(&device->read_queue);
  145.         kill_fasync(&device->fasync, SIGIO, POLL_IN);
  146.         printk("%s kill fasync\n", __func__);
  147.     }
  148.  
  149.     dev_info(device->dev, "%s:%s,pid=%d,actual_write=%d,pos=%lld\n", __func__,
  150.         device->name, current->pid, actual_write, *ppos);
  151.  
  152.     return actual_write;
  153. }
  154.  
  155. static unsigned int demodrv_poll(struct file* file, poll_table* wait)
  156. {
  157.     int mask = 0;
  158.     struct mydemo_private_data* data = file->private_data;
  159.     struct mydemo_device* device = data->device;
  160.  
  161.     mutex_lock(&device->lock);
  162.     poll_wait(file, &device->read_queue, wait);
  163.     poll_wait(file, &device->write_queue, wait);
  164.  
  165.     if (!kfifo_is_empty(&device->mydemo_fifo))
  166.         mask |= POLLIN | POLLRDNORM;
  167.     if (!kfifo_is_full(&device->mydemo_fifo))
  168.         mask |= POLLOUT | POLLWRNORM;
  169.     mutex_unlock(&device->lock);
  170.  
  171.     return mask;
  172. }
  173.  
  174. static int demodrv_fasync(int fd, struct file* file, int on)
  175. {
  176.     struct mydemo_private_data* data = file->private_data;
  177.     struct mydemo_device* device = data->device;
  178.     int ret;
  179.  
  180.     mutex_lock(&device->lock);
  181.     ret = fasync_helper(fd, file, on, &device->fasync);
  182.     mutex_unlock(&device->lock);
  183.  
  184.     return ret;
  185. }
  186.  
  187. static const struct file_operations demodrv_fops = {
  188.     .owner = THIS_MODULE,
  189.     .open = demodrv_open,
  190.     .release = demodrv_release,
  191.     .read = demodrv_read,
  192.     .write = demodrv_write,
  193.     .poll = demodrv_poll,
  194.     .fasync = demodrv_fasync,
  195. };
  196.  
  197. static int __init simple_char_init(void)
  198. {
  199.     int ret;
  200.     int i;
  201.     struct mydemo_device* device;
  202.  
  203.     ret = alloc_chrdev_region(&dev, 0, MYDEMO_MAX_DEVICES, DEMO_NAME);
  204.     if (ret)
  205.     {
  206.         printk("failed to allocate char device region");
  207.         return ret;
  208.     }
  209.  
  210.     demo_cdev = cdev_alloc();
  211.     if (!demo_cdev)
  212.     {
  213.         printk("cdev_alloc failed\n");
  214.         goto unregister_chrdev;
  215.     }
  216.  
  217.     cdev_init(demo_cdev, &demodrv_fops);
  218.  
  219.     ret = cdev_add(demo_cdev, dev, MYDEMO_MAX_DEVICES);
  220.     if (ret)
  221.     {
  222.         printk("cdev_add failed\n");
  223.         goto cdev_fail;
  224.     }
  225.  
  226.     mydemo_class = class_create(THIS_MODULE, "my_class");
  227.  
  228.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  229.     {
  230.         device = kzalloc(sizeof(struct mydemo_device), GFP_KERNEL);
  231.         if (!device) {
  232.             ret = -ENOMEM;
  233.             goto free_device;
  234.         }
  235.  
  236.         sprintf(device->name, "%s%d", DEMO_NAME, i);
  237.         mutex_init(&device->lock);
  238.  
  239.         device->dev = device_create(mydemo_class, NULL, MKDEV(dev, i), NULL, "mydemo:%d:%d", MAJOR(dev), i);
  240.         dev_info(device->dev, "create device: %d:%d\n", MAJOR(dev), MINOR(i));
  241.         mydemo_device[i] = device;
  242.         init_waitqueue_head(&device->read_queue);
  243.         init_waitqueue_head(&device->write_queue);
  244.  
  245.         ret = kfifo_alloc(&device->mydemo_fifo,
  246.             MYDEMO_FIFO_SIZE,
  247.             GFP_KERNEL);
  248.         if (ret) {
  249.             ret = -ENOMEM;
  250.             goto free_kfifo;
  251.         }
  252.  
  253.         printk("mydemo_fifo=%p\n", &device->mydemo_fifo);
  254.     }
  255.  
  256.     printk("succeeded register char device: %s\n", DEMO_NAME);
  257.  
  258.     return 0;
  259.  
  260. free_kfifo:
  261.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  262.         if (&device->mydemo_fifo)
  263.             kfifo_free(&device->mydemo_fifo);
  264. free_device:
  265.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  266.         if (mydemo_device[i])
  267.             kfree(mydemo_device[i]);
  268. cdev_fail:
  269.     cdev_del(demo_cdev);
  270. unregister_chrdev:
  271.     unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
  272.     return ret;
  273. }
  274.  
  275. static void __exit simple_char_exit(void)
  276. {
  277.     int i;
  278.     printk("removing device\n");
  279.  
  280.     if (demo_cdev)
  281.         cdev_del(demo_cdev);
  282.  
  283.     unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
  284.  
  285.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  286.     {
  287.         if (mydemo_device[i])
  288.         {
  289.             device_destroy(mydemo_class, MKDEV(dev, i));
  290.             kfree(mydemo_device[i]);
  291.  
  292.         }
  293.     }
  294.     class_destroy(mydemo_class);
  295. }
  296.  
  297. module_init(simple_char_init);
  298. module_exit(simple_char_exit);
  299.  
  300. MODULE_AUTHOR("rlk");
  301. 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 很类似。

代码清单 2 工作队列
  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/uaccess.h>
  4. #include <linux/init.h>
  5. #include <linux/miscdevice.h>
  6. #include <linux/device.h>
  7. #include <linux/slab.h>
  8. #include <linux/kfifo.h>
  9. #include <linux/wait.h>
  10. #include <linux/sched.h>
  11. #include <linux/cdev.h>
  12. #include <linux/poll.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/workqueue.h>
  15.  
  16. #define DEMO_NAME "mydemo_dev"
  17. #define MYDEMO_FIFO_SIZE 64
  18.  
  19. static dev_t dev;
  20. static struct cdev* demo_cdev;
  21. static struct class* mydemo_class;
  22.  
  23. struct mydemo_device
  24. {
  25.     char name[64];
  26.     struct device* dev;
  27.     wait_queue_head_t read_queue;
  28.     wait_queue_head_t write_queue;
  29.     struct kfifo mydemo_fifo;
  30.     struct fasync_struct* fasync;
  31.     struct mutex lock;
  32. };
  33.  
  34. struct mydemo_private_data
  35. {
  36.     struct mydemo_device* device;
  37.     char name[64];
  38.     struct tasklet_struct tasklet;
  39.     struct work_struct my_work;
  40. };
  41.  
  42. #define MYDEMO_MAX_DEVICES 8
  43. static struct mydemo_device* mydemo_device[MYDEMO_MAX_DEVICES];
  44.  
  45. static void do_tasklet(unsigned long data)
  46. {
  47.     struct mydemo_device* device = (struct mydemo_device*)data;
  48.     dev_info(device->dev, "%s: trigger a tasklet\n", __func__);
  49. }
  50.  
  51. static void do_work(struct work_struct* work)
  52. {
  53.     struct mydemo_private_data* data;
  54.     struct mydemo_device* device;
  55.  
  56.     data = container_of(work, struct mydemo_private_data, my_work);
  57.     device = data->device;
  58.     dev_info(device->dev, "%s:trigger a work\n", __func__);
  59. }
  60.  
  61. static int demodrv_open(struct inode* inode, struct file* file)
  62. {
  63.     unsigned int minor = iminor(inode);
  64.     struct mydemo_private_data* data;
  65.     struct mydemo_device* device = mydemo_device[minor];
  66.  
  67.     dev_info(device->dev, "%s: major=%d,minor=%d,device=%s\n", __func__,
  68.         MAJOR(inode->i_rdev), MINOR(inode->i_rdev), device->name);
  69.  
  70.     data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);
  71.     if (!data)
  72.         return -ENOMEM;
  73.  
  74.     sprintf(data->name, "private_data_%d", minor);
  75.     tasklet_init(&data->tasklet, do_tasklet, (unsigned long)device);
  76.     INIT_WORK(&data->my_work, do_work);
  77.  
  78.     data->device = device;
  79.     file->private_data = data;
  80.  
  81.     return 0;
  82. }
  83.  
  84. static int demodrv_release(struct inode* inode, struct file* file)
  85. {
  86.     struct mydemo_private_data* data = file->private_data;
  87.  
  88.     tasklet_kill(&data->tasklet);
  89.     kfree(data);
  90.  
  91.     return 0;
  92. }
  93.  
  94. static ssize_t demodrv_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
  95. {
  96.     struct mydemo_private_data* data = file->private_data;
  97.     struct mydemo_device* device = data->device;
  98.     int actual_readed;
  99.     int ret;
  100.  
  101.     if (kfifo_is_empty(&device->mydemo_fifo))
  102.     {
  103.         if (file->f_flags & O_NONBLOCK)
  104.             return -EAGAIN;
  105.  
  106.         dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
  107.         ret = wait_event_interruptible(device->read_queue, !kfifo_is_empty(&device->mydemo_fifo));
  108.         if (ret)
  109.             return ret;
  110.     }
  111.  
  112.     mutex_lock(&device->lock);
  113.     ret = kfifo_to_user(&device->mydemo_fifo, buf, count, &actual_readed);
  114.     if (ret)
  115.         return -EIO;
  116.     schedule_work(&data->my_work);
  117.     mutex_unlock(&device->lock);
  118.  
  119.     if (!kfifo_is_full(&device->mydemo_fifo))
  120.     {
  121.         wake_up_interruptible(&device->write_queue);
  122.         kill_fasync(&device->fasync, SIGIO, POLL_OUT);
  123.     }
  124.  
  125.     dev_info(device->dev, "%s:%s,pid=%d,actual_readed=%d,pos=%lld\n", __func__,
  126.         device->name, current->pid, actual_readed, *ppos);
  127.     return actual_readed;
  128. }
  129.  
  130. static ssize_t demodrv_write(struct file* file, const char __user* buf, size_t count, loff_t* ppos)
  131. {
  132.     struct mydemo_private_data* data = file->private_data;
  133.     struct mydemo_device* device = data->device;
  134.  
  135.     unsigned int actual_write;
  136.     int ret;
  137.  
  138.     if (kfifo_is_full(&device->mydemo_fifo))
  139.     {
  140.         if (file->f_flags & O_NONBLOCK)
  141.             return -EAGAIN;
  142.  
  143.         dev_info(device->dev, "%s:%s pid=%d,going to sleep,%s\n", __func__, device->name, current->pid, data->name);
  144.         ret = wait_event_interruptible(device->write_queue, !kfifo_is_full(&device->mydemo_fifo));
  145.         if (ret)
  146.             return ret;
  147.     }
  148.  
  149.     mutex_lock(&device->lock);
  150.     ret = kfifo_from_user(&device->mydemo_fifo, buf, count, &actual_write);
  151.     if (ret)
  152.         return -EIO;
  153.     mutex_unlock(&device->lock);
  154.  
  155.     if (!kfifo_is_empty(&device->mydemo_fifo))
  156.     {
  157.         wake_up_interruptible(&device->read_queue);
  158.         kill_fasync(&device->fasync, SIGIO, POLL_IN);
  159.         printk("%s kill fasync\n", __func__);
  160.     }
  161.  
  162.     dev_info(device->dev, "%s:%s,pid=%d,actual_write=%d,pos=%lld\n", __func__,
  163.         device->name, current->pid, actual_write, *ppos);
  164.  
  165.     return actual_write;
  166. }
  167.  
  168. static unsigned int demodrv_poll(struct file* file, poll_table* wait)
  169. {
  170.     int mask = 0;
  171.     struct mydemo_private_data* data = file->private_data;
  172.     struct mydemo_device* device = data->device;
  173.  
  174.     mutex_lock(&device->lock);
  175.     poll_wait(file, &device->read_queue, wait);
  176.     poll_wait(file, &device->write_queue, wait);
  177.  
  178.     if (!kfifo_is_empty(&device->mydemo_fifo))
  179.         mask |= POLLIN | POLLRDNORM;
  180.     if (!kfifo_is_full(&device->mydemo_fifo))
  181.         mask |= POLLOUT | POLLWRNORM;
  182.     mutex_unlock(&device->lock);
  183.  
  184.     return mask;
  185. }
  186.  
  187. static int demodrv_fasync(int fd, struct file* file, int on)
  188. {
  189.     struct mydemo_private_data* data = file->private_data;
  190.     struct mydemo_device* device = data->device;
  191.     int ret;
  192.  
  193.     mutex_lock(&device->lock);
  194.     ret = fasync_helper(fd, file, on, &device->fasync);
  195.     mutex_unlock(&device->lock);
  196.  
  197.     return ret;
  198. }
  199.  
  200. static const struct file_operations demodrv_fops = {
  201.     .owner = THIS_MODULE,
  202.     .open = demodrv_open,
  203.     .release = demodrv_release,
  204.     .read = demodrv_read,
  205.     .write = demodrv_write,
  206.     .poll = demodrv_poll,
  207.     .fasync = demodrv_fasync,
  208. };
  209.  
  210. static int __init simple_char_init(void)
  211. {
  212.     int ret;
  213.     int i;
  214.     struct mydemo_device* device;
  215.  
  216.     ret = alloc_chrdev_region(&dev, 0, MYDEMO_MAX_DEVICES, DEMO_NAME);
  217.     if (ret)
  218.     {
  219.         printk("failed to allocate char device region");
  220.         return ret;
  221.     }
  222.  
  223.     demo_cdev = cdev_alloc();
  224.     if (!demo_cdev)
  225.     {
  226.         printk("cdev_alloc failed\n");
  227.         goto unregister_chrdev;
  228.     }
  229.  
  230.     cdev_init(demo_cdev, &demodrv_fops);
  231.  
  232.     ret = cdev_add(demo_cdev, dev, MYDEMO_MAX_DEVICES);
  233.     if (ret)
  234.     {
  235.         printk("cdev_add failed\n");
  236.         goto cdev_fail;
  237.     }
  238.  
  239.     mydemo_class = class_create(THIS_MODULE, "my_class");
  240.  
  241.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  242.     {
  243.         device = kzalloc(sizeof(struct mydemo_device), GFP_KERNEL);
  244.         if (!device) {
  245.             ret = -ENOMEM;
  246.             goto free_device;
  247.         }
  248.  
  249.         sprintf(device->name, "%s%d", DEMO_NAME, i);
  250.         mutex_init(&device->lock);
  251.  
  252.         device->dev = device_create(mydemo_class, NULL, MKDEV(dev, i), NULL, "mydemo:%d:%d", MAJOR(dev), i);
  253.         dev_info(device->dev, "create device: %d:%d\n", MAJOR(dev), MINOR(i));
  254.         mydemo_device[i] = device;
  255.         init_waitqueue_head(&device->read_queue);
  256.         init_waitqueue_head(&device->write_queue);
  257.  
  258.         ret = kfifo_alloc(&device->mydemo_fifo,
  259.             MYDEMO_FIFO_SIZE,
  260.             GFP_KERNEL);
  261.         if (ret) {
  262.             ret = -ENOMEM;
  263.             goto free_kfifo;
  264.         }
  265.  
  266.         printk("mydemo_fifo=%p\n", &device->mydemo_fifo);
  267.     }
  268.  
  269.     printk("succeeded register char device: %s\n", DEMO_NAME);
  270.  
  271.     return 0;
  272.  
  273. free_kfifo:
  274.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  275.         if (&device->mydemo_fifo)
  276.             kfifo_free(&device->mydemo_fifo);
  277. free_device:
  278.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  279.         if (mydemo_device[i])
  280.             kfree(mydemo_device[i]);
  281. cdev_fail:
  282.     cdev_del(demo_cdev);
  283. unregister_chrdev:
  284.     unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
  285.     return ret;
  286. }
  287.  
  288. static void __exit simple_char_exit(void)
  289. {
  290.     int i;
  291.     printk("removing device\n");
  292.  
  293.     if (demo_cdev)
  294.         cdev_del(demo_cdev);
  295.  
  296.     unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
  297.  
  298.     for (i = 0; i < MYDEMO_MAX_DEVICES; i++)
  299.     {
  300.         if (mydemo_device[i])
  301.         {
  302.             device_destroy(mydemo_class, MKDEV(dev, i));
  303.             kfree(mydemo_device[i]);
  304.  
  305.         }
  306.     }
  307.     class_destroy(mydemo_class);
  308. }
  309.  
  310. module_init(simple_char_init);
  311. module_exit(simple_char_exit);
  312.  
  313. MODULE_AUTHOR("rlk");
  314. 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_interruptiblewake_up_interruptible 的使用。这边再介绍一下 prepare_to_waitschedulefinish_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_INTERRUPTIBLETASK_UNINTERRUPTIBLE

可以使用 DEFINE_WAIT 宏创建当前执行进程的等待队列条目。

prepare_to_wait 只是信息管理,不会执行进程调度。进程调用使用 schedule 函数,它会将当前进程从运行队列中移出,允许内核选择另一个进程来执行。

当进程不再需要等待特定条件时,使用 finish_wait 函数把指定的进程从等待队列中移除,并执行必要的清理操作。

代码清单 3 定时器和内核线程
  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/kthread.h>
  5. #include <linux/freezer.h>
  6. #include <linux/mutex.h>
  7. #include <linux/delay.h>
  8.  
  9. static void my_timefunc(struct timer_list* unused);
  10. static DEFINE_TIMER(my_timer, my_timefunc);
  11. static atomic_t flags;
  12. wait_queue_head_t wait_head;
  13.  
  14. static void my_timefunc(struct timer_list* unused)
  15. {
  16.     atomic_set(&flags, 1);
  17.     wake_up_interruptible(&wait_head);
  18.     mod_timer(&my_timer, jiffies + msecs_to_jiffies(2000));
  19. }
  20.  
  21. static void my_try_to_sleep(void)
  22. {
  23.     DEFINE_WAIT(wait);
  24.  
  25.     if (freezing(current) || kthread_should_stop())
  26.         return;
  27.  
  28.     prepare_to_wait(&wait_head, &wait, TASK_INTERRUPTIBLE);
  29.  
  30.     if (!atomic_read(&flags))
  31.         schedule();
  32.  
  33.     finish_wait(&wait_head, &wait);
  34. }
  35.  
  36. static void show_reg(void)
  37. {
  38.     unsigned int spsr, sp;
  39.     struct task_struct* task = current;
  40.  
  41.     asm("mrs %0, spsr_el1" : "=r" (spsr) : : "cc");
  42.     asm("mov %0, sp" : "=r" (sp) : : "cc");
  43.  
  44.     printk("%s: %s, pid:%d\n", __func__, task->comm, task->pid);
  45.     printk("cpsr:0x%x, sp:0x%x\n", spsr, sp);
  46. }
  47.  
  48. static int my_thread(void* nothing)
  49. {
  50.     set_freezable();
  51.     set_user_nice(current, 0);
  52.  
  53.     while (!kthread_should_stop())
  54.     {
  55.         my_try_to_sleep();
  56.         atomic_set(&flags, 0);
  57.         show_reg();
  58.     }
  59.     return 0;
  60. }
  61.  
  62. static struct task_struct* thread;
  63. static int __init my_init(void)
  64. {
  65.     printk("ben: my lockdep module init\n");
  66.  
  67.     thread = kthread_run(my_thread, NULL, "ktest");
  68.  
  69.     my_timer.expires = jiffies + msecs_to_jiffies(500);
  70.     add_timer(&my_timer);
  71.  
  72.     init_waitqueue_head(&wait_head);
  73.  
  74.     return 0;
  75. }
  76.  
  77. static void __exit my_exit(void)
  78. {
  79.     printk("goodbye\n");
  80.     kthread_stop(thread);
  81. }
  82.  
  83. MODULE_LICENSE("GPL");
  84. module_init(my_init);
  85. module_exit(my_exit);

现在我们看代码清单 3 的功能,就会很清楚。首先在模块加载时,会创建一个内核进程,执行想要的功能。具体的功能是定时打印寄存器信息,见 show_reg 函数。实现定时的机制是:通过原子变量 flags 标识是否到时间打印信息了,如果没到,则线程进行调度睡眠;定时器到时时,设置标识原子变量 flags,唤醒等待中的线程。

为什么不直接在定时器函数中执行具体的功能?因为定时器函数是在中断上下文中执行的,需要尽可能快的返回。