[Linux内核驱动]tasklet

tasklet

使用tasklet构建下面的数据表

   time   delta  inirq    pid   cpu command
  1321555    0     0     10659   3   cat
  1321805  250     1       0     3  swapper/3
  1322055  250     1       0     3  swapper/3
  1322306  251     1       0     3  swapper/3
  1322560  254     1       0     3  swapper/3

linux中断处理

对于中断处理,linux将其分为两个部分——上半部(top half)和下半部(bottom half)。上半部用于完成尽量少的比较紧急的任务并在清除中断标志后进行“登记中断”的工作,即将下半部处理程序挂到该设备的下半部执行队列中。

一般来说,上半部被设计成不可中断,下半部可以被新的中断打断。下半部几乎做了中断处理程序的所有工作,下半部相对来说不是非常紧急的工作,而且相对耗时,不在硬件中断服务程序中执行。

实现下半部的机制——tasklet

除了tasklet以外,实现下半部的机制还有工作队列(workqueue)、软中断和线程化irq

tasklet的使用比较简单,他的执行上下文软中断,我们只需要定义tasklet及其处理函数,并将二者关联起来即可。

tasklet_struct 结构体

struct tasklet_struct {
	struct tasklet_struct *next;     //链表中的下一个tasklet
	unsigned long state;             //tasklet的状态        
	atomic_t count;                  //引用计数器
	void(*func) (unsigned long data) //tasklet处理函数
	unsigned long data;              //给tasklet处理函数的参数     
}
  • state:表示该tasklet的状态,TASKLET_STATE_SCHED表示该tasklet以及被调度到某个CPU上执行,TASKLET_STATE_RUN表示该tasklet正在某个cpu上执行
  • count:和enable或者disable该tasklet的状态相关,如果count等于0那么该tasklet是处于enable的,如果大于0,表示该tasklet是disable的。

初始化tasklet

  1. 动态初始化
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
  1. 静态初始化
DECLARE_TASKLET(name, func, data)
DECLARE_TASKLET_DISABLED(name, func, data)

这两个宏都可以静态定义一个struct tasklet_struct的变量,只不过初始化后的tasklet一个是处于eable状态,一个处于disable状态的。

调度tasklet

void tasklet_schedule(struct tasklet_struct *t)

启用/禁止tasklet

void tasklet_enable(struct tasklet_struct *t)
void tasklet_disable(struct tasklet_struct *t)

删除tasklet

void tasklet_kill(struct tasklet_struct *t)

代码

/*
 * @Date: 2024-05-18 11:12:53
 * @author: lidonghang-02 2426971102@qq.com
 * @LastEditTime: 2024-05-19 19:57:13
 */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/sched.h>

#include <linux/interrupt.h>

#define TASKLET_MAJOR 0
#define TASKLET_MINOR 0
#define TASKLET_NR_DEVS 1

static int tasklet_major = TASKLET_MAJOR;
static int tasklet_minor = TASKLET_MINOR;
static int tasklet_nr_devs = TASKLET_NR_DEVS;

struct tasklet_dev
{
  struct cdev cdev;
  struct device* cls_dev;
  char* buf;
  int loops;
  unsigned long pre_jiffies;
  wait_queue_head_t wq;
  struct tasklet_struct tasklet;
};

static struct class* class = NULL;
static struct tasklet_dev* tasklet_devp;

static int tasklet_open_func(struct inode* inode, struct file* filp)
{
  struct tasklet_dev* dev = container_of(inode->i_cdev, struct tasklet_dev, cdev);
  filp->private_data = dev;

  printk(KERN_INFO "tasklet_open\n");
  return 0;
}

static int tasklet_release_func(struct inode* inode, struct file* filp)
{
  printk(KERN_INFO "tasklet_release\n");
  return 0;
}

