Linux内核之tasklet机制

tasklet机制用于queue up work等到未来某时执行。tasklet可以并行执行,但同一个tasklet在某一时刻只能运行于某一CPU上。为了cache优化,每个tasklet只会在调度它的CPU上运行。

tasklet就好比一个既没有stack也没有context的小巧thread,它会快速执行完毕

概要

使用tasklet需要注意一些原则:

  1. tasklets是原子操作的,所以不能调用sleep()等阻塞函数以及mutexes(互斥量),semaphores(信号量)等,不过可以使用自旋锁(spinlock)
  2. A tasklet only runs on the same core (CPU) that schedules it
  3. Different tasklets can be running in parallel. But at the same time, a tasklet cannot be called concurrently with itself, as it runs on one CPU only
  4. tasklets的调度机制必须是non-preemptive调度器,一个接一个的执行,不能抢占。所以可以用normal 和 high两种优先级调度它

tasklet structure

Create tasklet

include/linux/interrupt.h头文件中定义

/* Tasklets --- multithreaded analogue of BHs.

   This API is deprecated. Please consider using threaded IRQs instead:
   https://lore.kernel.org/lkml/20200716081538.2sivhkj4hcyrusem@linutronix.de

   Main feature differing them of generic softirqs: tasklet
   is running only on one CPU simultaneously.

   Main feature differing them of BHs: different tasklets
   may be run simultaneously on different CPUs.

   Properties:
   * If tasklet_schedule() is called, then tasklet is guaranteed
     to be executed on some cpu at least once after this.
   * If the tasklet is already scheduled, but its execution is still not
     started, it will be executed only once.
   * If this tasklet is already running on another CPU (or schedule is called
     from tasklet itself), it is rescheduled for later.
   * Tasklet is strictly serialized wrt itself, but not
     wrt another tasklets. If client needs some intertask synchronization,
     he makes it with spinlocks.
 */

struct tasklet_struct
{
	struct tasklet_struct *next;
	unsigned long state;
	atomic_t count;
	bool use_callback;
	union {
		void (*func)(unsigned long data);
		void (*callback)(struct tasklet_struct *t);
	};
	unsigned long data;
};

#define DECLARE_TASKLET(name, _callback)		\
struct tasklet_struct name = {				\
	.count = ATOMIC_INIT(0),			\
	.callback = _callback,				\
	.use_callback = true,				\
}

#define DECLARE_TASKLET_DISABLED(name, _callback)	\
struct tasklet_struct name = {				\
	.count = ATOMIC_INIT(1),			\
	.callback = _callback,				\
	.use_callback = true,				\
}

DECLARE_TASKLET()宏创建了一个tasklet结构体,并赋值其enable(count值为0表示enable,count值为非0,表示tasklet是disabled)。如果使用DECLARE_TASKLET_DISABLE()宏创建tasklet,该tasklet可以被调度,但其实disable的,需要enable之后才能实际运行。

Eanble/Disable
static inline void tasklet_disable(struct tasklet_struct *t)
{
	tasklet_disable_nosync(t);
	tasklet_unlock_wait(t);
	smp_mb();
}

static inline void tasklet_enable(struct tasklet_struct *t)
{
	smp_mb__before_atomic();
	atomic_dec(&t->count);
}

如果tasklet是disabled状态的,它是可以被加入queue然后被schedule的,但不会在CPU上运行,直至它被enable之后才会实际运行。如果某个tasklet被disable了n次, 那么对应的也需要enable它n次(count会计数),它才能真正被enabled

Schedule tasklet

当调度tasklet时,tasklet会被放入队列,一共是两种队列,normal和high两种优先级,单链表结构组织,每个CPU都有它自己的tasklet队列。

  • normal priority
  • high prioriy
tasklet_schedule

调度tasklet使用normal优先级

include/linux/interrupt.h

enum
{
	TASKLET_STATE_SCHED,	/* Tasklet is scheduled for execution */
	TASKLET_STATE_RUN	/* Tasklet is running (SMP only) */
};

static inline void tasklet_schedule(struct tasklet_struct *t)
{
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_schedule(t);
}

kernel/softirq.c

/*
 * This function must run with irqs disabled!
 */
