[Linux内核驱动]定时器应用

定时器的应用

使用定时器构造下面的数据表

   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

部分用到的函数

setup_timer()

setup_timer 是 Linux 内核中用于初始化一个 struct timer_list 结构体的函数。这个结构体用于表示一个内核定时器,当定时器到期时,会调用一个预先设定的函数(称为定时器处理函数)。

void setup_timer(struct timer_list *timer, void (*function)(struct timer_list *), unsigned int flags);

参数说明:

  • struct timer_list *timer:指向要初始化的 timer_list 结构体的指针。
  • void (*function)(struct timer_list *):当定时器到期时调用的处理函数。这个函数应该接受一个指向 timer_list 结构体的指针作为参数。
  • unsigned int flags:标志位,用于控制定时器的行为。通常设置为 0,但也可以包含一些特殊的标志位,如 TIMER_DEFERRABLE(表示该定时器可以延迟执行)。

在初始化定时器之后,你可以使用 mod_timer 或 add_timer 函数将其添加到内核的定时器队列中,以便在指定的时间后触发。

in_interrupt()

in_interrupt() 是一个宏,它用于检查当前是否处于中断上下文中。

current

current 是一个指向当前正在运行的进程描述符(通常是task_struct类型)的指针。

  • pid 是该进程描述符中的一个成员,表示进程的进程ID(PID)。
  • comm 是task_struct结构中的一个成员,它表示进程的命令名。

smp_processor_id()

smp_processor_id() 是一个宏,用于在多处理器系统(SMP)上获取当前CPU的ID。在多处理器系统中,每个CPU都有一个唯一的ID,这个ID可以通过这个宏来获取。

代码

/*
 * @Date: 2024-05-17 19:16:18
 * @author: lidonghang-02 2426971102@qq.com
 * @LastEditTime: 2024-05-29 17:08:27
 */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/sched.h>

#include <linux/timer.h>

#define TIMER_MAJOR 0
#define TIMER_MINOR 0
#define TIMER_NR_DEVS 1

static int timer_major = TIMER_MAJOR;
static int timer_minor = TIMER_MINOR;
static int timer_nr_devs = TIMER_NR_DEVS;

static struct class *class = NULL;
struct timer_dev
{
  struct cdev cdev;
  struct device *cls_dev;
  char *buf;
  int delay;
  int loops;
  unsigned long pre_jiffies;
  wait_queue_head_t wq;
  struct timer_list timer;
};

static struct timer_dev *timer_devp;

// 定时器回调函数
void timer_handler(unsigned long data)
{
  struct timer_dev *dev = (struct timer_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 > 0)
  {
    mod_timer(&dev->timer, jiffies + dev->delay);
    dev->pre_jiffies = jiffies;
  }
  else
  {
    del_timer(&dev->timer);
    wake_up_interruptible(&dev->wq);
  }
}

static int timer_open_func(struct inode *inode, struct file *filp)
{
  struct timer_dev *dev = container_of(inode->i_cdev, struct timer_dev, cdev);
  filp->private_data = dev;
  printk(KERN_INFO "timer_open\n");
  return 0;
}

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

static ssize_t timer_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 timer_dev *dev = filp->private_data;

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

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

  dev->loops = 4;
  dev->delay = HZ;
  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);

  // setup_timer在新版本的内核中已经被替换为timer_setup
  setup_timer(&dev->timer, timer_handler, (unsigned long)dev);
  mod_timer(&dev->timer, jiffies + dev->delay);

  // 也可以使用下面方法添加定时器
  // init_timer(&dev->timer);
  // dev->timer.function = timer_handler;
  // dev->timer.data = (unsigned long)dev;
  // dev->timer.expires = jiffies + dev->delay;
  // add_timer(&dev->timer);

  dev->pre_jiffies = jiffies;
  wait_event_interruptible(dev->wq, !dev->loops);
  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 timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open_func,
    .release = timer_release_func,
    .read = timer_read_func,
};

static int __init timer_init_module(void)
{
  int ret, i;
  dev_t devno = MKDEV(timer_major, timer_minor);

  if (timer_major)
    ret = register_chrdev_region(devno, timer_nr_devs, "timer");
  else
  {
    ret = alloc_chrdev_region(&devno, timer_minor, timer_nr_devs, "timer");
    timer_major = MAJOR(devno);
  }

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

  timer_devp = kzalloc(sizeof(struct timer_dev) * timer_nr_devs, GFP_KERNEL);
  if (timer_devp == NULL)
  {
    ret = -ENOMEM;
    printk(KERN_ALERT "Out of memory\n");
    goto out_2;
  }

  class = class_create(THIS_MODULE, "timer_class");
  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 < timer_nr_devs; i++)
  {
    cdev_init(&timer_devp[i].cdev, &timer_fops);
    timer_devp[i].cdev.owner = THIS_MODULE;
    ret = cdev_add(&timer_devp[i].cdev, MKDEV(timer_major, timer_minor + i), 1);
    if (ret < 0)
      printk(KERN_ALERT "cdev_add failed with %d\n", ret);
    else
    {
      timer_devp[i].cls_dev = device_create(class, NULL, MKDEV(timer_major, timer_minor + i), NULL, "timer%d", i);
      if (IS_ERR(timer_devp[i].cls_dev))
        printk(KERN_ALERT "device_create failed with %d\n", ret);
    }
    init_waitqueue_head(&timer_devp[i].wq);
  }

  printk(KERN_INFO "timer_init\n");
  return 0;

out_3:
  kfree(timer_devp);
out_2:
  unregister_chrdev_region(devno, timer_nr_devs);
out_1:
  return ret;
}

static void __exit timer_exit_module(void)
{
  int i;

  for (i = 0; i < timer_nr_devs; i++)
  {
    device_destroy(class, MKDEV(timer_major, timer_minor + i));
    cdev_del(&timer_devp[i].cdev);
  }
  class_destroy(class);
  kfree(timer_devp);
  unregister_chrdev_region(MKDEV(timer_major, timer_minor), timer_nr_devs);
  printk(KERN_INFO "timer_exit\n");
}

module_param(timer_major, int, S_IRUGO);
module_param(timer_minor, int, S_IRUGO);
module_param(timer_nr_devs, int, S_IRUGO);

module_init(timer_init_module);
module_exit(timer_exit_module);

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值