驱动程序中(4种I/O模型)

1 阻塞:  在应用层调用read函数的时候,如果硬件中的数据没有准备好,此时进程会进入休眠状态,当硬件的数据准备好的时候会给驱动发送中断。驱动收到中断之后,唤醒休眠的进程。这个被唤醒的进程在driver_read读取硬件的数据,并把数据  返回到用户空间。(模型中断)
可以使用队列,把整个进程进入到队列中,使进程进入阻塞的状态,当有数据产生的时候,产生中断,唤醒在队列中的进程,从而读取数据.
队列:
         wait_queue_head_t wq  //定义等待队列头
        init_waitqueue_head(&wq)//初始化等待队列头
         wait_event(wq, condition)  //不可中断的等待态
        wait_event_interruptible(wq, condition)//可中断的等待态(当产生中断信号就可以唤醒)
        参数:
        @wq         :等待对列头
        @condition :如果条件为假表示可休眠,如果为真表示不休眠
        返回值:成功返回0,失败返回错误码
        w ake_up(&wq)
        wake_up_interruptible(&wq)
        唤醒休眠
        condition = 1;
        分析:wake_up_interruptible()唤醒后,wait_event_interruptible(wq, condition)宏,自身再检查“condition”这个条件以决定是返回还是继续休眠,真(1)则返回,假(0)则继续睡眠,不过这个程序中若有中断程序的话,中断来了,还是会继续执行中断函数的。只有当执行wake_up_interruptible()并且condition条件成立时才会把程序从队列中唤醒。
==========================================================    
        user
        fd = open("hello",O_RDWR); //阻塞的方式打开文件
        read(fd,buf,sizeof(buf));
    -----------------------------------------------------
        kernel:
        fops:driver_read()
        {
            
             //2.阻塞方式打开的
             那进入休眠状态(等待唤醒)
 
            3.读取数据拷贝数据
        }
 ========================================================
     
 
2 非阻塞:在应用层调用read函数的时候,不管硬件的数据是否准备好都需要立返回到用户空间
   open()函数默认的是阻塞模式,我们需要把它设置成非阻塞模式
   int open(const char *pathname, int flags);
   参数1:文件的路径
   参数2:打开的的方式如下图
  
======================================================
     user
        fd = open("hello",O_RDWR|O_NONBLOCK); //非阻塞的方式打开文件
        read(fd,buf,sizeof(buf));
    -----------------------------------------------------------------
    kernel:
        fops:driver_read()
        {
             1.检查用户打开驱动的设备文件的方式
              if(file->f_flags & O_NONBLOCK&!esswait)
                return -EINVAL;  
                
                //非阻塞方式打开
                2.读取数据,向用户空间拷贝数据
                
            
        }
===========================================================
3 I/O多路复用(select/poll/epoll)
 
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数1 :一个结构体指针  struct pollfd *fds
             struct pollfd {
               int fd;               //文件描述符
              short events;     //产生的事件如读写错误                         
                              POLLIN  读写
                              POLLOUT 写
                              POLLERR 出错 
              short revents;  //结果描述,表示当前的fd是否可读,写初错
                              //用于判断出错
                               POLLIN  读写
                               POLLOUT 写
                               POLLERR 出错
参数2 nfds:要监视的描述符的数目
参数3 timeout 超时时间  是指定poll在返回前没有接收事件时应该等待的时间。 
                       timeout值:
                       INFTIM     永远等待(为负数)
                       0           立即返回,不阻塞进程
                       >0         等待指定数目的毫秒数      
返回值:
           负数:表示出错
          大于0 表示fd有数据
          小于0 表示时间到
  如果在应用中使用了poll对设备文件进行了监控,那么在驱动中必须必须实现poll
   unsigned  int  xxx_poll(struct  file *filp,struct poll_table_struct *pts)
    {
          //返回一个mask数值
          unsigned  int mask;
          //调用poll_wait,将当前的等待队列注册系统中
          poll_wait();
          //当没有数据的时候返回一个0(通过判断队列的状态)
          //当有数据的时候返回一个POLLIN
    }
================================================================
  int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); 
 功 能:select用于监测是哪个或哪些文件描述符产生件; 
            参数:nfds:         监测的最大文件描述个数
                      readfds:     读事件集合; //读
                      writefds:     写事件集合;  //NULL表示不关心
                      exceptfds:   异常事件集合(带外集合);  //一般为NULL
                      timeout:      超时设置.  NULL:一直阻塞,直到有文件描述符就绪或出错
                                            时间值为0:仅仅检测文件描述符集的状态,然后立即返回
                                            时间值不为0:在指定时间内,如果没有事件发生,则超时返回
                       struct timeval {
               long    tv_sec;           /* seconds */秒
               long    tv_usec;        /* microseconds */微妙
           };
   select返回值:  <0  出错
                            >0  表示有事件产生;
                            ==0 表示超时时间已到
