内核中的同步—内核多任务并发实例

内核任务是指在内核态执行的任务,具体包括内核线程、系统调用、中断处理程序、下半部任务等几类。

一、内核任务及其并发关系

在下面实例中,涉及如下三种内核任务,分别是系统调用、内核线程和定时器任务队列。

1、系统调用:是用户程序通过门机制来进入内核执行的内核例程,它运行在内核态,处于进程上下文中,可以认为是代表用户进程的内核任务,因此具有用户态任务的特性。

2、内核线程:内核线程可以理解成在内核中运行的特殊进程,它有自己的进程上下文,所以同样被进程调度程序调度,也可以睡眠。不同之处就在于内核线程运行于内核空间,可访问内核数据,运行期间不能被抢占。

3、定时器任务队列:它属于下半部,在每次产生时钟节拍时得到处理。

上述三种内核任务存在如下竞争关系:系统调用和内核线程可能和各种内核任务并发执行,除了中断可以抢占它、产生并发外,它们还有可能自发地主动睡眠,放弃处理器,从而其他任务被重新调度,所以系统调用和内核线程除与定时器任务队列发生竞争,也会与其他系统调用和内核线程发生竞争。

二、问题描述

假设存在一个内核共享资源——链表(mine),有多个内核任务并发访问链表:200个内核线程sharelist向链表中加入新结点;内核定时器qt_task定时删除结点;系统调用share_exit销毁链表。这三种内核任务并发执行时,有可能破坏链表数据的完整性,所以必须对链表进行同步访问保护,以保证数据的一致性。

三、实现机制

1、驱动文件代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/sched.h> 
#include <linux/err.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/semaphore.h>

#define NTHREADS 200                                        /* 线程数 */

typedef struct my_struct {
    struct list_head list;
    int id;
    int pid;
}my_struct_t;


static struct work_struct queue;                            /* 定义工作队列 */
static struct task_struct *sharelist_task = NULL;
static struct timer_list timer;                                /* 定时器队列 */
static struct semaphore sem;                                /* 内核线程进行同步的信号量 */
static spinlock_t lock;                                           /* 保护对链表的操作 */
static LIST_HEAD(mine);                                        /* sharelist头 */
static unsigned int list_len = 0;
static atomic_t my_count = ATOMIC_INIT(0);                    /* 以原子方式进行追加 */
static long count = 0;                                        /* 行计数器,每行打印4个信息 */

static void qt_task(unsigned long data);
static void kthread_launcher(struct work_struct *q);        /* 创建内核线程 */
static void start_kthread(void);                            /* 调度内核线程 */
static int sharelist(void *data);                            /* 从共享链表增删结点的线程 */

/**
*
*    该函数通过kthread_create方法创建内核线程sharelist
*
*/
static void kthread_launcher(struct work_struct *q)
{
    sharelist_task = kthread_create(sharelist, NULL, "sharelist_task");/* 创建内核线程sharelist */
    if(IS_ERR(sharelist_task)){
        printk(KERN_ERR "Unable to create sharelist thread.\n");
        sharelist_task = NULL;
        return;
    }
    up(&sem);
}


/**
*
*    该函数负责调度内核线程
*    创建内核线程kthread_launcher的任务挂载工作队列上,也就是
*    说该任务受内核中默认的工作者线程events调度。
*    备注:使用信号量同步机制保证了串行地创建内核线程,虽然串行
*    并非必需。
*/
static void start_kthread(void)
{
    down(&sem);
    schedule_work(&queue);                                    /* 调度工作队列 */
}


/**
*
*    对共享链表操作的内核线程
*    注意:为防止定时器任务队列抢占执行时造成链表的数据不一致,需要
*    在操作链表期间进行同步保护。
*/
static int sharelist(void *data)
{
    struct my_struct *p;
    
    if(count++ % 4 == 0)
        printk("\n");
    
    spin_lock(&lock);                                        /* 添加锁,保护共享资源 */
    if(list_len < 100) {
        p = kzalloc(sizeof(struct my_struct), GFP_KERNEL);
        if(!p)
            return -ENOMEM;
        
        p->id = atomic_read(&my_count);                        /* 原子变量操作 */
        atomic_inc(&my_count);
        p->pid = current->pid;
        list_add(&p->list, &mine);                            /* 向队列中添加新结点 */    
        list_len++;
        printk(KERN_INFO "thread add: %-5d\t", p->id);
    } else {                                                /* 队列超过定长则删除结点 */
        struct my_struct *my = NULL;
        my = list_entry(mine.prev, struct my_struct, list);
        list_del(mine.prev);                                /* 从队列尾部删除结点 */
        list_len--;
        printk(KERN_INFO "thread del: %-5d\t", my->id);
        kfree(my);
    }
    spin_unlock(&lock);
    
    return 0;
}

/**
*
*    删除结点的定时器任务
*
*/
static void qt_task(unsigned long data)
{
    
    if(!list_empty(&mine)) {
        struct my_struct *my = NULL;
        if(count++ % 4 == 0)
            printk("\n");
        
        my = list_entry(mine.next, struct my_struct, list); /* 取下一个结点 */
        list_del(mine.next);                                /* 删除结点 */
        list_len--;
        printk(KERN_INFO "timer del: %-5d\t", my->id);
        kfree(my);
    }
    
    mod_timer(&timer, jiffies + 1);                            /* 修改定时器时间:从现在开始1个节拍 */
}

static int __init share_init(void)
{
    int i;
    
    printk(KERN_INFO "share_init.\n");
    spin_lock_init(&lock);
    INIT_WORK(&queue, kthread_launcher);                    /* 初始化工作队列 */
    sema_init(&sem, 1);                                        /* 初始化信号量 */
    setup_timer(&timer, qt_task, 0);                        /* 设置定时器 */
    add_timer(&timer);                                        /* 添加定时器 */
    for(i = 0; i < NTHREADS; i++)
        start_kthread();                                    /* 再启动200个内核线程来添加结点 */
    
    wake_up_process(sharelist_task);
    
    return 0;
    
}

/**
*
*    负责销毁链表
*    注意:销毁时内核线程与定时器任务都在运行,应该进行同步保护,即锁住链表,通过
*    自旋锁机制达到的,因为自旋锁保证了任务执行的串行化,此刻其他任务就没有机会执
*    行了。当重新打开自旋锁时,其他任务就可以运行。
*/
static void __exit share_exit(void)
{
    struct list_head *n, *p = NULL;
    struct my_struct *my = NULL;
    
    printk(KERN_INFO "share_exit.\n");
    if(sharelist_task){
        kthread_stop(sharelist_task);
        sharelist_task = NULL;
    }
    del_timer(&timer);
    spin_lock(&lock);
    list_for_each_safe(p, n, &mine) { /* 删除所有结点,销毁链表 */
        if(count++ % 4 == 0)
            printk("\n");
        
        my = list_entry(p, struct my_struct, list);
        list_del(p);
        printk(KERN_INFO "syscall del: %d\t", my->id);
        kfree(my);
    }
    spin_unlock(&lock);
    printk(KERN_INFO "Over\n");
}


module_init(share_init);
module_exit(share_exit);

MODULE_LICENSE("GPL");
MODULE_VERSION("v1.0");
MODULE_AUTHOR("xz@vichip.com.cn");
2、Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
#$(warning "11111111111111111111111)
all:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
else
        obj-m := test1.o

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值