找回密码
 立即注册
首页 业界区 业界 linux设备驱动阻塞IO应用

linux设备驱动阻塞IO应用

咸和璧 2026-2-16 10:30:02
1. 驱动中阻塞相关函数的基础

1.1 wait_queue_head_t

定义等待队列头
  1. #include <linux/wait.h>
  2. /*
  3. * lock:自旋锁,用于保护队列操作(如添加/删除等待项)的并发安全
  4. * head:链表头,指向等待队列项的链表
  5. */
  6. typedef struct wait_queue_head {
  7.     spinlock_t      lock;
  8.     struct list_head    head;
  9. } wait_queue_head_t;
复制代码
1.2 init_waitqueue_head

初始化一个已经分配了内存的等待队列头,设置其自旋锁和链表为空
  1. void init_waitqueue_head(wait_queue_head_t *q);
复制代码
1.3 DECLARE_WAITQUEUE

静态声明并初始化一个等待队列项(wait queue entry)。该宏创建一个 wait_queue_entry 类型的变量,并将指定的进程描述符 tsk (当前进程为 current )与该队列项关联,同时设置默认的唤醒函数
  1. DECLARE_WAITQUEUE(name, tsk);
  2. // 使用方式
  3. DECLARE_WAITQUEUE(wait, current);
  4. // 展开后为
  5. wait_queue_entry_t wait = {
  6.     .private    = current,                 // 指向等待的进程 task_struct
  7.     .func       = default_wake_function,   // 唤醒时调用的函数
  8.     .task_list  = { NULL, NULL }           // 链表节点,用于挂入等待队列头
  9. };
复制代码
1.4 add_wait_queue

将一个已经初始化好的等待队列项 wait 添加到等待队列头 queue 所管理的队列中。添加后,该队列项就成为了等待队列的一部分
在进程准备睡眠之前,先将自己添加到等待队列,这样其他唤醒者才能找到它
  1. void add_wait_queue(wait_queue_head_t *queue, wait_queue_t *wait);
复制代码
1.5 set_current_state

设置当前进程的状态,将 current->state 赋值为 new_state ,进程状态定义在  中,常见的有:

  • TASK_RUNNING:可运行状态(正在运行或就绪)。
  • TASK_INTERRUPTIBLE:可中断的睡眠状态,可以被信号唤醒。
  • TASK_UNINTERRUPTIBLE:不可中断的睡眠状态,只能由显式唤醒解除。
  1. void set_current_state(int new_state);        // 有内存屏障,保证顺序
  2. void __set_current_state(int new_state);      // 没有内存屏障   
复制代码
1.6 remove_wait_queue

将之前通过 add_wait_queue 添加的等待队列项从等待队列中移除;当进程被唤醒并重新获得 CPU 后,通常需要调用此函数将自己从等待队列中删除,表示不再等待该条件;如果忘记移除,队列项仍留在等待队列中,可能导致后续不必要的唤醒或资源泄漏
  1. void remove_wait_queue(wait_queue_head_t *queue, wait_queue_t *wait);
复制代码
1.7 wake_up

唤醒队列中所有进程(包括 TASK_UNINTERRUPTIBLE和 TASK_INTERRUPTIBLE )
  1. void wake_up(wait_queue_head_t *queue);
复制代码
唤醒等待队列 queue 中所有状态为 TASK_INTERRUPTIBLE 的进程。这些进程将被设置为 TASK_RUNNING 并移入运行队列,等待调度器选择它们运行
  1. void wake_up_interruptible(wait_queue_head_t *queue);