注意:当select()函数退出后,集合表示有数据的集合
       当select()函数退出前,集合表示描述符的集合
void FD_CLR(int fd, fd_set *set);//把fd从集合中清除
int  FD_ISSET(int fd, fd_set *set);//判断fd是否在在集合中
void FD_SET(int fd, fd_set *set);//把描述符插入到集合中
void FD_ZERO(fd_set *set);//对集合清零
if( FD_ISSET(int fd, fd_set *set))//判断fd是否在集合中
{  
 
}
 
//在我们调用select时进程会一直阻塞直到以下的一种情况发生.
有文件可以读.
有文件可以写.
超时所设置的时间到
===============================================================
epoll的使用(现在常用的是epoll)
man epoll
#include <sys/epoll.h>
功能:创建epoll的实例
int epoll_create(int size);
参数:
      @size  :无效了
       返回值:成功返回epoll实例的文件描述符,失败-1;
 
//功能:向epoll实例中添加文件描述符,或者从中删除文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数:
        @ epfd :epoll实例的文件描述符
        @ op   :EPOLL_CTL_ADD  添加文件描述符
                    EPOLL_CTL_MOD  更正对文件描述符监听的事件
                    EPOLL_CTL_DEL  删除文件描述符
        @ fd   :想要添加的文件描述符
        @event :epoll_event
         typedef union epoll_data {
               int          fd;
         } epoll_data_t;
         struct epoll_event {
               uint32_t     events;    //监听的事件的类型EPOLLIN读/EPOLLOUT写
               epoll_data_t data;      //fd
           };
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
        返回值:成功返回0,失败返回-1
 
 
//功能:阻塞等待文件描述符对应驱动的数据是否准备好    //死等    
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
参数:    
          @epfd        :e poll实例的文件描述符
          @events     :被返回的epoll_event
          @maxevents  :监听文件描述符的最大值
          @timeout    :超时(-1表示或略超时时间)
          返回值:>0  :返回文件描述符的个数
                       =0  :超时
                        -1  :失败了
注意:支持管道,FIFO,套接字,POSIX消息队列,终端,设备等,但是就是不支持普通文件或目录的
==============================================================    
4 异步通知
    在应用层使用signal为一个信号绑定一个处理函数,应用层执行signal之后接着往下执行。当硬件中的数据准备好的时候硬件会给驱动发送中断,驱动收到中断后给应用程序发送信号,应用程序收到信号后执行信号处理函数,并在信号处理函数中调用read函数读取数据。
#include <signal.h>
sighandler_t signal(int signum, sighandler_t handler);
参数1  signum信号量  
参数2  handler处理函数‘
 
功能描述:根据文件描述词来操作文件的特性。
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);         
int fcntl(int fd, int cmd, struct flock *lock);
描述: fcntl()针对(文件)描述符提供控制.参数fd是被参数cmd操作(如下面的描述)的描述符.            
           针对cmd的值,fcntl能够接受第三个参数(arg)
 参数1   fd 文件描述符
 参数2  cmd
             复制一个现有的描述符(cmd=F_DUPFD).
             获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
             获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
             获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
             获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)
             注意:在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值 然后按照希望修改它,  最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。

 