inline void raise_softirq_irqoff(unsigned int nr)
{
	__raise_softirq_irqoff(nr);

	/*
	 * If we're in an interrupt or softirq, we're done
	 * (this also catches softirq-disabled code). We will
	 * actually run the softirq once we return from
	 * the irq or softirq.
	 *
	 * Otherwise we wake up ksoftirqd to make sure we
	 * schedule the softirq soon.
	 */
	if (!in_interrupt() && should_wake_ksoftirqd())  /*会判断是否在中断环境中*/
		wakeup_softirqd();
}

static void __tasklet_schedule_common(struct tasklet_struct *t,
				      struct tasklet_head __percpu *headp,
				      unsigned int softirq_nr)
{
	struct tasklet_head *head;
	unsigned long flags;

	local_irq_save(flags);
	head = this_cpu_ptr(headp);
	t->next = NULL;
	*head->tail = t;
	head->tail = &(t->next);
	raise_softirq_irqoff(softirq_nr);
	local_irq_restore(flags);
}

void __tasklet_schedule(struct tasklet_struct *t)
{
	__tasklet_schedule_common(t, &tasklet_vec,
				  TASKLET_SOFTIRQ);
}
EXPORT_SYMBOL(__tasklet_schedule);
tasklet_schedule
__tasklet_schedule
__tasklet_schedule_common
raise_softirq_irqoff

调度tasklet最后是通过TASKLET_SOFTIRQ的软中断去触发ksoftirqd内核线程来完成调度。

tasklet_hi_schedule

调度tasklet使用high优先级

include/linux/interrupt.h

static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_hi_schedule(t);
}

kernel/softirq.c

void __tasklet_hi_schedule(struct tasklet_struct *t)
{
	__tasklet_schedule_common(t, &tasklet_hi_vec,
				  HI_SOFTIRQ);
}
EXPORT_SYMBOL(__tasklet_hi_schedule);

调度tasklet进入high优先级调度队列是通过HI_SOFTIRQ软中断触发

Based on softirq

几个软中断定义在include/linux/interrupt.h

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
   frequency threaded job scheduling. For almost all the purposes
   tasklets are more than enough. F.e. all serial device BHs et
   al. should be converted to tasklets, not to softirqs.
 */

enum
{
	HI_SOFTIRQ=0,
	TIMER_SOFTIRQ,
	NET_TX_SOFTIRQ,
	NET_RX_SOFTIRQ,
	BLOCK_SOFTIRQ,
	IRQ_POLL_SOFTIRQ,
	TASKLET_SOFTIRQ,
	SCHED_SOFTIRQ,
	HRTIMER_SOFTIRQ,
	RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

	NR_SOFTIRQS
};
Kill tasklet
void tasklet_kill(struct tasklet_struct *t)
{
	if (in_interrupt())
		pr_notice("Attempt to kill tasklet from interrupt\n");

	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		wait_var_event(&t->state, !test_bit(TASKLET_STATE_SCHED, &t->state));

	tasklet_unlock_wait(t);
	tasklet_clear_sched(t);
}
EXPORT_SYMBOL(tasklet_kill);

示例

static method

通过DECLARE_TASKLET()宏构建tasklet

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h>                 //kmalloc()
#include<linux/uaccess.h>              //copy_to/from_user()
#include<linux/sysfs.h> 
#include<linux/kobject.h> 
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/hw_irq.h> 
 
#define IRQ_NO 11
 
void tasklet_fn(struct tasklet_struct *); 
/* Init the Tasklet by Static Method */
DECLARE_TASKLET(test_tasklet, tasklet_fn);
 
 
/*Tasklet Function*/
void tasklet_fn(struct tasklet_struct *t)
{
        printk(KERN_INFO "Executing Tasklet Function : tasklet count = %d\n", atomic_read(&t->count));
}
 
 
//Interrupt handler for IRQ 11. 
static irqreturn_t irq_handler(int irq,void *dev_id) {
        printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
        /*Scheduling Task to Tasklet*/
        tasklet_schedule(&test_tasklet); 
        
        return IRQ_HANDLED;
}
 
volatile int tasklet_value = 0;
 
dev_t dev = 0;
static struct class *dev_class;
static struct cdev tasklet_cdev;
struct kobject *kobj_ref;
 
static int __init tasklet_driver_init(void);
static void __exit tasklet_driver_exit(void);
 
