Chinaunix首页 | 论坛 | 博客
  • 博客访问: 601797
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: LINUX

2013-12-26 16:54:47

Linxu设备驱动中的阻塞与非阻塞I/O

轮询的缺点:

1、效率低

2CPU负担重

中断的优点:

1、减轻CPU负担

2、实时性好

{//?驱动中阻塞和非阻塞,异步通知是什么

  ----------

  |        |                  --------

  |  进程  |  <-------------->| 设备 |

  |        |                  --------

  ----------

   阻塞: 进程等待设备资源可用, 等待过程中, 进程休眠(CPU切换去执行别的进程)。 

   非阻塞: 不等待,轮询设备 

   异步通知:  让设备主动去通知进程。本质就是中断

   

   

   故和CPU打交道的有阻塞、非组赛、中断

   

   注: 提出阻塞,非阻塞,异步通知概念,这里是为了解决 进程和设备间是如何打交道的

}

   

{//?如何在驱动中实现阻塞I/O

  当有多个进程阻塞睡眠时,可用排队的方式,当造成阻塞的条件消失(如有资源释放了),去唤醒阻塞在排队的进程.  

  

  {//?什么是等待队列

    以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制,同步对系统资源的访问。

    

    如:

      当某进程不满足某条件时(如资源), 可把进程暂时睡眠,放入队列中,

      等条件满足时(如某资源被释放出来了),再把进程唤醒去执行

  }

  

  {//?如何使用等待队列,在驱动中实现阻塞

#cd  /root/driver_example/waitqueue

#make clean

#make 

#insmod hello.ko

#mknod /dev/hello c 250 0

#cat /dev/hello         

打开另一终端

#echo hello > /dev/hello

可以发现在前面终端会打印出hello

#rmmod hello

  {//---driver_example/waitqueue/hello.c

     static wait_queue_head_t queue;   //定义等待队列

     static char *gbuf=NULL;

     static int glen=0;

     static ssize_t hello_read (struct file *filp, char __user *buf, size_t count,

                     loff_t *f_pos)

     {

     

        /*

          wait_event_interruptible  内部有一for循环,

          1. 当条件满足时,且无信号需处理时, 跳出循环,返回0

          2. 如果有信号未处理,返回非零值

             return -ERESTARTSYS,  

             系统调用看到ERESTARTSYS,则在处理完信号后,又会再次触发前次的调用(即又调用了一次hello_read

        

          3. 当条件不满足时,且无信号需处理,则睡眠等待         

                      

        */

        if (wait_event_interruptible(queue, glen!=0))   //把调用该函数的进程(cat /dev/hello放入等待队列queue中,等

        {

           return -ERESTARTSYS;  //表示信号函数处理完毕后重新执行信号函数前的某个系统调用.

                                 //即等待队列在等待中,如果有信号,先执行完信号后,再执行一次系统调用,继续等待

        }

                

        glen =0;

        ...

     }

    

     static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count,

                     loff_t *f_pos)

     {

        glen = count;

        wake_up_interruptible(&queue);  //唤醒在queue等待队列上等待的所有进程 这里指 cat /dev/hello 进程

        ...

     }

    

     static int hello_init(void)

     {

       gbuf = kmalloc(GBUF_MAX, GFP_KERNEL);

      init_waitqueue_head(&queue);

      ...

     }

    

}

}

}