[返回值]
fcntl()的返回值 与命令有关 如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD , F_GETFD , F_GETFL以及F_GETOWN。
    F_DUPFD   返回新的文件描述符
    F_GETFD   返回相应标志
    F_GETFL , F_GETOWN   返回一个正的进程ID或负的进程组ID
cmd值的F_GETFL和F_SETFL : F_GETFL取得fd的文件状态标志,如同下面的描述一样(arg被忽略),在说明open函数时,已说明了文件状态标志。不幸的是,三个存取方式标志 (O_RDONLY , O_WRONLY , 以及O_RDWR)并不各占1位。(这三种标志的值各是0 , 1和2,由于历史原因,这三种值互斥 — 一个文件只能有这三种值之一。) 因此首先 必须用屏蔽字O_ACCMODE相与取得存取方式位,然后将结果与这三种值相比较。  
F_SETFL    设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC。 而fcntl的文件状态标志总共有7个:O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC             
可更改的几个标志如下面的描述:
    O_NONBLOCK   非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
    O_APPEND     强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
    O_DIRECT       最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
    O_ASYNC      当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候
cmd值的F_GETOWN和F_SETOWN:   
F_GETOWN   取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回的是负值(arg被忽略)     
F_SETOWN   设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明(arg绝对值的一个进程组ID),否则arg将被认为是进程id
 
=-====================================================================
 应用层:
 1. signal(SIGIO,信号的处理函数);
 2. fcntl(fd,F_SETFL,fcntl(fd, F_GETFL)|FASYNC);//重新设置文件状态的标志
 3. fcntl(fd,F_SETOWN,getpid());//设置将接收SIGIO和SIGURG信号的进程id或进程组id
---------------------------------------------------------------------------------
 驱动----发送信号
1 需要和进程进行关联--记录信号发送给谁
  实现一个fasync()的接口
  int    key_drv_fasync (int   fd,  struct file *file, int on);
 {
        //记录信号发送给谁
        return fasync_helper(fd,file,on,&key->fasync);
 }
然后在特定的环境中发送信号(函数),当有数据的时候:
kill_fasync(struct fasync_struct * * fp, int sig, int band);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
} @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment信号驱动式I/O模型是一异步的I/O模型,它可以同时处理多个I/O操作,提高系统的并发性和响应性。下面是详细介绍: 1. 应用程序向内核_music, container, false); Button btnPlay = view.findViewById(R.id.btn_play); Button btnPause = view.findViewById(R.id.btn发起I/O请求,请求在指定的文件描述符上监听I/O事件。 2. 内核在指定的文件_pause); Button btnStop = view.findViewById(R.id.btn_stop); mMediaPlayer = MediaPlayer.create(getContext(), R.raw.music); btn描述符上等待I/O事件发生。 3. 当I/O事件发生时,内核会向应用程序发送Play.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mMediaPlayer.isPlaying()) { 一个信号,通知应用程序有I/O事件需要处理。 4. 应用程序收到信号后,可以在 mMediaPlayer.start(); } } }); btnPause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View信号处理函数处理相应的I/O事件。在信号处理函数,应用程序可以读写数据,或 v) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); } } }); btnStop.setOnClickListener(new View者关闭文件描述符等操作。 5. 当应用程序完成对I/O事件的处理后,可以再次向内核.OnClickListener() { @Override public void onClick(View v) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.stop(); 发起I/O请求,等待下一个I/O事件的发生。 信号驱动式I/O模型可以有效地 mMediaPlayer = MediaPlayer.create(getContext(), R.raw.music); } } }); return view; } @Override 避免阻塞等待I/O操作完成的情况,提高系统的并发性和响应性。但是, public void onDestroy() { super.onDestroy(); if (mMediaPlayer != null) { mMediaPlayer.release(); mMediaPlayer =信号驱动式I/O模型需要应用程序处理信号,增加了一定的复杂度。同时, null; } } } ``` 7.其他模块的Fragment和Java代码与音乐模块类似,这里由于信号可能会被其他信号打断,因此需要应用程序进行信号处理函数的重入处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值