Linux内核开发之阻塞非阻塞IO----轮询操作

“小王,来聊聊,今天面试的情况怎么样,应该挺顺利的吧..”看着小王平淡的眉头,我问道。

“唉,别提了,你说,我的运气咋这差呢,面试前你不是给我讲了有关阻塞的问题吗,我见了面试官是吧,还跟他好好的用今天排队的例子说了有关阻塞的问题,但是..”小王哀声叹气地说到。

“别但是了,怎么啦..”

“可问题是面试官压根就没打算问我有关阻塞的问题及解决方案,但是问我说:这样吧,你给我说说在Linux设备驱动中有关非阻塞的方法,我这一听,傻眼了不是,你刚好给我讲的是阻塞的东西,可人家偏要问我有关非阻塞的问题,我..”小王欲哭无泪啊..

“怎么这样呢,算了,机会多的是,亡羊补牢,我现在就给你说说有关非阻塞的问题----Linux设备驱动程序之阻塞非阻塞IO----轮询操作”。

通过上一节,我们都明白了,有关阻塞的相关知识(不知道,那我没辙了,饭送到嘴,你还挑食,难不成我拿把起子把嘴撬开不成,自己看上一篇吧),现在就来聊聊对

立面非阻塞。

使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问,这两个系统调用最终又会引发设备驱动中的poll()函数被执行

所以我们的问题就集中到了如何编写设备驱动中的poll()函数就可以了。二话不说,先来看看设备驱动中的poll()函数原型:

unsigned int (*poll)(struct file *filp, struct poll_table *wait);

这个函数要进行下面两项工作。首先,对可能引起设备文件状态变化的等待队列调用poll_wait(),将对应的等待队列头添加到poll_table.然后,返回表示是否能对设备进行无阻塞读写访问的掩码。在上面提到了一个poll_wait()函数,它的原型:

void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);

它的作用就是把当前进程添加到wait参数指定的等待列表(poll_table)中。需要注意的是这个函数是不会引起阻塞的,呵呵,谁给它取得个名字带wait的,给咱们添这么多麻烦。

“等等,你先停停,你是高手,我可是菜鸟呢,你先给我说说poll_table结构吧,心里总是想它是什么..”小王打断我道。

行行,说起这个结构,我也是费了一番周折,它定义在“include/linux/poll.h, line 38“,具体如下:

typedef struct poll_table_struct {
    poll_queue_proc qproc;
    unsigned long key;
} poll_table;   看看,其实没什么吧,不要想的太复杂了
经过以上驱动程序的poll()函数应该返回设备资源的可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位"或"结果.每个宏的含义都表示设备的一种状态,如:
常量说明
POLLIN普通或优先级带数据可读
POLLRDNORM普通数据可读
POLLRDBAND优先级带数据可读
POLLPRI高优先级数据可读
POLLOUT普通数据可写
POLLWRNORM普通数据可写
POLLWRBAND优先级带数据可写
POLLERR发生错误
POLLHUP发生挂起
POLLNVAL描述字不是一个打开的文件
"小王,你明白了没.."看着小王眨巴眨巴的小眼睛,我说。
"呵呵,你干脆给我来个典型模板,行不?"小王苛求道。
行,没问题,你现在特殊时期,我是有求必应。请看下边:
static unsigned int XXX_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
        struct XXX_dev *dev = filp->private_data;     //获得设备结构指针
    ...
    poll_wait(filp, &dev->r_wait, wait);    //加读等待对列头
    poll_wait(filp ,&dev->w_wait, wait);    //加写等待队列头
    
    if(...)//可读
    {
          mask |= POLLIN | POLLRDNORM;    //标识数据可获得if(...)//可写
    {
          mask |= POLLOUT | POLLRDNORM;    //标识数据可写入
     }
    ..
    return mask;
}

"小王,这次看明白了吧,要是还看不明白,我就再给你讲讲用户空间的轮询编程,两个结合起来也许好懂点,行不"。我补充道。

在用户程序中,select()和poll()本质上是一样的, 不同只是引入的方式不同,前者是在BSD UNIX中引入的,后者是在System V中引入的。用的比较广泛的是select

系统调用。原型如下:

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptionfds, struct timeval *timeout);

其中readfs,writefds,exceptfds分别是select()监视的读,写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的文件描述符加1,timeout则是一个时间上限值,超过该值后,即使仍没有描述符准备好也会返回。

struct timeval
{
    int tv_sec;    //秒
    int tv_usec;   //微秒
}
涉及到文件描述符集合的操作主要有以下几种:
1)清除一个文件描述符集   FD_ZERO(fd_set *set);
2)将一个文件描述符加入文件描述符集中    FD_SET(int fd,fd_set *set);
3)将一个文件描述符从文件描述符集中清除  FD_CLR(int fd,fd_set *set);
4)判断文件描述符是否被置位    FD_ISSET(int fd,fd_set *set);

最后我们利用上面的文件描述符集的相关来写个验证添加了设备轮询的驱动,把上边两块联系起来:

必要的头文件
 
#define FIFO_CLEAR 0x1
#define BUFFER_LEN 20
main()
{
  int fd, num;
  char rd_ch[BUFFER_LEN];
  fd_set rfds,wfds;
  /*以非阻塞方式打开/dev/polltest设备文件*/
  fd = open("/dev/polltest", O_RDONLY | O_NONBLOCK);
  if (fd !=  - 1)
  { /*FIFO清0*/
    if (ioctl(fd, FIFO_CLEAR, 0) < 0)
    {
      printf("ioctl command 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);
      /*数据可获得*/
      if (FD_ISSET(fd, &rfds))
      {
      	printf("Device can be read now\n");
      }
      /*数据可写入*/
      if (FD_ISSET(fd, &wfds))
      {
      	printf("Device can be written now\n");
      }      
    }
  }
  else
  {
    printf("Device open failure now\n");
  }
}

 




 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核IO模型主要包括阻塞IO非阻塞IO、多路复用IO和异步IO。下面我将逐个介绍这些模型的特点。 1. 阻塞IO(Blocking IO):当应用程序发起一个IO操作后,内核会一直阻塞等待,直到IO操作完成才返回结果给应用程序。在这期间,应用程序是被阻塞的,无法进行其他操作阻塞IO模型适用于对实时性要求不高的场景,简单易用,但会导致资源浪费。 2. 非阻塞IO(Non-Blocking IO):当应用程序发起一个IO操作后,内核会立即返回一个结果给应用程序,无论IO操作是否完成。如果IO操作还未完成,应用程序可以继续做其他事情,而不需要一直等待。应用程序可以通过轮询来检查IO操作的状态,直到操作完成。非阻塞IO模型可以提高系统的并发性能,但需要应用程序自己处理轮询逻辑。 3. 多路复用IO(Multiplexing IO):多路复用IO模型通过一个系统调用(如select、poll、epoll等)来同时监听多个IO事件,当有任意一个IO事件就绪时,内核会通知应用程序进行处理。这种模型避免了阻塞轮询的问题,可以同时处理多个IO操作,提高系统的并发性能。 4. 异步IO(Asynchronous IO):异步IO模型中,应用程序发起一个IO操作后,可以立即返回继续执行其他操作,而不需要等待IO操作完成。当IO操作完成后,内核会通知应用程序,并返回结果。异步IO模型通过回调函数来处理IO完成的通知,相比于其他模型,可以更高效地处理大量的IO操作。 这些IO模型在不同的场景下有各自的优劣,选择合适的IO模型可以提高系统的性能和响应能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值