linux中实现对输入的异步



1:传统的输入信号

    传统的输入都是通过阻塞来实现,例如getchar一直等待用户输入。又或者是再curses库中的getch都是通过阻塞的方式来等待用户输入。那么想象一个场景要设计一个游戏,这个游戏可以让玩家动态输入一些值来动态调整游戏参数。不可能通过getchar这样的阻塞函数来获取用户输入把。那么这个该如何实现呢,再想象一下另外一种场景操作系统的CPU不可能是一直等待网卡的输入把。所以对于一些特别的场景阻塞输入是无法满足要求的。下面的这个例子就是一个阻塞输入的例子。

#include<stdio.h>
#include<stdlib.h>
#include<curses.h>

void init_setup (  );
void init_end (  );
void on_input (  );
void do_main (  );
int main ( int argc, char *argv[] )
{

        init_setup();
        on_input();
        do_main();
        init_end();
        return EXIT_SUCCESS;
}
        /* ----------  end of function main  ---------- */



void init_setup (  )
{
        initscr();
        crmode();
        noecho();
        clear();
}               /* -----  end of function init_setup  ----- */

void init_end (  )
{
        endwin();
}               /* -----  end of function init_end  ----- */

void on_input (  )
{
        char c;
        while((c = getch()) != ‘q‘){
                if(c == ‘w‘)
                mvaddch(20,20,‘!‘);
                else if(c == ‘e‘)
                mvaddch(20,20,‘0‘);
                else if(c == ‘r‘)
                mvaddch(20,20,‘t‘);
        }
}               /* -----  end of function on_input  ----- */

void do_main (  )
{
        while(1){
                move(50,50);
                addstr("do other thing");
        }

}               /* -----  end of function do_main  ----- */

    从这个例子可以发现do_main没有执行,因为on_input一直等待用户输入阻塞了下面的程序运行。所以在有些场景像getchar或者getch这类的阻塞函数无法满足一些需求,那么就需要使用异步IO。异步IO的实现有两种方法:

    1.设置输入O_ASYNC位

    2.使用aio_read()

2:异步输入一

    步骤如下:

            1.设置0描述符,当输入就绪的时候发送信号

            2.设置0描述符O_ASYNC标志位

            2.设置信号处理函数。

下面是一个例子:

    

#include<stdio.h>
#include<stdlib.h>
#include<curses.h>
#include<signal.h>
#include<fcntl.h>
void init_setup (  );
void init_end (  );
void on_input (  );
void do_main (  );
void enable_kdb_signals();
int main ( int argc, char *argv[] )
{

        init_setup();
        enable_kdb_signals();
        signal(SIGIO,on_input);
        do_main();
        init_end();
        return EXIT_SUCCESS;
}
        /* ----------  end of function main  ---------- */



void init_setup (  )
{
        initscr();
        crmode();
        noecho();
        clear();
}               /* -----  end of function init_setup  ----- */

void init_end (  )
{
        endwin();
}               /* -----  end of function init_end  ----- */
void on_input (  )
{
        char c;
        c = getch();
        if(c == ‘w‘)
        mvaddch(20,20,‘!‘);
        else if(c == ‘e‘)
        mvaddch(20,20,‘0‘);
        else if(c == ‘r‘)
        mvaddch(20,20,‘t‘);
}               /* -----  end of function on_input  ----- */

void do_main (  )
{
        while(1){
                sleep(1);
                move(50,50);
                addstr("do other thing");
                refresh();
        }

}               /* -----  end of function do_main  ----- */

//设置输入时发送信号,设置输入为O_ASYNC
void enable_kdb_signals()
{
        int fd_flags;
        fcntl(0,F_SETOWN,getpid());
        fd_flags = fcntl(0,F_GETFL);
        fcntl(0,F_SETFL,(fd_flags|O_ASYNC));
}

    上面这个例子可以看到do other thing以及输出,do_main正在运行,但是此时你输入字符程序可以通过信号处理函数on_input来接受输入进行响应。达到了异步的效果。

