Unix/Linux系统编程:信号驱动IO

这篇具有很好参考价值的文章主要介绍了Unix/Linux系统编程:信号驱动IO。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

详细信息参考unix/linux系统编程手册第63章 

在I/O多路复用中,进程是通过系统调用(select()或poll())来检查文件描述符上是否可以执行I/O操作。而在信号驱动I/O中,当文件描述符上可执行I/O操作时,进程请求内核为自己发送一个信号。之后进程就可以执行任何其他的任务直到I/O就绪为止,此时内核会发送信号给进程。要使用信号驱动I/O,程序需要按照如下步骤来执行。

1.为内核发送的通知信号安装一个信号处理例程。默认情况下,这个通知信号为SIGIO。

2.设定文件描述符的属主,也就是当文件描述符上可执行I/O时会接收到通知信号的进程或进程组。通常我们让调用进程成为属主。设定属主可通过fcntl()的F_SETOWN操作来完成:

fcntl(STDIN_FILENO,F_SETOWN,getpid()

3.通过设定O_NONBLOCK标志使能非阻塞I/O。

4.通过打开O_ASYNC标志使能信号驱动I/O。这可以和上一步合并为一个操作,因为它们都需要用到fcntl()的F_SETFL操作

flags = fcntl(STDIN_FILENO,F_GETFL);
fcntl(STDIN_FILENO,F_SETFL,flags | O_ASYNC | O_NONBLOCK)

5.调用进程现在可以执行其他的任务了。当I/O操作就绪时,内核为进程发送一个信号,然后调用在第1步中安装好的信号处理例程。

6.信号驱动I/O提供的是边缘触发通知。这表示一旦进程被通知I/O就绪,它就应该尽可能多地执行I/O(例如尽可能多地读取字节)。假设文件描述符是非阻塞式的,这表示需要在循环中执行I/O系统调用直到失败为止,此时错误码为EAGAIN或EWOULDBLOCK。

测试代码一:

#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>

#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif
char buf[1024];
static void sig_func(int sig){
    DEBUG_INFO("signal: %d\n", sig);
    // scanf("%s",buf);
    int res = 0;
    do{
        memset(buf,0,1024);
        res = read(0,buf,sizeof(buf));
        DEBUG_INFO("res = %d,buf = %s",res,buf);
    }while(res > 0);
    int err = errno;
    if(err == EAGAIN){
        DEBUG_INFO("EAGAIN = %d\n",errno);
    }else if(err == EWOULDBLOCK){
        DEBUG_INFO("EWOULDBLOCK = %d\n",errno);
    }
    
    DEBUG_INFO("wait input:");
}

int main(int argc, char **argv)
{
    int flags,j,cnt;
    char ch;
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = sig_func;
    if(sigaction(SIGIO,&sa,NULL)){
        perror("sigaction");
        exit(-1);
    }
    if(fcntl(STDIN_FILENO,F_SETOWN,getpid()) == -1){
        perror("fcntl");
        exit(-1);
    }

    flags = fcntl(STDIN_FILENO,F_GETFL);
    if(fcntl(STDIN_FILENO,F_SETFL,flags | O_ASYNC | O_NONBLOCK) == -1){
        perror("fcntl set");
        exit(-1);
    }
    DEBUG_INFO("wait input:");
    while (1)
    {
        /* code */
        sleep(1);
    }
    return 0;
}

实验结果:

/big/modbus/libmodbus/src_mymodbus/sigio1.c:main:59 -- wait input:
555
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:19 -- signal: 29

/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:25 -- res = 4,buf = 555

/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:25 -- res = -1,buf =
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:29 -- EAGAIN = 11

/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:34 -- wait input:

实验解析:

输入555,sig_func得到信号SIGIO,在此函数中读取输入的数据,因为SIGIO是边沿触发,一旦得到信号,要尽可能多的读取数据,所以直到读完所有的数据,才退出信号处理代码。读取完毕后,的错误码是11,也就是EAGAIN。

 测试二:

#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <ctype.h>

#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif

static volatile sig_atomic_t gotsigio = 0;

static void sig_func(int sig){
    gotsigio = 1;
}

void sleep_ms(int ms){
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = ms * 1000;
    select(0,NULL,NULL,NULL,&tv);
}

int ttySetCbreak(int fd, struct termios *prevTermios)
{
    struct termios t;

    if (tcgetattr(fd, &t) == -1)
        return -1;

    if (prevTermios != NULL)
        *prevTermios = t;

    t.c_lflag &= ~(ICANON | ECHO);
    t.c_lflag |= ISIG;

    t.c_iflag &= ~ICRNL;

    t.c_cc[VMIN] = 1;                   /* Character-at-a-time input */
    t.c_cc[VTIME] = 0;                  /* with blocking */

    if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
        return -1;

    return 0;
}

int main(int argc, char **argv)
{
    int flags,j;
    char ch;
    int res = 0;
    struct termios os;
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = sig_func;
    if(sigaction(SIGIO,&sa,NULL)){
        perror("sigaction");
        exit(-1);
    }
    if(fcntl(STDIN_FILENO,F_SETOWN,getpid()) == -1){
        perror("fcntl");
        exit(-1);
    }

    flags = fcntl(STDIN_FILENO,F_GETFL);
    if(fcntl(STDIN_FILENO,F_SETFL,flags | O_ASYNC | O_NONBLOCK) == -1){
        perror("fcntl set");
        exit(-1);
    }
    if(ttySetCbreak(STDIN_FILENO,&os) == -1){
        perror("ttySetCbreak");
        exit(-1);
    }
    DEBUG_INFO("wait input:");
    while (1)
    {
        /* code */
        sleep_ms(100);
        if(gotsigio){
            do{
                res = read(STDIN_FILENO,&ch,1);
                if(res > 0)
                    printf("ch=%c\n",ch);
            }
            while(res > 0);
            gotsigio = 0;
            if(ch == '#'){
                break;
            }
        }
    }
    DEBUG_INFO("bye bye");
    return 0;
}

测试结果:

ch=a
ch=b
ch=c
ch=d
ch=e
ch=f
ch=#
/big/modbus/libmodbus/src_mymodbus/sigio2.c:main:102 -- bye bye

小结

详细信息参考unix/linux系统编程手册第63章文章来源地址https://www.toymoban.com/news/detail-507427.html

到了这里,关于Unix/Linux系统编程:信号驱动IO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包