Linux内核的驱动阻塞机制

  1. 什么叫阻塞,阻塞就是执行操作的时候,如果条件不满足则进程挂起,进入睡眠状态,知道等待的条件满足。
  2. 阻塞的实现机制,应用程序是无法实现阻塞的,进程阻塞是通过Linux内核的等待队列来实现的。如果像将一个进程阻塞,就将它放入等待队列中,运行条件不满足的时候就将等待队列上的进程睡眠,实现阻塞的目的。
  3. 实现函数:
0.wait_queue_head_t myQueue;
	功能描述:创建一个等待队列头,相当于生成一个空节点
1.init_waitqueue_head(&myQueue);
	功能描述:初始化队列头,相当于生成一个空节点
2.DECLARE_WAITQUEUE(name,current);
	功能描述:定义一个等待队列任务,相当于生成一个链式队列的节点,但此时并没有放入等待队列中
	参数说明:name是一个wait_queue_t类型的等待队列变量,第二个参数是进程,一般用current这个默认的
	指针,代表的是用户当前进程。
3.add_wait_queue(myQueue,name);
	功能描述:将等待队列任务放入等待队列中,相当于入队操作
	参数说明:第一个参数是头结点,第二个参数是要插入的节点
4.remove_wait_queue(myQueue,name);
	功能描述:将等待队列任务删除出队列,相当于出队操作
	参数说明:第一个参数是头结点,第二个参数是要删除的节点
	
----------------------以下是等待队列的睡眠操作接口函数------------------------------

5.wait_event(myQueue, condition);
  wait_event_interruptible(myQueue, condition);
	功能描述:使等待队列上的进程睡眠,改睡眠不可/可被信号打断
	参数说明:第一个参数是等待队列头变量,第二个参数是睡眠条件,如果是假则一直睡眠,为真就退出睡眠
6.wait_event_timeout(myQueue, condition, timeout);
  wait_event_interruptible_timeout(myQueue, condition,timeout);
	功能描述:使等待队列上的进程睡眠一段时间,改睡眠不可/可被信号打断
	参数说明:第一个参数是等待队列头变量,第二个参数是睡眠条件,如果是假则睡眠一段时间,为真就退出
	睡眠,timeout是睡眠时间

----------------------以下是等待队列的唤醒操作接口函数------------------------------

7.wake_up(myQueue);
  wake_up_interruptible(myQueue);
	功能描述:唤醒不可/可被信号打断的等待队列上的睡眠进程
  1. 代码实例
实例:将A.c代码中的read函数设置成阻塞模式,只有当B.c函数执行并向内核中写东西的时候才会读取内容,否则一直阻塞。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include "linux/cdev.h"
#include "linux/kdev_t.h"
#include "linux/fs.h"
#include "asm/uaccess.h"
#include "linux/sched.h"

MODULE_LICENSE ("GPL");

struct cdev  myuart;
int major = 200;
int minor = 2;
int read_flag = 0;
wait_queue_head_t  my_queue;			0.设置等待队列头结点
dev_t  dev_id;
char kernel_buf[64] = {0};
struct file_operations  var;
wait_queue_t  node_var;			2.定义一个等待队列任务,相当于生成一个节点

int myopen(struct inode *pinode, struct file *pfile)
{
    printk(KERN_INFO "myopen run\n");
    return 0;
}
int myclose(struct inode *pinode, struct file *pfile)
{
     printk(KERN_INFO "myclose run\n");
     return 0;
}
ssize_t myread(struct file *pfile, char __user *buf, size_t len, loff_t *off)
{
      int ret = 0;

      DECLARE_WAITQUEUE(node_var,current); 		2.定义一个等待队列任务,相当于生成一个节点
      printk(KERN_INFO "myread run\n");
      add_wait_queue(&my_queue, &node_var);		3.入队操作
      wait_event(my_queue,read_flag != 0); 		4.A.c程序执行read函数时,如果read_flag = 0,此
      时阻塞,否则继续执行(初值为0,当write函数执行的时候会设置为1)
      ret = copy_to_user(buf,kernel_buf,len);
      read_flag = 0;
      remove_wait_queue(&my_queue, &node_var);		6.remove函数放在这里,执行完以后就删除队列
      return len-ret;
}

ssize_t mywrite(struct file *pfile, const char __user *buf, size_t len, loff_t *off)
{
     int ret;
     ret = copy_from_user(kernel_buf,buf,len) ; 
     printk(KERN_INFO "mywrite fun run") ;  
     read_flag = 1;			//write函数执行,将标志位设置成1,
     wake_up(&my_queue);		5.叫醒等待队列上的任务,此时可以继续进行读
     return 30;
}
static int  __init hello_init (void)
{
        
        int ret = 0;
        int ret1;

        dev_id = MKDEV(major,minor) ;
        ret = register_chrdev_region(dev_id,1, "uart");
        if(ret == -1) // 
        {
             printk(KERN_INFO "dev_id = %d can't use \n",major);
             ret1 = alloc_chrdev_region(&dev_id,minor,1, "uart");
             if(ret1 == -1)
             {
                  printk(KERN_INFO "dev_id error \n");
                  return -1;
             }
             else
             {
                  printk(KERN_INFO "major = %d \n", MAJOR(dev_id)); 
             }   
        }
        else
        {
            printk(KERN_INFO "major = %d can use \n",major);
        }
        var.open = myopen;
        var.release = myclose;
        var.write = mywrite;
        var.read = myread;
        cdev_init(&myuart,&var); 
 
        cdev_add(&myuart,dev_id,1);
        init_waitqueue_head(&my_queue);			1.初始化等待队列
	return 0;
}

static void  __exit hello_exit (void)
{
      unregister_chrdev_region(dev_id, 1);
      cdev_del(&myuart);
}

module_init (hello_init);
module_exit (hello_exit);


代码A.c
#include "stdio.h"
#include "fcntl.h"
#include "stdlib.h"
#include "unistd.h"
int main()
{
    int fd;
    int len;
    char buf[64] = {0};
    fd  = open("./kkk.c",O_RDWR,0777);
    if(fd == -1)
    {
        printf("open error\n");
        return -1;
    }
    printf("read before\n");
    len = read(fd,buf,64);
    printf("read after: buf = %s,len = %d\n",buf,len);
    close(fd);
    return 0;
}

代码B.c
#include "stdio.h"
#include "fcntl.h"
#include "stdlib.h"
#include "unistd.h"
int main()
{
    int fd;
    int len;
    char buf[64] = {0};
    fd  = open("./kkk.c",O_RDWR,0777);
    if(fd == -1)
    {
        printf("open error\n");
        return -1;
    }
    printf("please input send to kernel data\n");
    scanf("%s",buf);
    len = write(fd,buf,64);
    while(1);
    close(fd);
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值