阻塞、非阻塞、poll机制、异步详解

1. 阻塞

阻塞是一种同步机制,当进程需要等待某个条件满足时,会进入休眠状态,直到条件满足时被唤醒。在Linux内核中,阻塞机制主要通过等待队列(wait_queue)实现。

初始化等待队列:

数据类型:wait_queue_head_t

初始化:#define init_waitqueue_head(q)

内部调用__init_waitqueue_head,需要传递一个等待队列头结构体指针。

struct __wait_queue_head {

    spinlock_t lock;

    struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;

extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);

#define init_waitqueue_head(q) \do { \

    static struct lock_class_key __key; \

    __init_waitqueue_head((q), #q, &__key); \} while (0)

休眠等待:

wait_event:wait_event(wq, condition)

wq:等待队列

condition:条件唤醒,1时结束等待,0时继续等待

wait_event_interruptible:类似wait_event,但可以被信号中断

#define wait_event(wq, condition) \do { \

    if (condition) \

        break; \

    wait_event_begin(wq, condition, TASK_UNINTERRUPTIBLE, 0); \} while (0)

#define wait_event_interruptible(wq, condition) \({ \

    int __ret = 0; \

    if (!(condition)) \

        __ret = wait_event_interruptible_begin(wq, condition, TASK_INTERRUPTIBLE, 0); \

    unlikely(__ret); \})

唤醒进程:

wake_up_interruptible:#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

wake_up:#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)

x:等待队列,需要传递一个等待队列指针

#define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)

注意事项:

1.sleep_on方式需要wake_up唤醒,因为sleep_on设置进程状态为TASK_UNINTERRUPTIBLE。

2.wait_event与wait_event_interruptible在唤醒时需要设置condition为1(非0),否则不能唤醒进程。

3.wake_up_interruptible不能唤醒wait_event休眠的进程,wait_event设置状态为TASK_UNINTERRUPTIBLE,而wait_event_interruptible设置状态为TASK_INTERRUPTIBLE。

4.wake_up可以唤醒TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE,因为TASK_NORMAL == TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE。

5.建议使用wait_event_interruptible休眠方式

实例:

hello.c

#include <linux/module.h>

#include <linux/kdev_t.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/uaccess.h>

#include <linux/device.h>

#include <linux/semaphore.h>

#include <linux/wait.h>

#include <linux/sched.h>

#include "common.h"

#define SIZE 100

int g_major = 0;

int g_min   = 0;

int g_iDevNo = 0;

int g_iCount = 2;

struct cdev *g_pDev = NULL;

char data[SIZE] = "cc pp hh xx cq cf jh zy";

int g_condition = 0; //g_condition:条件唤醒,1时结束等待 ,0 :继续等待

struct class *g_pClass = NULL;

wait_queue_head_t g_queue; //初始化等待队列

int HelloOpen (struct inode *pNode, struct file *pFile)

{

 printk("hello open enter\r\n");

 printk("major %d, minor %d\r\n", MAJOR(pNode->i_rdev), MINOR(pNode->i_rdev));

 printk("hello open exit\r\n");

 g_condition = 0; //0 :继续等待

 return 0;

}

int HelloClose(struct inode *pNode, struct file *pFile)

{

 printk("hello close enter\r\n");

 printk("major %d, minor %d\r\n", MAJOR(pNode->i_rdev), MINOR(pNode->i_rdev));

 printk("hello close exit\r\n");

 g_condition = 0; //0 :继续等待

 return 0;

}

ssize_t HelloRead(struct file *pFile, char __user *buf, size_t count, loff_t *pos)

{

 printk("hello read enter\r\n");

 wait_event_interruptible(g_queue, g_condition); //休眠等待

 if (count > SIZE - 1)

 {

  count = SIZE - 1;

 }

 if (copy_to_user(buf, data, count))

 {

  printk("copy to user error\r\n");

  return -EINVAL;

 }

 printk("hello read exit\r\n");

 return count;

}

ssize_t HelloWrite(struct file *pFile, const char __user *buf, size_t count, loff_t *pos)

{

 printk("hello write enter\r\n");

 if (count > SIZE -1)

 {

  count = SIZE -1;

 }

 if (copy_from_user(data, buf, count))

 {

  return -EINVAL;

 }

 printk("data is %s\r\n", data);

 g_condition = 1;  //1时结束等待

 wake_up(&g_queue);  //唤醒进程

 printk("hello write exit\r\n");

 return count;

}

long HelloIOctl(struct file *pFile, unsigned int cmd, unsigned long arg)

{

 printk("hello io ctl enter\r\n"); 

 switch (cmd)

 {

  case TEST_CMD:

  {

   printk("test cmd\r\n");

  }

  break;

  case TEST_CMD1:

  {

   printk("test cmd1\r\n");

  }

  break;

  case TEST_CMD2:

  {

   printk("test cmd2 %ld\r\n", arg);

  }

  break;

  default:

  {

   printk("command error\r\n");

  }

 }

 printk("hello io ctl exit\r\n"); 

 return 0;

}

struct file_operations g_stFileOp =

{

 .owner = THIS_MODULE,

 .open  = HelloOpen,

 .release = HelloClose,

 .read  = HelloRead,

 .write = HelloWrite,

 .unlocked_ioctl = HelloIOctl,

};

int HelloModuleInit(void)