{//?如何在驱动中实非阻塞I/O

 应用:

    用select/poll       // I/O多路复用 

 驱动:

    实现.poll 函数。 

  ---------------------------

  | 应用                    

  |         select/poll        

  ---------------------------

               |

  ---------------------------

  |内核     sys_poll           //poll系统调用 ,轮询poll_table中的驱动.poll, 当设备可读或可写则返回

  |            |            

  |          .poll             //驱动实现的.poll

  ---------------------------

   /*原理:

       select/poll 会调用  sys_poll(系统调用)  ,再调用驱动中的.poll

 

    sys_poll它会循环监听 poll_table上的设备是否就绪(由其关联的等待队列queue触发),如果就绪则返回

    如不就绪,则 schedule (让渡内核去执行别的进程), 返回后继续监测。

    如果发生阻塞,会发生在sys_poll中,

    

   */

{//------实验 poll------------

#cd  /root/driver_example/poll

#make clean

#make 

#insmod hello.ko

#mknod /dev/hello c 250 0

#gcc test.c

#./a.out    等待有数据输入

打开另一终端

#echo hello > /dev/hello //写入数据, 原终端显示Poll monitor:can be read

#cat /dev/hello //读走内容,原终端等待有数据输入

}

{//---poll/test.c    //功能:  查询设备,看设备是否可读写

#include 

#include 

#include 

#include 

#include 

#include 

int main()

{

  int fd, num;

  fd_set rfds,wfds;

  fd = open("/dev/hello", O_RDONLY);

  if (fd < 0)

  {

     printf("Device open failure\n");

  }

  while (1)

  {

FD_ZERO(&rfds);

FD_ZERO(&wfds);

FD_SET(fd, &rfds);

FD_SET(fd, &wfds);

select(fd + 1, &rfds , &wfds, NULL, NULL);  //  I/O多路复用  最终调用驱动的.poll 函数

select在可读或者可写时,不阻塞,否则阻塞,但是真正的阻塞并不是在这,而是在系统调用的sys_poll

  //数据可获得

if (FD_ISSET(fd, &rfds))

{

     printf("Poll monitor:can be read\n");

}

  //数据可写入

if (FD_ISSET(fd, &wfds))

{

     printf("Poll monitor:can be write\n");

}

  }

}  

}

{//---poll/hello.c

 #include 

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

   {

       unsigned int mask = 0;

       poll_wait(filp, &queue, wait);// 把当前进程添加到waitpoll_table 设备监控列表)中,并关联相应的等待队列queue

                                     //当有多个驱动添加到poll_table,表示可以同时监控多个驱动设备的变化 

                                     //poll_wait函数并不阻塞, 真正的阻塞动作是在poll系统调用中完成  

       if (glen != 0)

       {

          mask |= POLLIN | POLLRDNORM; /*标示数据可获得*/

       }       

 if (glen < GBUF_MAX)

 {

    mask |= POLLOUT | POLLWRNORM; /*标示数据可写入*/

 }                           

      return mask;      //通过立即返回标志

   } 

static struct file_operations hello_fops = {

  .poll = hello_poll,

};

}

}

{//?如何在驱动中实现异步通知

#cd  /root/driver_example/async

#make clean

#make 

#insmod hello.ko

#mknod /dev/hello c 250 0

#gcc test.c

#./a.out    //等待中

打开另一终端

#echo hello > /dev/hello //写数据, 触发异步信号

                          //原终端会显示receive a signal from globalfifo,signalnum:29

{//--/async/test.c

   #include 

   #include 

   #include 

   #include 

   #include 

   #include 

   

   void input_handler(int signum) //接收到异步读信号后的动作

   {

     printf("receive a signal from globalfifo,signalnum:%d\n",signum);

   }

   

   int main()

   {

     int fd = 0;

     int oflags;

   

     fd = open("/dev/hello",O_RDWR, S_IRUSR | S_IWUSR);

     if (fd < 0)

     {

        printf("Device open failure\n");

     }

   

     //启动信号驱动机制

     signal(SIGIO, input_handler); //input_handler()处理SIGIO信号

     fcntl(fd, F_SETOWN, getpid());  //设置设备文件的所有者为本进程

     oflags = fcntl(fd, F_GETFL);    // 会调用 驱动中的 .fasync

     fcntl(fd, F_SETFL, oflags | FASYNC);  //FASYNC  设置支持异步通知模式 

           

      while (1)

      {

         sleep(100);

      }  

   }

}

{//--/async/hello.c

    struct fasync_struct *async_queue; /* 异步结构体指针,用于读 */

    

    static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count,

                    loff_t *f_pos)

    {

       kill_fasync(&async_queue, SIGIO, POLL_IN);  //发送SIGIO 异步通知信号

       return count;

    }

    

    static int hello_fasync(int fd, struct file *filp, int mode)

    {

       return fasync_helper(fd, filp, mode, &async_queue);  //处理标志的变更

    }

    

    static struct file_operations hello_fops = {

      .owner = THIS_MODULE,

      .write = hello_write,

      .fasync = hello_fasync,

    };

}

}

阅读(828) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~