蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O

简介:   今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合起来写的,,总之这又给了额外的动力,让自己继续前进,,希望和大家能够分享一些自己的经验,,在最需要奋斗的年级以及在技术的领域踽踽独行的过程中有共同的伙伴继续前进~   今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。

  今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合起来写的,,总之这又给了额外的动力,让自己继续前进,,希望和大家能够分享一些自己的经验,,在最需要奋斗的年级以及在技术的领域踽踽独行的过程中有共同的伙伴继续前进~

  今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。

一、基本概念:

  • 阻塞操作     : 是指在执行设备操作时,若不能获得资源,则挂起进程直到满足操作条件后再进行操作。被挂起的进程进入休眠, 被从调度器移走,直到条件满足。
  • 非阻塞操作  :在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作。非阻塞应用程序通常使用select系统调用查询是否可以对设备进行无阻塞的访问最终会引发设备驱动中   poll函数执行。

二、轮询操作

阻塞的读取一个字符:

char buf;
fd = open("/dev/ttyS1",O_RDWR);
.....
res = read(fd,&buf,1); //当串口上有输入时才返回,没有输入则进程挂起睡眠
if(res == 1)
{
  printf("%c/n",buf);
}

非阻塞的读一个字符:

char buf;
fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK);//O_NONBLOCK 非阻塞标识
.....
while(read(fd,&buf,1)!=1);//串口上没有输入则返回,所以循环读取
printf("%c/n",buf);

  阻塞操作常常用等待队列来实现,而非阻塞操作用轮询的方式来实现。非阻塞I/O的操作在应用层通常会用到select()和poll()系统调用查询是否可对设备进行无阻塞访问。select()和poll()系统调用最终会引发设备驱动中的poll()函数被调用。这里对队列就不多介绍了,大家可以看看数据结构里面的知识点。

  应用层的select()原型为:

int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptionfds,

struct timeval *timeout);  numfds 的值为需要检查的号码最高的文件描述符加1,若select()在等待timeout时间后,若没有文件描述符准备好则返回。

  应用程序为:

#inlcude------
main()
{
  int fd,num;
  char rd_ch[BUFFER_LEN];
  fd_set rfds,wfds;  //读写文件描述符集
  //以非阻塞方式打开/dev/globalfifo设备文件
  fd=open("/dev/globalfifo",O_RDWR|O_NONBLOCK);
  if(fd != -1)
  {
   //FIFO 清零
   if(ioctl(fd,FIFO_CLEAR,0) < 0)
   {
     printf("ioctl cmd failed /n");
   }
   while(1)
   {
     FD_ZERO(&rfds);
     FD_ZERO(&wfds);
     FD_SET(fd,&rfds);
     FD_SET(fd,&wfds);
      select(fd+1,&rfds,&wfds,null,null);

    }
  }
}

  下面说说设备驱动中的poll()函数,函数原型如下:

static unsigned int poll(struct file *file, struct socket *sock,poll_table *wait) //第一个参数是file结构体指针,第三个参数是轮询表指针,这个函数应该进行两项工作
  • 对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table
  • 返回表示是否能对设备进行无阻塞读,写访问的掩码

  这里还要提到poll_wait()函数,很多人会以为是和wait_event()一样的函数,会阻塞的等待某件事情的发生,其实这个函数并不会引起阻塞,它的工作是把当前的进程增添到wait参数指定的等待列表poll_table中去,poll_wait()函数原型如下:

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
从中可以看出是将等待队列头wait_address添加到p所指向的结构体中(poll_table)

  驱动函数中的poll()函数典型模板如下:

static unsigned int xxx_poll(struct file *filp,struct socket *sock,
 poll_table *wait)
{
unsigned int mask = 0;
struct xxx_dev *dev = filp->private_data;//获得设备结构体指针
...
poll_wait(filp,&dev->r_wait,wait);//加读等待队列头到poll_table
poll_wait(filp,&dev->w_wait,wait);//加写等待队列头到poll_table
...
if(...)//可读
mask |= POLLIN | POLLRDNORM;
if(...)//可写
mask |= POLLOUT | POLLRDNORM;
...
return mask;
 
}

  

三、支持轮询操作的globalfifo驱动

  在globalfifo的poll()函数中,首先将设备结构体重的r_wait和w_wait等待队列头加到等待队列表,globalfifo设备驱动的poll()函数如下:

static unsigned int gloablfif0_poll(struct file *filp,poll_table *wait)
{
     unsigned int mask = 0;
    struct globalfifo_dev *dev = filp->private_data;

    down(&dev->sem);

    poll_wait(filp,&dev->r_wait , wait)  ;
    poll_wait(filp,&dev->r_wait , wait)  ;

    if(dev->current_len != 0)
    {
          mask |= POLLIN | POLLRDNORM;  
    }

    if(dev->current_len != GLOBALFIFO_SIZE)
    {
        mask |= POLLOUT | POLLWRNORM;
    }

    up(&dev->sem);
    return mask;
}

  

四、总结

阻塞与非阻塞操作:

  • 定义并初始化等待对列头;
  • 定义并初始化等待队列;
  • 把等待队列添加到等待队列头
  • 设置进程状态(TASK_INTERRUPTIBLE(可以被信号打断)和TASK_UNINTERRUPTIBLE(不能被信号打断))
  • 调用其它进程

poll机制:

  • 把等待队列头加到poll_table
  • 返回表示是否能对设备进行无阻塞读,写访问的掩码

 

  版权所有,转载请注明转载地址:http://www.cnblogs.com/lihuidashen/p/4442573.html

相关文章
|
4月前
|
存储 缓存 Unix
Linux 设备驱动程序(三)(上)
Linux 设备驱动程序(三)
46 3
|
4月前
|
Linux
Linux 设备驱动程序(四)
Linux 设备驱动程序(四)
27 1
|
4月前
|
存储 数据采集 缓存
Linux 设备驱动程序(三)(中)
Linux 设备驱动程序(三)
43 1
|
3月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
4月前
|
存储 缓存 安全
Linux 设备驱动程序(三)(下)
Linux 设备驱动程序(三)
35 0
|
4月前
|
安全 Linux 程序员
Linux 设备驱动程序(二)(下)
Linux 设备驱动程序(二)
31 0
|
23天前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
52 8
|
24天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
145 6
|
25天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
65 3