{

 int iRet = -1;

 int i = 0;

 struct device *pDevTmp = NULL;

 printk("hello module enter\r\n");

 //register dev no

 iRet = alloc_chrdev_region(&g_iDevNo,0, g_iCount, "hehe");

 if (iRet != 0)

 {

  printk("register dev error\r\n");

  return -1;

 }

 g_major = MAJOR(g_iDevNo);

 printk("major %d, devno %d\r\n", MAJOR(g_iDevNo), g_iDevNo);

 //alloc char dev

 g_pDev = cdev_alloc();

 if (!g_pDev)

 {

  printk("cdev alloc error\r\n");

  goto CDEV_ALLOC_ERROR;

 }

 //init char dev

 cdev_init(g_pDev, &g_stFileOp);

 //add char dev to kernel

 iRet = cdev_add(g_pDev, g_iDevNo, g_iCount);

 if (iRet != 0)

 {

  printk("cdev add error\r\n");

  goto CDEV_ADD_ERROR;

 }

  

 //create class

 g_pClass = class_create(THIS_MODULE, "haha");

 if (IS_ERR(g_pClass))

 {

  printk("create class error\r\n");

  goto CDEV_ADD_ERROR;

 }

 i = 0;

 for (i = 0; i < g_iCount; i++)

 {

  pDevTmp = device_create(g_pClass, NULL, MKDEV(g_major, i), NULL, "haha%d", i);

  if (IS_ERR(pDevTmp))

  {

   goto DEVICE_CREATE_ERROR;

  }

 }

 //init queue

 init_waitqueue_head(&g_queue); //初始化等待队列头

 printk("hello module exit\r\n"); 

 return 0;

DEVICE_CREATE_ERROR:

 i = 0;

 for (i = 0; i < g_iCount; i++)

 {

  device_destroy(g_pClass, MKDEV(g_major, i));

 }

 class_destroy(g_pClass);

 g_pClass = NULL;

CDEV_ADD_ERROR:

 cdev_del(g_pDev);

 g_pDev = NULL;

CDEV_ALLOC_ERROR:

 unregister_chrdev_region(g_iDevNo, g_iCount);

 return -1;

}

void HelloModuleExit(void)

{

 int i = 0;

 printk("exit  module enter\r\n");

 i = 0;

        for (i = 0; i < g_iCount; i++)

        {

                device_destroy(g_pClass, MKDEV(g_major, i));

        }

        class_destroy(g_pClass);

        g_pClass = NULL;

 cdev_del(g_pDev);

 g_pDev = NULL;

 unregister_chrdev_region(g_iDevNo, g_iCount);

 printk("exit  module exit\r\n");

}

MODULE_LICENSE("GPL");

module_init(HelloModuleInit);

module_exit(HelloModuleExit);

make->sudo insmod hello.ko->lsmod->dmesg | tail

test.c->test_r

gcc -o test_r test.c ->sudo ./test_r dev/haha0

此时阻塞,dmesg | tail:

test1.c->test_w

gcc -o test_w test1.c ->sudo ./test_r dev/haha1

这时haha0阻塞解除

dmesg | tail:

2. 非阻塞

非阻塞是一种异步机制,进程不会因等待某个条件而进入休眠状态,而是立即返回。在应用层需要设置O_NONBLOCK标志,在驱动中可以通过struct file的f_flags成员获取此标志。

int flags = fcntl(fd, F_GETFL, 0);

if (flags & O_NONBLOCK) {// 非阻塞模式}

实例:

hello.c

3. poll

poll机制允许进程在同一时间等待多个文件描述符的事件。在驱动中实现file_operations结构体的poll函数指针,通过poll_wait可以将打开的file结构体和等待队列与传入的poll_table添加到内核中。

poll_wait函数:

static inline void poll_wait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p) {

    if (p && p->qproc) {

        spin_lock_irq(&p->qproc->wait_lock);

        p->qproc->nwait++;

        spin_unlock_irq(&p->qproc->wait_lock);

    }

add_wait_queue(wait_address, &p->wait);}

返回状态:

驱动的poll函数应该返回设备的当前状态,常用的状态值有:

POLLIN:可读

POLLOUT:可写

POLLRDNORM:普通数据可读

POLLERR:错误

static unsigned int device_poll(struct file *filp, poll_table *wait) {

    poll_wait(filp, &device_wait_queue, wait);

    if (device_data_available()) {

        return POLLIN | POLLRDNORM;

    }

return 0;}

4. 异步

异步机制允许设备驱动在事件发生时发送信号通知进程。

在驱动中实现file_operations结构体的fasync函数指针,通过fasync_helper将文件描述符加入到异步队列中。

fasync函数

static int device_fasync(int fd, struct file *filp, int on)

{

return fasync_helper(fd, filp, on, &device_fasync_queue);

}

发送信号

void kill_fasync(struct fasync_struct **fp, int sig, int band)

 {

if (*fp)

 {

        struct fasync_struct *fa = *fp;

        while (fa)

{

            kill_pid_info(sig, 0, fa->pid);

            fa = fa->fa_next;

        }

}

}

通过这些机制,可以在驱动程序中实现高效的同步和异步操作。

fasync_helper() 是 Linux 内核中的一个辅助函数,用于实现文件描述符的异步通知(asynchronous I/O,AIO)。这个函数在处理文件描述符的异步 I/O 时非常有用,它可以将文件描述符加入或移出异步通知队列。当文件描述符的状态发生变化时(例如文件可读或可写),内核会发送信号给相应的进程。

函数原型:

int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp);

参数说明:

fd:文件描述符。这个参数通常是从用户空间传入的。

filp:指向 struct file 的指针。这个结构体表示一个打开的文件,包含了文件的各种属性和操作方法。

on:一个整数,用于控制是否开启或关闭异步通知。如果 on 为 1,则开启异步通知;如果为 0,则关闭异步通知。

fapp:一个指向 struct fasync_struct 指针的指针。这个结构体用于管理异步通知队列。

返回值:

0:操作成功。

-EINVAL:参数无效。

-EBADF:文件描述符无效。

-ENOMEM:内存不足。

-ENOLINK:无异步通知支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值