复制代码
2. 阻塞驱动使用例子
  1. #include <linux/init.h>
  2. #include <linux/errno.h>
  3. #include <linux/mm.h>
  4. #include <linux/sched.h>
  5. #include <linux/module.h>
  6. #include <linux/ioctl.h>
  7. #include <linux/io.h>
  8. #include <linux/fs.h>
  9. #include <linux/cdev.h>
  10. #include <linux/uaccess.h>
  11. #include <linux/slab.h>
  12. #include <linux/wait.h>
  13. #define GLOBALFIFO_SIZE    1024
  14. #define GLOBALMEM_MAGIC    'M'
  15. #define MEM_CLEAR        _IO(GLOBALMEM_MAGIC, 0)
  16. struct globalfifo_dev {
  17.     struct cdev m_cdev;            /* 字符设备 */
  18.     unsigned int current_len;        /* fifo有效数据长度 */
  19.     unsigned char mem[GLOBALFIFO_SIZE];    /* 全局内存 */
  20.     struct semaphore sem;            /* 并发控制信号量 */
  21.     wait_queue_head_t r_wait;        /* 阻塞读等待队列头 */
  22.     wait_queue_head_t w_wait;        /* 阻塞写等待队列头 */
  23. };
  24. static int globalfifo_major = 266;
  25. // 存放字符设备私有数据
  26. struct globalfifo_dev* globalfifo_devp;
  27. /* user open fd */
  28. static int globalfifo_open(struct inode* inode, struct file* filp) {
  29.    
  30.     struct globalfifo_dev* dev;
  31.     dev = container_of(inode->i_cdev, struct globalfifo_dev, m_cdev);
  32.     filp->private_data = dev;
  33.     return 0;
  34. }
  35. /* user release fd*/
  36. static int globalfifo_release(struct inode* inode, struct file* filp) {
  37.     return 0;
  38. }
  39. /* user read fd */
  40. static ssize_t globalfifo_read(struct file* filp, char __user* buf, size_t count, loff_t* ppos) {
  41.     int ret;   
  42.     struct globalfifo_dev* dev = filp->private_data;   
  43.    
  44.     // 定义等待队列
  45.     DECLARE_WAITQUEUE(wait, current);   
  46.     down(&dev->sem);
  47.     // 1.将等待队列加入到等待队列头
  48.     add_wait_queue(&dev->r_wait, &wait);
  49.     // 2.循环检查等待条件(防止假唤醒,如果唤醒后不满足条件会再次睡眠)
  50.     while (dev->current_len == 0) {
  51.         // 3. 检查非阻塞模式直接返回
  52.         if (filp->f_flags & O_NONBLOCK) {
  53.             ret = -EAGAIN;
  54.             up(&dev->sem);
  55.             remove_wait_queue(&dev->w_wait, &wait);
  56.             set_current_state(TASK_RUNNING);
  57.             return ret;
  58.         }
  59.         // 4.改变进程状态为可中断睡眠
  60.         __set_current_state(TASK_INTERRUPTIBLE);
  61.         up(&dev->sem);
  62.         // 5.调度其他进程执行(真正睡眠)        
  63.         schedule();
  64.         // 6.检查如果有信号到达返回上层处理错误(自己的唤醒只将状态转换为TASK_RUNNING,但信号到来也会做这个处理)
  65.         if (signal_pending(current)) {
  66.             ret = -ERESTARTSYS;
  67.             remove_wait_queue(&dev->w_wait, &wait);
  68.             set_current_state(TASK_RUNNING);
  69.             return ret;
  70.         }
  71.         
  72.         // 被唤醒后的处理
  73.         down(&dev->sem);
  74.     }
  75.     if(count > dev->current_len)
  76.         count = dev->current_len;
  77.     if(copy_to_user(buf, dev->mem, count)) {
  78.         ret = -EFAULT;
  79.         up(&dev->sem);
  80.         remove_wait_queue(&dev->w_wait, &wait);
  81.            set_current_state(TASK_RUNNING);
  82.         return ret;
  83.     } else {
  84.         memcpy(dev->mem, dev->mem + count, dev->current_len - count);
  85.         dev->current_len -= count;
  86.             
  87.         wake_up_interruptible(&dev->w_wait);    // 读出数据后唤醒写进程
  88.         ret = count;
  89.     }
  90.    
  91.     up(&dev->sem);
  92.     remove_wait_queue(&dev->w_wait, &wait);
  93.     set_current_state(TASK_RUNNING);
  94.     return ret;
  95. }
  96. /* user write fd */
  97. static ssize_t globalfifo_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos) {
  98.     int ret;   
  99.     struct globalfifo_dev* dev = filp->private_data;   
  100.     // 定义等待队列
  101.     DECLARE_WAITQUEUE(wait, current);
  102.     down(&dev->sem);
  103.     // 1.将等待队列插入写等待队列头
  104.     add_wait_queue(&dev->w_wait, &wait);
  105.     // 2.循环等待 若FIFO满则应该挂起
  106.     while (dev->current_len == GLOBALFIFO_SIZE) {
  107.         // 3. 若非阻塞则直接返回
  108.         if (filp->f_flags & O_NONBLOCK) {
  109.             up(&dev->sem);
  110.             ret = -EAGAIN;
  111.             remove_wait_queue(&dev->w_wait, &wait);
  112.             set_current_state(TASK_RUNNING);
  113.             return ret;
  114.         }
  115.         // 4.将进程状态改为可打断睡眠
  116.         __set_current_state(TASK_INTERRUPTIBLE);
  117.         up(&dev->sem);
  118.         // 5.调度其他进程(真正睡眠)
  119.         schedule();
  120.         // 6.若因为信号唤醒,则返回让上层完成错误处理
  121.         if (signal_pending(current)) {
  122.             ret = -ERESTARTSYS;
  123.             remove_wait_queue(&dev->w_wait, &wait);
  124.             set_current_state(TASK_RUNNING);
  125.             return ret;
  126.         }
  127.         down(&dev->sem);
  128.     }
  129.     if (count > GLOBALFIFO_SIZE - dev->current_len)
  130.         count = GLOBALFIFO_SIZE - dev->current_len;
  131.     if (copy_from_user(dev->mem, buf, count)) {
  132.         ret = -EFAULT;
  133.         up(&dev->sem);
  134.         remove_wait_queue(&dev->w_wait, &wait);
  135.         set_current_state(TASK_RUNNING);
  136.         return ret;
  137.     } else {
  138.         dev->current_len += count;
  139.         // 唤醒等待队列
  140.         wake_up_interruptible(&dev->r_wait);
  141.         ret = count;   
  142.     }
  143.     up(&dev->sem);
  144.     remove_wait_queue(&dev->w_wait, &wait);
  145.     set_current_state(TASK_RUNNING);
  146.     return count;
  147. }
  148. /* user lseek fd */
  149. static loff_t globalfifo_llseek(struct file* filp, loff_t offset, int orig) {
  150.     loff_t ret;
  151.     switch(orig) {
  152.     // 从起始位置开始移动指针
  153.     case 0:
  154.         if(offset < 0) {
  155.             ret = -EINVAL;
  156.             break;
  157.         }
  158.         if((unsigned int)offset > GLOBALFIFO_SIZE) {
  159.             ret = -EINVAL;
  160.             break;
  161.         }
  162.         filp->f_pos = (unsigned int)offset;
  163.         ret = filp->f_pos;
  164.         break;
  165.     // 从当前位置开始移动指针
  166.     case 1:
  167.         if((filp->f_pos + offset) > GLOBALFIFO_SIZE) {
  168.             ret = -EINVAL;
  169.             break;
  170.         }
  171.         if((filp->f_pos + offset) < 0) {
  172.             ret = -EINVAL;
  173.             break;
  174.         }
  175.         filp->f_pos += offset;
  176.         ret = filp->f_pos;
  177.         break;
  178.     default:
  179.         ret = -EINVAL;
  180.     }
  181.     return ret;
  182. }
  183. /* user ioctl fd */
  184. static long globalfifo_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
  185.     // 获取设备结构体指针   
  186.     struct globalfifo_dev* dev = filp->private_data;   
  187.    
  188.     switch(cmd) {
  189.     case MEM_CLEAR:
  190.         down(&dev->sem);
  191.         dev->current_len = 0;
  192.         memset(dev->mem, 0, GLOBALFIFO_SIZE);
  193.         up(&dev->sem);
  194.         break;
  195.     default:
  196.         return -EINVAL;
  197.     }
  198.     return 0;
  199. }
  200. static const struct file_operations globalfifo_fops = {
  201.     .owner = THIS_MODULE,
  202.     .open = globalfifo_open,
  203.     .release = globalfifo_release,
  204.     .llseek = globalfifo_llseek,
  205.     .read = globalfifo_read,
  206.     .write = globalfifo_write,
  207.     .unlocked_ioctl = globalfifo_unlocked_ioctl
  208. };
  209. /* 设备驱动模块insmod加载函数 */
  210. static int globalfifo_init(void) {
  211.     // 向 Linux 内核中注册字符设备编号范围
  212.     register_chrdev_region(MKDEV(globalfifo_major, 0), 1, "globalfifo");
  213.     // 为设备以及共享内存分配内存
  214.     globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  215.     memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
  216.    
  217.     // 初始化字符设备0的基本字段
  218.     cdev_init(&globalfifo_devp->m_cdev, &globalfifo_fops);
  219.     globalfifo_devp->m_cdev.owner = THIS_MODULE;
  220.     // 将主设备号globalfifo_major次设备号0,与字符设备驱动的关联
  221.     cdev_add(&globalfifo_devp->m_cdev, MKDEV(globalfifo_major, 0), 1);
  222.     // 初始化信号量
  223.     sema_init(&globalfifo_devp->sem, 1);
  224.     // 初始化读写等待队列头
  225.     init_waitqueue_head(&globalfifo_devp->r_wait);
  226.     init_waitqueue_head(&globalfifo_devp->w_wait);
  227.     return 0;
  228. }
  229. static void globalfifo_exit(void) {
  230.     dev_t devno;
  231.     // 注销cdev
  232.     cdev_del(&globalfifo_devp->m_cdev);
  233.     // 释放设备结构体内存
  234.     kfree(globalfifo_devp);   
  235.     // 释放设备号
  236.     devno = MKDEV(globalfifo_major, 0);
  237.     unregister_chrdev_region(devno, 1);
  238. }
  239. MODULE_AUTHOR("cear");
  240. MODULE_LICENSE("GPL");
  241. module_param(globalfifo_major, int, S_IRUGO);
  242. module_init(globalfifo_init);
  243. module_exit(globalfifo_exit);
复制代码
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

2026-2-20 10:18:26

举报

2026-3-4 06:32:00

举报

喜欢鼓捣这些软件,现在用得少,谢谢分享!
您需要登录后才可以回帖 登录 | 立即注册