5 kernel timer programming

Like Linux interrupt, the programming of kernel timer only needs to call the API functions provided by the kernel. After the clock interrupt occurs, the kernel will check whether each timer expires. After expiration, it will put its bound timer processing function to the bottom half for execution.
Kernel timer essentially depends on hardware timer.

1, Timer API function

1,timer_list structure

In the Linux kernel, if you need to use a timer, you must first define a timer_list structure, corresponding to the following:

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the
	 * same cacheline
	 */
	struct list_head entry;
	unsigned long expires;
	struct tvec_base *base;

	void (*function)(unsigned long);
	unsigned long data;

	int slack;

#ifdef CONFIG_TIMER_STATS
	int start_pid;
	void *start_site;
	char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

In this structure, we usually only need to pay attention to three parameters, namely:

① unsigned long expires; / / expiration time of timer
② void (*function)(unsigned long); / / functions that will be executed when the timer expires
③ unsigned long data; / / private data will be passed to the timer processing function

2. Initialization timer

init_timer(struct timer_list *timer);

        init_timer is a macro when you define a timer_ After the list structure, you need to use this macro for initialization

3. Add timer

void add_timer(struct timer_list *timer)
{
	BUG_ON(timer_pending(timer));
	mod_timer(timer, timer->expires);
}

After initializing a timer, you need to use add_timer function to add the timer to the dynamic timer list of the kernel.

4. Delete timer

int del_timer(struct timer_list *timer)
{
	struct tvec_base *base;
	unsigned long flags;
	int ret = 0;

	debug_assert_init(timer);

	timer_stats_timer_clear_start_info(timer);
	if (timer_pending(timer)) {
		base = lock_timer_base(timer, &flags);
		ret = detach_if_pending(timer, base, true);
		spin_unlock_irqrestore(&base->lock, flags);
	}

	return ret;
}

When unloading the driver, you need to delete the timer on the kernel dynamic timer chain in the exit function.

5. Modify the expiration time of the timer

int mod_timer(struct timer_list *timer, unsigned long expires)
{
	expires = apply_slack(timer, expires);

	/*
	 * This is a common optimization triggered by the
	 * networking code - if the timer is re-modified
	 * to be the same thing then just return:
	 */
	if (timer_pending(timer) && timer->expires == expires)
		return 1;

	return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}

This function can modify the expiration time of the original timer.

Timer use template:

 struct timer_list timer; /* Define timer */
/* Timer callback function */
void function(unsigned long arg){
/*
* Timer processing code
*/
 
 /* If the timer needs to run periodically, use mod_timer
 * Function resets the timeout value and starts the timer.
 */
 mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
 }
 
 /* Initialization function */
void init(void){
 init_timer(&timer); /* Initialization timer */
 timer.function = function; /* Set timing processing function */
 timer.expires=jffies + msecs_to_jiffies(2000);/* Timeout 2 seconds */
 timer.data = (unsigned long)&dev; /* Take equipment structure as parameter */
 
 add_timer(&timer); /* Start timer */
}
 
 /* Exit function */
 void exit(void){
 del_timer(&timer); /* Delete timer */
 /* Or use */
 del_timer_sync(&timer);
}

2, Second character device

The function of the device driver: output the current jiffies once per second

Driver code:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define SECOND_MAJOR 248

static int second_major = SECOND_MAJOR;
module_param(second_major, int, S_IRUGO);

struct second_dev {
	struct cdev cdev;
	atomic_t counter;
	struct timer_list s_timer;
};

static struct second_dev *second_devp;

static void second_timer_handler(unsigned long arg)
{
	mod_timer(&second_devp->s_timer, jiffies + HZ); /* Trigger next timing */
	atomic_inc(&second_devp->counter); /* Increase second count */

	printk(KERN_INFO "current jiffies is %ld\n", jiffies);
}

static int second_open(struct inode *inode, struct file *filp)
{
	init_timer(&second_devp->s_timer);
	second_devp->s_timer.function = &second_timer_handler;
	second_devp->s_timer.expires = jiffies + HZ;

	add_timer(&second_devp->s_timer);

	atomic_set(&second_devp->counter, 0);  /* The initialization second count is 0 */                   

	return 0;
}

static int second_release(struct inode *inode, struct file *filp)
{
	del_timer(&second_devp->s_timer);

	return 0;
}

static ssize_t second_read(struct file *filp, char __user * buf, size_t count,
	loff_t * ppos)
{
	int counter;

	counter = atomic_read(&second_devp->counter);
	if (put_user(counter, (int *)buf)) /* Copy counter to userspace */
		return -EFAULT;
	else
		return sizeof(unsigned int);
}

static const struct file_operations second_fops = {
	.owner = THIS_MODULE,
	.open = second_open,
	.release = second_release,
	.read = second_read,
};

static void second_setup_cdev(struct second_dev *dev, int index)
{
	int err, devno = MKDEV(second_major, index);

	cdev_init(&dev->cdev, &second_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev, devno, 1);
	if (err)
		printk(KERN_ERR "Failed to add second device\n");
}

static int __init second_init(void)
{
	int ret;
	dev_t devno = MKDEV(second_major, 0);

	if (second_major)
		ret = register_chrdev_region(devno, 1, "second");
	else {		
		ret = alloc_chrdev_region(&devno, 0, 1, "second");
		second_major = MAJOR(devno);
	}
	if (ret < 0)
		return ret;

	second_devp = kzalloc(sizeof(*second_devp), GFP_KERNEL);
	if (!second_devp) {
		ret = -ENOMEM;
		goto fail_malloc;
	}

	second_setup_cdev(second_devp, 0);

	return 0;

fail_malloc:
	unregister_chrdev_region(devno, 1);
	return ret;
}
module_init(second_init);

static void __exit second_exit(void)
{
	cdev_del(&second_devp->cdev);	
	kfree(second_devp);	
	unregister_chrdev_region(MKDEV(second_major, 0), 1);
}
module_exit(second_exit);

MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL v2"); 

XXX will be executed when the driver is opened by the application_ The open function initializes the timer and adds the timer to the kernel.
Then the current jiffies will be uploaded to the user every 1 second

Application code:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	int fd;
	int counter = 0;
	int old_counter = 0;
	fd = open("/dev/second",O_RDONLY);//open device
	if(fd != -1)
	{
		 while(1)
		 {
			  read(fd,&counter,sizeof(unsigned int));//Read sizeof(unsigned int) bytes from fd pointer descriptor to counter
			  if(counter != old_counter) //Is it equal to old_counter
			  {
				   printf("seconds after open /dev/second : %d\n",counter);
				   old_counter = counter;//In this way, it will be synchronized
			  }
		 }
	}
	else
	{
		printf("Device open failure\n");
	}
}

Verify the driver on the development board:

Load drivers and create device nodes

Run the application and find that the current jiffies will be printed every second. You can find that the frequency set by the current kernel is 100HZ

Tags: Linux Linux Driver

Posted by bad_gui on Sun, 22 May 2022 01:25:07 +0300