3:异步输入二

    异步输入的第二种方法是通过aio_read来实现的,使用aio_read更加灵活,但是设置起来也比较复杂。

设置步骤如下:

    1.设置信号处理函数,接受用户输入

    2.设置aiocb结构体中的变量指明等待什么类型的输入,当输入的时候产生什么信号。

    3.将aiocb结构体传递给aio_read来递交读入请求。

aiocb结构体定义如下:

         struct aiocb {
               /* The order of these fields is implementation-dependent */

               int             aio_fildes;     /* File descriptor */
               off_t           aio_offset;     /* File offset */
               volatile void  *aio_buf;        /* Location of buffer */
               size_t          aio_nbytes;     /* Length of transfer */
               int             aio_reqprio;    /* Request priority */
               struct sigevent aio_sigevent;   /* Notification method */
               int             aio_lio_opcode; /* Operation to be performed;
                                                  lio_listio() only */

               /* Various implementation-internal fields not shown */
           };
           
                  struct sigevent {
           int          sigev_notify; /* Notification method */
           int          sigev_signo;  /* Notification signal */
           union sigval sigev_value;  /* Data passed with
                                         notification */
           void       (*sigev_notify_function) (union sigval);
                            /* Function used for thread
                               notification (SIGEV_THREAD) */
           void        *sigev_notify_attributes;
                            /* Attributes for notification thread
                               (SIGEV_THREAD) */
           pid_t        sigev_notify_thread_id;
                            /* ID of thread to signal (SIGEV_THREAD_ID) */
       };

下面是一个简单的例子:

#include<stdio.h>
#include<stdlib.h>
#include<curses.h>
#include<signal.h>
#include<fcntl.h>
#include<aio.h>
void init_setup (  );
void init_end (  );
void on_input (  );
void do_main (  );
void setup_aio_buffer();
struct aiocb kbcbuf;
int main ( int argc, char *argv[] )
{

        init_setup();
        signal(SIGIO,on_input);
        setup_aio_buffer();
        aio_read(&kbcbuf);
        do_main();
        init_end();
        return EXIT_SUCCESS;
}
        /* ----------  end of function main  ---------- */



void init_setup (  )
{
        initscr();
        crmode();
        noecho();
        clear();
}               /* -----  end of function init_setup  ----- */

void init_end (  )
{
        endwin();
}               /* -----  end of function init_end  ----- */
void on_input (  )
{
        char c;
        char *cp = (char *)kbcbuf.aio_buf;
        if(aio_error(&kbcbuf) != 0)
                perror("reading faild");
        else
        if(aio_return(&kbcbuf) ==1){
                c = *cp;
                if(c == ‘w‘)
                mvaddch(20,20,‘!‘);
                else if(c == ‘e‘)
                mvaddch(20,20,‘0‘);
                else if(c == ‘r‘)
                mvaddch(20,20,‘t‘);
        }
        aio_read(&kbcbuf);
}               /* -----  end of function on_input  ----- */

void do_main (  )
{
        while(1){
                sleep(1);
                move(50,50);
                addstr("do other thing");
                refresh();
        }

}               /* -----  end of function do_main  ----- */

void setup_aio_buffer()
{
        static char input[1];
        kbcbuf.aio_fildes = 0;
        #设置接受输入的buf
        kbcbuf.aio_buf = input;
        #设置接受输入的字节大小
        kbcbuf.aio_nbytes = 1;
        kbcbuf.aio_offset = 0;
        #设置处理输入的方法
        #SIGE_SIGNAL 代表通过发送信号来处理
        kbcbuf.aio_sigevent.sigev_notify =SIGEV_SIGNAL;
        #设置要发送的信号
        kbcbuf.aio_sigevent.sigev_signo = SIGIO;
}


本文出自 “专注linux” 博客,请务必保留此出处http://forlinux.blog.51cto.com/8001278/1533448

linux中实现对输入的异步,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。