/*************** Driver Functions **********************/
static int tasklet_open(struct inode *inode, struct file *file);
static int tasklet_release(struct inode *inode, struct file *file);
static ssize_t tasklet_read(struct file *filp, 
                char __user *buf, size_t len,loff_t * off);
static ssize_t tasklet_write(struct file *filp, 
                const char *buf, size_t len, loff_t * off);
 
/*************** Sysfs Functions **********************/
static ssize_t sysfs_show(struct kobject *kobj, 
                struct kobj_attribute *attr, char *buf);
static ssize_t sysfs_store(struct kobject *kobj, 
                struct kobj_attribute *attr,const char *buf, size_t count);
 
struct kobj_attribute tasklet_attr = __ATTR(tasklet_value, 0660, sysfs_show, sysfs_store);
 
/*
** File operation sturcture
*/
static struct file_operations fops =
{
        .owner          = THIS_MODULE,
        .read           = tasklet_read,
        .write          = tasklet_write,
        .open           = tasklet_open,
        .release        = tasklet_release,
};
 
/*
** This function will be called when we read the sysfs file
*/  
static ssize_t sysfs_show(struct kobject *kobj, 
                struct kobj_attribute *attr, char *buf)
{
        printk(KERN_INFO "Sysfs - Read!!!\n");
        return sprintf(buf, "%d", tasklet_value);
}
/*
** This function will be called when we write the sysfsfs file
*/  
static ssize_t sysfs_store(struct kobject *kobj, 
                struct kobj_attribute *attr,const char *buf, size_t count)
{
        printk(KERN_INFO "Sysfs - Write!!!\n");
        sscanf(buf,"%d",&tasklet_value);
        return count;
}
/*
** This function will be called when we open the Device file
*/  
static int tasklet_open(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Opened...!!!\n");
        return 0;
}
/*
** This function will be called when we close the Device file
*/   
static int tasklet_release(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Closed...!!!\n");
        return 0;
}
/*
** This function will be called when we read the Device file
*/ 
static ssize_t tasklet_read(struct file *filp, 
                char __user *buf, size_t len, loff_t *off)
{
        struct irq_desc *desc;
        printk(KERN_INFO "Read function\n");
        desc = irq_to_desc(11);
        if (!desc)
        {
            return -EINVAL;
        }
        __this_cpu_write(vector_irq[59], desc);
        asm("int $0x3B");  // Corresponding to irq 11
        return 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t tasklet_write(struct file *filp, 
                const char __user *buf, size_t len, loff_t *off)
{
        printk(KERN_INFO "Write Function\n");
        return len;
}
 
/*
** Module Init function
*/ 
static int __init tasklet_driver_init(void)
{
        /*Allocating Major number*/
        if((alloc_chrdev_region(&dev, 0, 1, "tasklet_Dev")) <0){
                printk(KERN_INFO "Cannot allocate major number\n");
                return -1;
        }
        printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
 
        /*Creating cdev structure*/
        cdev_init(&tasklet_cdev,&fops);
 
        /*Adding character device to the system*/
        if((cdev_add(&tasklet_cdev,dev,1)) < 0){
            printk(KERN_INFO "Cannot add the device to the system\n");
            goto r_class;
        }
 
        /*Creating struct class*/
        if((dev_class = class_create(THIS_MODULE,"tasklet_class")) == NULL){
            printk(KERN_INFO "Cannot create the struct class\n");
            goto r_class;
        }
 
        /*Creating device*/
        if((device_create(dev_class,NULL,dev,NULL,"tasklet_device")) == NULL){
            printk(KERN_INFO "Cannot create the Device 1\n");
            goto r_device;
        }
 
        /*Creating a directory in /sys/kernel/ */
        kobj_ref = kobject_create_and_add("tasklet_sysfs",kernel_kobj);
 
        /*Creating sysfs file for tasklet_value*/
        if(sysfs_create_file(kobj_ref,&tasklet_attr.attr)){
                printk(KERN_INFO"Cannot create sysfs file......\n");
                goto r_sysfs;
        }
        if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "tasklet_device", (void *)(irq_handler))) {
            printk(KERN_INFO "my_device: cannot register IRQ ");
                    goto irq;
        }
 
        printk(KERN_INFO "Device Driver Insert...Done!!!\n");
        return 0;
 
