Linux驱动开发笔记:设备驱动中的阻塞和同步机制

本文详细介绍了阻塞和非阻塞的概念及其在进程管理中的应用,包括等待队列的实现方式及如何通过内核机制进行进程间的同步。同时,还提供了具体的系统调用示例,帮助读者深入理解这些概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 阻塞和非阻塞

阻塞调用:调用结果返回之前,当前线程被挂起,函数只有在得到结果之后才会返回

非阻塞调用:在不能立即得到结果之前,该函数不会阻塞当前进程,而会立即返回

2 等待队列

阻塞进程可以使用等待队列来实现

等待队列的基本数据结构是一个双向链表,存储睡眠的进程

2.1 等待队列的实现

等待队列定义:

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};

typedef struct __wait_queue_head wait_queue_head_t;

(1)lock

功能:对task_list起保护作用

当要向task_list链表中加入或删除元素时,内核内部会锁定lock锁,当修改完成后,会释放lock锁

(2)task_list

双向链表,存放等待的进程

2.2 等待队列的作用

(1)定义和初始化等待队列头

定义:

struct wait_queue_head_t wait;

初始化定义:

#define DECLARE_WAIT_QUEUE_HEAD(name) \
    wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

(2)定义等待队列

#define DECLARE_WAITQUEUE(name, tsk) \
    wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

(3)添加和移除等待队列

// 将等待队列元素wait添加到等待队列头q所指向的等待队列链表中
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

// 将队列元素wait从等待队列头q所指向的等待队列链表中移除
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

(4)等待事件

#define wait_event(wq, condition)
#define wait_event_timeout(wq, condition, ret)
#define wait_event_interruptible(wq, condition, ret)
#define wait_event_interruptible_timeout(wq, condition, ret)
  • wait_event:在等待队列中睡眠直到condition为真。等待期间,进程被置为TASK_UNINTERRUPTIBLE,进入睡眠,直到condition为真。
  • wait_event_timeout:与wait_event类似,但如果睡眠时间为负数,则立即返回。如果在睡眠期间被唤醒,且condition为真,则返回剩余睡眠时间,否则继续睡眠直到到达给定时间,返回0。
  • wait_event_interruptible:调用该宏在等待过程中当前进程会被设置成TASK_INTERRUPTIBLE。每次被唤醒时,查询condition是否为真,如果为真返回0,否则如果是被信号唤醒,返回-ERESTARTSYS。
  • wait_event_interruptible_timeout:与wait_event_interruptible类似,如果在睡眠时被信号打断,则返回-ERESTARTSYS。

(5)唤醒等待队列

#define wake_up(x)  __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_interruptible(x)  __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
  • wake_up:可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程,与wait_event、wait_event_timeout成对使用。
  • wake_up_interruptible:只能唤醒TASK_INTERRUPTIBLE状态的进程。可以唤醒wait_event_interruptible、wait_event_interruptible_timeout睡眠的进程。

3 同步机制实验

3.1 同步机制设计

进程同步机制设计首先需要一个等待队列,所有等待一个事件完成的进程都挂在这个等待队列中:

struct CustomEvent{
    int eventNum;                 // 事件号
    wait_queue_head_t *p;         // 系统等待队列首指针
    struct CustomEvent *next;     // 队列链指针
};

为了实现实验的意图,设计两个指针分别表示事件头部和尾部:

CustomEvent *lpevent_head = NULL;  // 链头指针
CustomEvent *lpevent_end = NULL;   // 链尾指针

每个事件由一个链表组成,每个链表中包含了等待这个事件的等待队列:

FindEventNum()从一个事件链表中找到某一个事件对应的链表:

// 参数1:事件序号  参数2:返回事件的前一个事件
CustomEvent *FindEventNum(int eventNum, CustomEvent **prev)
{
    CustomEvent *tmp = lpevent_head;   // 事件链表的头部
    *prev = NULL;
    while(tmp)      // 找到所要事件的结构体指针
    {
        if(tmp->eventNum == eventNum) {   // tmp所指的事件号是否与eventNum相同
            return tmp;   // 找到返回
        }
        *prev = tmp;
        tmp=tmp->next;
    }
    return NULL;
}

系统调用函数sys_CustomEvent_open():新分配一个事件,并返回新分配事件的事件号。

// 建立一个新的事件  eventNum:事件号
asmlinkage int sys_CustomEvent_open(int eventNum)
{
    // 定义两个事件指针
    CustomEvent *new;
    CustomEvent *prev;

    if(eventNum) {   // 判断事件号是否为0
        if(!FindEventNum(eventNum, &prev)) {   // 根据事件号查找事件
            return -1;
        } else {
            return eventNum;
        }
    } else {    // 重新创建一个事件
        new = (CustomEvent *)kmalloc(sizeof(CustomEvent), GFP_KERNEL);  // 分配一个新事件
        new—>p = (wait_queue_head_t *)kmalloc(sizeof(wait_queue_head_t),  // 分配该事件的等待队列
                                             GFP_KERNEL);
        new->next = NULL;
        new—>p->task_list.next = &new—>p—>task_list;
        new—>p->task_list.prev = &new—>p—>task_list;
        if(!lpevent_head) {     // 如果没有事件链表头 将新分配的事件赋给事件链表头
            new->eventNum = 2;
            lpevent_head = lpevent_end = new;
            return new->eventNum;
        } else {      // 已有事件链表头 将新分配的事件连接到链表中
            new->eventNum = lpevent_end->eventNum + 2;
            lpevent_end—>next = new;
            1pevent_end = new;
        }
        return new->eventNum;
    }
    return 0;

}

将进程阻塞到一个事件的系统调用函数,直到等待事件被唤醒时,事件才退出。

// 实现等待队列系统调用
asmlinkage int sys_CustomEvent_wait(int eventNum)
{
    // 定义两个事件指针
    CustomEvent *tmp;
    CustomEvent *prev = NULL;

    if((tmp = FindEventNum(eventNum, &prev)) != NULL) {   // 查找事件结构体
        DEFINE_WAIT(wait);   // 定义并初始化一个等待队列wait_queue_head
        prepare_to_wait(tmp->p, &wait, TASK_INTERRUPTIBLE);  // 将当前进程放入等待队列中
        schedule();    // 重新调度
        finish_wait(tmp->p, &wait);   // 进程被唤醒 从阻塞队列中退出
        return eventNum;
    } 
    return -1;

}

唤醒等待特定事件函数:

// 唤醒等待事件
asmlinkage int sys_CustomEvent_signal(int eventNum)
{
    // 定义两个事件指针
    CustomEvent *tmp = NULL;
    CustomEvent *prev = NULL;

    if(!(tmp = FindEventNum(eventNum, &prev))) {   
        return 0;    // 没有发现事件
    }
    wake_up(tmp->p);  // 唤醒队列上所有进程
    return 1;

}

关闭事件函数:先唤醒事件上的等待队列,然后清除事件占用空间

// 唤醒等待事件
asmlinkage int sys_CustomEvent_close(int eventNum)
{
    // 定义两个事件指针
    CustomEvent *prev= NULL;
    CustomEvent *releaseItem= NULL;

    if(releaseItem = tmp = FindEventNum(eventNum, &prev)) {   // 找到关闭事件
        if(releaseItem == lpevent_end) {   // 链表中最后一个事件
            lpevent_end = prev;
        } else if(releaseItem == lpevent_head) {   // 链表中第一个事件
            lpevent_head = lpevent_head->next;
        } else {
            prev->next = releaseItem->next;
        }
        sys_CustomEvent_signal(eventNum);   // 唤醒需要关闭的事件
        if(releaseNum) {
            kfree(releaseNum);
        }
        return releaseItem;
    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值