static void tasklet_handler(unsigned long data)
{
  struct tasklet_dev* dev = (struct tasklet_dev*)data;
  dev->buf += sprintf(dev->buf,
    "%9li  %3li     %i    %6i   %i   %s\n",
    jiffies, jiffies - dev->pre_jiffies,
    in_interrupt() ? 1 : 0, current->pid,
    smp_processor_id(), current->comm);

  dev->loops--;
  if (dev->loops)
  {
    dev->pre_jiffies = jiffies;
    tasklet_schedule(&dev->tasklet);
  }
  else
    wake_up_interruptible(&dev->wq);
}
static ssize_t tasklet_read_func(struct file* filp, char __user* buf, size_t count, loff_t* f_pos)
{
  ssize_t ret = 0;
  size_t cnt;
  char* tmp_buf;

  struct tasklet_dev* dev = filp->private_data;

  // 只读一次
  if (*f_pos > 0)
    return 0;

  tmp_buf = kmalloc(200, GFP_KERNEL);
  if (!tmp_buf)
    return -ENOMEM;

  dev->loops = 4;
  dev->buf = tmp_buf;
  dev->buf += sprintf(dev->buf, "   time   delta  inirq    pid   cpu command\n");
  dev->buf += sprintf(dev->buf,
    "%9li  %3li     %i    %6i   %i   %s\n",
    jiffies, 0L, in_interrupt() ? 1 : 0,
    current->pid, smp_processor_id(), current->comm);
  dev->pre_jiffies = jiffies;

  tasklet_init(&dev->tasklet, tasklet_handler, (unsigned long)dev);
  tasklet_schedule(&dev->tasklet);

  wait_event_interruptible(dev->wq, !dev->loops);
  tasklet_kill(&dev->tasklet);

  cnt = dev->buf - tmp_buf;
  if (copy_to_user(buf, tmp_buf, cnt))
  {
    ret = -EFAULT;
    goto out;
  }

  *f_pos += cnt;
  ret = cnt;

out:
  kfree(tmp_buf);
  return ret;
}

static const struct file_operations tasklet_fops = {
    .owner = THIS_MODULE,
    .open = tasklet_open_func,
    .release = tasklet_release_func,
    .read = tasklet_read_func,
};

static int __init tasklet_init_module(void)
{
  int ret, i;
  dev_t devno = MKDEV(tasklet_major, tasklet_minor);
  if (tasklet_major)
    ret = register_chrdev_region(devno, tasklet_nr_devs, "tasklet");
  else
  {
    ret = alloc_chrdev_region(&devno, 0, tasklet_nr_devs, "tasklet");
    tasklet_major = MAJOR(devno);
  }

  if (ret < 0)
  {
    printk(KERN_ALERT "register_chrdev_region failed with %d\n", ret);
    goto out_1;
  }

  tasklet_devp = kzalloc(tasklet_nr_devs * sizeof(struct tasklet_dev), GFP_KERNEL);
  if (!tasklet_devp)
  {
    ret = -ENOMEM;
    printk(KERN_ALERT "Out of memory\n");
    goto out_2;
  }

  class = class_create(THIS_MODULE, "tasklet");
  if (IS_ERR(class))
  {
    ret = PTR_ERR(class);
    printk(KERN_ALERT "class_create failed with %d\n", ret);
    goto out_3;
  }

  for (i = 0; i < tasklet_nr_devs; i++)
  {
    cdev_init(&tasklet_devp[i].cdev, &tasklet_fops);
    tasklet_devp[i].cdev.owner = THIS_MODULE;
    ret = cdev_add(&tasklet_devp[i].cdev, MKDEV(tasklet_major, tasklet_minor + i), 1);
    if (ret < 0)
      printk(KERN_ALERT "cdev_add failed with %d\n", ret);
    else
    {
      tasklet_devp[i].cls_dev = device_create(class, NULL, MKDEV(tasklet_major, tasklet_minor + i), NULL, "tasklet%d", i);
      if (IS_ERR(tasklet_devp[i].cls_dev))
        printk(KERN_ALERT "device_create failed with %ld\n", PTR_ERR(tasklet_devp[i].cls_dev));
    }
    init_waitqueue_head(&tasklet_devp[i].wq);
  }
  printk(KERN_INFO "tasklet_init\n");
  return 0;

out_3:
  kfree(tasklet_devp);
out_2:
  unregister_chrdev_region(devno, tasklet_nr_devs);
out_1:
  return ret;
}

static void __exit tasklet_exit_module(void)
{
  int i;
  for (i = 0; i < tasklet_nr_devs; i++)
  {
    device_destroy(class, MKDEV(tasklet_major, tasklet_minor + i));
    cdev_del(&tasklet_devp[i].cdev);
  }
  class_destroy(class);
  kfree(tasklet_devp);
  unregister_chrdev_region(MKDEV(tasklet_major, tasklet_minor), tasklet_nr_devs);
  printk(KERN_INFO "tasklet_exit\n");
}

module_param(tasklet_major, int, S_IRUGO);
module_param(tasklet_minor, int, S_IRUGO);
module_param(tasklet_nr_devs, int, S_IRUGO);

module_init(tasklet_init_module);
module_exit(tasklet_exit_module);

MODULE_AUTHOR("lidonghang-02");
MODULE_LICENSE("GPL");

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值