irq:
        free_irq(IRQ_NO,(void *)(irq_handler));
 
r_sysfs:
        kobject_put(kobj_ref); 
        sysfs_remove_file(kernel_kobj, &tasklet_attr.attr);
 
r_device:
        class_destroy(dev_class);
r_class:
        unregister_chrdev_region(dev,1);
        cdev_del(&tasklet_cdev);   
        return -1;
}
/*
** Module exit function
*/  
static void __exit tasklet_driver_exit(void)
{
        /*Kill the Tasklet */ 
        tasklet_kill(&test_tasklet);
        free_irq(IRQ_NO,(void *)(irq_handler));
        kobject_put(kobj_ref); 
        sysfs_remove_file(kernel_kobj, &tasklet_attr.attr);
        device_destroy(dev_class,dev);
        class_destroy(dev_class);
        cdev_del(&tasklet_cdev);
        unregister_chrdev_region(dev, 1);
        printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
 
module_init(tasklet_driver_init);
module_exit(tasklet_driver_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple device driver - Tasklet Static");
MODULE_VERSION("1.15");
dynamic method

通过tasklet_init()函数构建tasklet

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h>                 //kmalloc()
#include<linux/uaccess.h>              //copy_to/from_user()
#include<linux/sysfs.h> 
#include<linux/kobject.h> 
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/hw_irq.h>
 
 
#define IRQ_NO 11
 
void tasklet_fn(unsigned long); 
/* Tasklet by Dynamic Method */
struct tasklet_struct *dynamic_tasklet = NULL;
 
 
/*Tasklet Function*/
void tasklet_fn(unsigned long arg)
{
        printk(KERN_INFO "Executing Tasklet Function : arg = %ld\n", arg);
}
 
 
//Interrupt handler for IRQ 11. 
static irqreturn_t irq_handler(int irq,void *dev_id) {
        printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
        /*Scheduling Task to Tasklet*/
        tasklet_schedule(dynamic_tasklet); 
        
        return IRQ_HANDLED;
}
 
 
volatile int tasklet_value = 0;
 
 
dev_t dev = 0;
static struct class *dev_class;
static struct cdev tasklet_cdev;
struct kobject *kobj_ref;
 
static int __init tasklet_driver_init(void);
static void __exit tasklet_driver_exit(void);
 
/*************** Driver Functions **********************/
static int tasklet_open(struct inode *inode, struct file *file);
static int tasklet_release(struct inode *inode, struct file *file);
static ssize_t tasklet_read(struct file *filp, 
                char __user *buf, size_t len,loff_t * off);
static ssize_t tasklet_write(struct file *filp, 
                const char *buf, size_t len, loff_t * off);
 
/*************** Sysfs Functions **********************/
static ssize_t sysfs_show(struct kobject *kobj, 
                struct kobj_attribute *attr, char *buf);
static ssize_t sysfs_store(struct kobject *kobj, 
                struct kobj_attribute *attr,const char *buf, size_t count);
 
struct kobj_attribute tasklet_attr = __ATTR(tasklet_value, 0660, sysfs_show, sysfs_store);
/*
** File operation sturcture
*/
static struct file_operations fops =
{
        .owner          = THIS_MODULE,
        .read           = tasklet_read,
        .write          = tasklet_write,
        .open           = tasklet_open,
        .release        = tasklet_release,
};
/*
** This function will be called when we read the sysfs file
*/
static ssize_t sysfs_show(struct kobject *kobj, 
                struct kobj_attribute *attr, char *buf)
{
        printk(KERN_INFO "Sysfs - Read!!!\n");
        return sprintf(buf, "%d", tasklet_value);
}
/*
** This function will be called when we write the sysfsfs file
*/   
static ssize_t sysfs_store(struct kobject *kobj, 
                struct kobj_attribute *attr,const char *buf, size_t count)
{
        printk(KERN_INFO "Sysfs - Write!!!\n");
        sscanf(buf,"%d",&tasklet_value);
        return count;
}
/*
** This function will be called when we open the Device file
*/   
static int tasklet_open(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Opened...!!!\n");
        return 0;
}
 
/*
** This function will be called when we close the Device file
*/   
static int tasklet_release(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Closed...!!!\n");
        return 0;
}
/*
** This function will be called when we read the Device file
*/  
static ssize_t tasklet_read(struct file *filp, 
                char __user *buf, size_t len, loff_t *off)
{
        struct irq_desc *desc;
        printk(KERN_INFO "Read function\n");
        desc = irq_to_desc(11);
        if (!desc)
        {
            return -EINVAL;
        }
        __this_cpu_write(vector_irq[59], desc);
        asm("int $0x3B");  // Corresponding to irq 11
        return 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t tasklet_write(struct file *filp, 
                const char __user *buf, size_t len, loff_t *off)
{
        printk(KERN_INFO "Write Function\n");
        return len;
}
 
/*
** Module Init function
*/ 
static int __init tasklet_driver_init(void)
{
        /*Allocating Major number*/
        if((alloc_chrdev_region(&dev, 0, 1, "tasklet_Dev_dynamic")) <0){
                printk(KERN_INFO "Cannot allocate major number\n");
                return -1;
        }
        printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
 
        /*Creating cdev structure*/
        cdev_init(&tasklet_cdev,&fops);
 
        /*Adding character device to the system*/
        if((cdev_add(&tasklet_cdev,dev,1)) < 0){
            printk(KERN_INFO "Cannot add the device to the system\n");
            goto r_class;
        }
 
        /*Creating struct class*/
        if((dev_class = class_create(THIS_MODULE,"tasklet_class_dynamic")) == NULL){
            printk(KERN_INFO "Cannot create the struct class\n");
            goto r_class;
        }
 
        /*Creating device*/
        if((device_create(dev_class,NULL,dev,NULL,"tasklet_device_dynamic")) == NULL){
            printk(KERN_INFO "Cannot create the Device 1\n");
            goto r_device;
        }
 
        /*Creating a directory in /sys/kernel/ */
        kobj_ref = kobject_create_and_add("tasklet_sysfs_dynamic",kernel_kobj);
 
        /*Creating sysfs file for tasklet_value*/
        if(sysfs_create_file(kobj_ref,&tasklet_attr.attr)){
                printk(KERN_INFO"Cannot create sysfs file......\n");
                goto r_sysfs;
        }
        if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "tasklet_device_dynamic", (void *)(irq_handler))) {
            printk(KERN_INFO "tasklet_device_dynamic: cannot register IRQ ");
            goto irq;
        }
        /* Init the tasklet bt Dynamic Method */
        dynamic_tasklet  = kmalloc(sizeof(struct tasklet_struct),GFP_KERNEL);
        if(dynamic_tasklet == NULL) {
            printk(KERN_INFO "tasklet_device_dynamic: cannot allocate Memory");
            goto irq;
        }
        tasklet_init(dynamic_tasklet,tasklet_fn,0);
 
        printk(KERN_INFO "Device Driver Insert...Done!!!\n");
        return 0;
 
irq:
        free_irq(IRQ_NO,(void *)(irq_handler));
 
r_sysfs:
        kobject_put(kobj_ref); 
        sysfs_remove_file(kernel_kobj, &tasklet_attr.attr);
 
r_device:
        class_destroy(dev_class);
r_class:
        unregister_chrdev_region(dev,1);
        cdev_del(&tasklet_cdev);        
        return -1;
}
/*
** Module exit function
*/ 
static void __exit tasklet_driver_exit(void)
{
        /* Kill the Tasklet */ 
        tasklet_kill(dynamic_tasklet);
        if(dynamic_tasklet != NULL)
        {
          kfree(dynamic_tasklet);
        }
        free_irq(IRQ_NO,(void *)(irq_handler));
        kobject_put(kobj_ref); 
        sysfs_remove_file(kernel_kobj, &tasklet_attr.attr);
        device_destroy(dev_class,dev);
        class_destroy(dev_class);
        cdev_del(&tasklet_cdev);
        unregister_chrdev_region(dev, 1);
        printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
 
module_init(tasklet_driver_init);
module_exit(tasklet_driver_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple device driver - Tasklet Dynamic");
MODULE_VERSION("1.16");

reference

Linux Device Driver Tutorial – ch20~21

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入分析Linux内核源码 前言 第一章 走进linux 1.1 GNU与Linux的成长 1.2 Linux的开发模式和运作机制 1.3走进Linux内核 1.3.1 Linux内核的特征 1.3.2 Linux内核版本的变化 1.4 分析Linux内核的意义 1.4.1 开发适合自己的操作系统 1.4.2 开发高水平软件 1.4.3 有助于计算机科学的教学和科研 1.5 Linux内核结构 1.5.1 Linux内核在整个操系统中的位置 1.5.2 Linux内核的作用 1.5.3 Linux内核的抽象结构 1.6 Linux内核源代码 1.6.1 多版本的内核源代码 1.6.2 Linux内核源代码的结构 1.6.3 从何处开始阅读源代码 1.7 Linux内核源代码分析工具 1.7.1 Linux超文本交叉代码检索工具 1.7.2 Windows平台下的源代码阅读工具Source Insight 第二章 Linux运行的硬件基础 2.1 i386的寄存器 2.1.1通用寄存器 2.1.2段寄存器 2.1.3状态和控制寄存器 2.1.4 系统地址寄存器 2.1.5 调试寄存器和测试寄存器 2.2 内存地址 2.3 段机制和描述符 2.3.1 段机制 2.3.2 描述符的概念 2.3.3系统段描述符 2.3.4 描述符表 2.3.5 选择符与描述符表寄存器 2.3.6 描述符投影寄存器 2.3.7 Linux中的段 2.4 分页机制 2.4.1 分页机构 2.4.2页面高速缓存 2.5 Linux中的分页机制 2.5.1 与页相关的数据结构及宏的定义 2.5.2 对页目录及页表的处理 2.6 Linux中的汇编语言 2.6.1 AT&T与Intel汇编语言的比较 2.6.2 AT&T汇编语言的相关知识 2.6.3 Gcc嵌入式汇编 2.6.4 Intel386汇编指令摘要 第三章中断机制 3.1 中断基本知识 3.1.1 中断向量 3.1.2 外设可屏蔽中断 3.1.3异常及非屏蔽中断 3.1.4中断描述符表 3.1.5 相关汇编指令 3.2中断描述符表的初始化 3.2. 1 外部中断向量的设置 3.2.2中断描述符表IDT的预初始化 3.2.3 中断向量表的最终初始化 3.3异常处理 3.3.1 在内核栈中保存寄存器的值 3.3.2 中断请求队列的初始化 3.3.3中断请求队列的数据结构 3.4 中断处理 3.4.1中断和异常处理的硬件处理 3.4.2 Linux对异常和中断的处理 3.4.3 与堆栈有关的常量、数据结构及宏 3.4.4 中断处理程序的执行 3.4.5 从中断返回 3.5中断的后半部分处理机制 3.5.1 为什么把中断分为两部分来处理 3.5.2 实现机制 3.5.3数据结构的定义 3.5.4 软中断、bh及tasklet的初始化 3.5.5后半部分的执行 3.5.6 把bh移植到tasklet 第四章 进程描述 4.1 进程和程序(Process and Program) 4.2 Linux中的进程概述 4.3 task_struct结构描述 4.4 task_struct结构在内存中的存放 4.4.1 进程内核栈 4.4.2 当前进程(current宏) 4.5 进程组织的方式 4.5.1哈希表 4.5.2双向循环链表 4.5.3 运行队列 4.5.4 等待队列 4.6 内核线程 4.7 进程的权能 4.8 内核同步 4.8.1信号量 4.8.2原子操作 4.8.3 自旋锁、读写自旋锁和大读者自旋锁 4.9 本章小节 第五章进程调度 5.1 Linux时间系统 5.1.1 时钟硬件 5.1.2 时钟运作机制 5.1.3 Linux时间基准 5.1.4 Linux的时间系统 5.2 时钟中断 5.2.1 时钟中断的产生 5.2.2.Linux实现时钟中断的全过程 5.3 Linux的调度程序-Schedule( ) 5.3.1 基本原理 5.3.2 Linux进程调度时机 5.3.3 进程调度的依据 5.3.4 进程可运行程度的衡量 5.3.5 进程调度的实现 5.4 进程切换 5.4.1 硬件支持 5.4.2 进程切换 第六章 Linux内存管理 6.1 Linux的内存管理概述 6.1.1 Linux虚拟内存的实现结构 6.1.2 内核空间和用户空间 6.1.3 虚拟内存实现机制间的关系 6.2 Linux内存管理的初始化 6.2.1 启用分页机制 6.2.2 物理内存的探测 6.2.3 物理内存的描述 6.2.4 页面管理机制的初步建立 6.2.5页表的建立 6.2.6内存管理区 6.3 内存的分配和回收 6.3.1 伙伴算法 6.3.2 物理页面的分配和释放 6.3.3 Slab分配机制 6.4 地址映射机制 6.4.1 描述虚拟空间的数据结构 6.4.2 进程的虚拟空间 6.4.3 内存映射 6.5 请页机制 6.5.1 页故障的产生 6.5.2 页错误的定位 6.5.3 进程地址空间中的缺页异常处理 6.5.4 请求调页 6.5.5 写时复制 6.6 交换机制 6.6.1 交换的基本原理 6.6.2 页面交换守护进程kswapd 6.6.3 交换空间的数据结构 6.6.4 交换空间的应用 6.7 缓存和刷新机制 6.7.1 Linux使用的缓存 6.7.2 缓冲区高速缓存 6.7.3 翻译后援存储器(TLB) 6.7.4 刷新机制 6.8 进程的创建和执行 6.8.1 进程的创建 6.8.2 程序执行 6.8.3 执行函数 第七章 进程间通信 7.1 管道 7.1.1 Linux管道的实现机制 7.1.2 管道的应用 7.1.3 命名管道(FIFO) 7.2 信号(signal) 7.2.1 信号种类 7.2.2 信号掩码 7.2.3 系统调用 7.2.4 典型系统调用的实现 7.2.5 进程与信号的关系 7.2.6 信号举例 7.3 System V 的IPC机制 7.3.1 信号量 7.3.2 消息队列 7.3.3 共享内存 第八章 虚拟文件系统 8.1 概述 8.2 VFS中的数据结构 8.2.1 超级块 8.2.2 VFS的索引节点 8.2.3 目录项对象 8.2.4 与进程相关的文件结构 8.2.5 主要数据结构间的关系 8.2.6 有关操作的数据结构 8.3 高速缓存 8.3.1 块高速缓存 8.3.2 索引节点高速缓存 8.3.3 目录高速缓存 8.4 文件系统的注册、安装与拆卸 8.4.1 文件系统的注册 8.4.2 文件系统的安装 8.4.3 文件系统的卸载 8.5 限额机制 8.6 具体文件系统举例 8.6.1 管道文件系统pipefs 8.6.2 磁盘文件系统BFS 8.7 文件系统的系统调用 8.7.1 open 系统调用 8.7.2 read 系统调用 8.7.3 fcntl 系统调用 8 .8 Linux2.4文件系统的移植问题 第九章 Ext2文件系统 9.1 基本概念 9.2 Ext2的磁盘布局和数据结构 9.2.1 Ext2的磁盘布局 9.2.2 Ext2的超级块 9.2.3 Ext2的索引节点 9.2.4 组描述符 9.2.5 位图 9.2.6 索引节点表及实例分析 9.2.7 Ext2的目录项及文件的定位 9.3 文件的访问权限和安全 9.4 链接文件 9.5 分配策略 9.5.1 数据块寻址 9.5.2 文件的洞 9.5.3 分配一个数据块 第十章 模块机制 10.1 概述 10.1.1 什么是模块 10.1.2 为什么要使用模块? 10.2 实现机制 10.2.1 数据结构 10.2.2 实现机制的分析 10.3 模块的装入和卸载 10.3.1 实现机制 10.3.2 如何插入和卸载模块 10.4 内核版本 10.4.1 内核版本与模块版本的兼容性 10.4.2 从版本2.0到2.2内核API的变化 10.4.3 把内核2.2移植到内核2.4 10.5 编写内核模块 10.5.1 简单内核模块的编写 10.5.2 内核模块的Makefiles文件 10.5.3 内核模块的多个文件 第十一章 设备驱动程序 11.1 概述 11.1.1 I/O软件 11.1.2 设备驱动程序 11.2 设备驱动基础 11.2.1 I/O端口 11.2.2 I/O接口及设备控制器 11.2.3 设备文件 11.2.4 VFS对设备文件的处理 11.2.5 中断处理 11.2.6 驱动DMA工作 11.2.7 I/O 空间的映射 11.2.8 设备驱动程序框架 11.3 块设备驱动程序 11.3.1 块设备驱动程序的注册 11.3.2 块设备基于缓冲区的数据交换 11.3.3 块设备驱动程序的几个函数 11.3.4 RAM 盘驱动程序的实现 11.3.5 硬盘驱动程序的实现 11.4 字符设备驱动程序 11.4.1 简单字符设备驱动程序 11.4.2 字符设备驱动程序的注册 11.4.3 一个字符设备驱动程序的实例 11.4.4 驱动程序的编译与装载 第十二章 网络 12.1 概述 12.2 网络协议 12.2.1 网络参考模型 12.2.2 TCP/IP 协议工作原理及数据流 12.2.3 Internet 协议 12.2.4 TCP协议 12.3 套接字(socket) 12.3.1 套接字在网络中的地位和作用 12.3.2 套接字接口的种类 12.3.3 套接字的工作原理 12.3.4 socket 的通信过程 12.3.5 socket为用户提供的系统调用 12.4 套接字缓冲区(sk_buff) 12.4.1 套接字缓冲区的特点 12.4.2 套接字缓冲区操作基本原理 12.4.3 sk_buff数据结构的核心内容 12.4.4 套接字缓冲区提供的函数 12.4.5 套接字缓冲区的上层支持例程 12.5 网络设备接口 12.5.1 基本结构 12.5.2 命名规则 12.5.3 设备注册 12.5.4 网络设备数据结构 12.5.5 支持函数 第十三章 启动系统 13.1 初始化流程 13.1.1 系统加电或复位 13.1.2 BIOS启动 13.1.3 Boot Loader 13.1.4 操作系统的初始化 13.2 初始化的任务 13.2.1 处理器对初始化的影响 13.2.2 其他硬件设备对处理器的影响 13.3 Linux 的Boot Loarder 13.3.1 软盘的结构 13.3.2 硬盘的结构 13.3.3 Boot Loader 13.3.4 LILO 13.3.5 LILO的运行分析 13.4 进入操作系统 13.4.1 Setup.S 13.4.2 Head.S 13.5 main.c中的初始化 13.6 建立init进程 13.6.1 init进程的建立 13.6.2 启动所需的Shell脚本文件 附录: 1 Linux 2.4内核API 2.1 驱动程序的基本函数 2.2 双向循环链表的操作 2.3 基本C库函数 2.4 Linux内存管理中Slab缓冲区 2.5 Linux中的VFS 2.6 Linux的连网 2.7 网络设备支持 2.8 模块支持 2.9 硬件接口 2.10 块设备 2.11 USB 设备 2 参考文献
Linux 内核中,Tasklet 是一种延迟执行的机制,可以在中断上下文中执行,用于处理一些低优先级的事件。Tasklet 可以在中断处理程序执行完毕后,而不需要立即执行,从而避免了在中断上下文中进行的复杂操作,使得中断处理程序可以快速返回。 Tasklet 与软中断的工作方式类似,但是 Tasklet 只能在进程上下文中执行,而不能在硬件中断上下文中执行。Tasklet 通常用于处理一些低优先级的事件,比如网络数据包的到达、定时器的到期等。 下面是 Tasklet 的使用步骤: 1. 定义 Tasklet 函数: ```c void my_tasklet_handler(unsigned long data); DECLARE_TASKLET(my_tasklet, my_tasklet_handler, 0); ``` 2. 实现 Tasklet 函数: ```c void my_tasklet_handler(unsigned long data) { // 处理事件 // ... } ``` 3. 注册 Tasklet: ```c int ret = request_irq(irq, my_interrupt_handler, IRQF_SHARED, "my_interrupt", (void *)(my_interrupt_handler)); if (ret) { printk(KERN_INFO "request_irq failed\n"); return -1; } tasklet_init(&my_tasklet, my_tasklet_handler, 0); ``` 4. 触发 Tasklet: ```c void my_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs) { // 处理中断事件 // ... // 触发 Tasklet tasklet_schedule(&my_tasklet); } ``` 总的来说,Tasklet 是一种非常有效的机制,可以在中断上下文中处理低优先级的事件,避免在中断上下文中进行复杂的操作,提高系统的效率和可靠性。在处理一些低优先级的事件时,可以使用 Tasklet 来优化系统的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值