水平触发,边缘触发

    在linux的IO多路复用中有水平触发,边缘触发两种模式,这两种模式的区别如下:

    水平触发:如果文件描述符已经就绪可以非阻塞的执行IO操作了,此时会触发通知.允许在任意时刻重复检测IO的状态,没有必要每次描述符就绪后尽可能多的执行IO.select,poll就属于水平触发.

    边缘触发:如果文件描述符自上次状态改变后有新的IO活动到来,此时会触发通知.在收到一个IO事件通知后要尽可能多的执行IO操作,因为如果在一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取到就绪的描述符.信号驱动式IO就属于边缘触发.

    epoll既可以采用水平触发,也可以采用边缘触发.

    大家可能还不能完全了解这两种模式的区别,我们可以举例说明:一个管道收到了1kb的数据,epoll会立即返回,此时读了512字节数据,然后再次调用epoll.这时如果是水平触发的,epoll会立即返回,因为有数据准备好了.如果是边缘触发的不会立即返回,因为此时虽然有数据可读但是已经触发了一次通知,在这次通知到现在还没有新的数据到来,直到有新的数据到来epoll才会返回,此时老的数据和新的数据都可以读取到(当然是需要这次你尽可能的多读取).

    下面我们还从电子的角度来解释一下:

    水平触发:也就是只有高电平(1)或低电平(0)时才触发通知,只要在这两种状态就能得到通知.上面提到的只要有数据可读(描述符就绪)那么水平触发的epoll就立即返回.

    边缘触发:只有电平发生变化(高电平到低电平,或者低电平到高电平)的时候才触发通知.上面提到即使有数据可读,但是没有新的IO活动到来,epoll也不会立即返回.

/*************************************************************************
> File Name: t_select.c
> Author: liuxingen
> Mail: liuxingen@nsfocus.com 
> Created Time: 2014年08月11日 星期一 21时22分32秒
************************************************************************/

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/time.h>
#include<sys/select.h>
#include<string.h>
#include<errno.h>

int main(int argc, char *argv[])
{
    struct timeval timeout;
    char buf[10];
    fd_set readfds;
    int nread, nfds, ready, fd;

    while(1)
    {
        timeout.tv_sec = 20L;
        timeout.tv_usec = 0;

        fd = 0;     //stdin
        nfds = fd + 1;
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);

        ready = select(nfds, &readfds, NULL, NULL, &timeout);
        if(ready == -1 && errno == EINTR)
        {
            continue;
        }else if(ready == -1)
        {
            fprintf(stderr, "select error:%s\n", strerror(errno));
        }
        for(fd = 0; fd < nfds; fd++)
        {
            if(FD_ISSET(fd, &readfds))
            {
                nread = read(fd, buf, 9);
                buf[nread] = '\0';
                puts(buf);
            }
        }
    }

    return 0;
}
    上面的示例中每次最多读取9个字节,当我们一次输入了20个字节那么分三次调用select,每次都能立即读取到数据,这也就证明了水平触发中只要数据准备好了那么select都会立即返回.

lxg@remoter:~/station$ ./t_select 
ni hao ma ,wo hen hao a ,ni ne ???
ni hao ma
 ,wo hen 
hao a ,ni
 ne ???

^C

/*************************************************************************
> File Name: demo_sigio.c
> Author: liuxingen
> Mail: liuxingen@nsfocus.com 
> Created Time: 2014年08月14日 星期四 21时32分03秒
************************************************************************/

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

static int g_fd;

static void sigio_handler(int signum)
{
    char buf[8] = {0};

    if(read(g_fd, buf, 7) < 0)
    {
        fprintf(stderr, "read error:%s\n", strerror(errno));
    }else
    {
        printf("sigio recv:%s\n", buf);
    }
}
int main(int argc, char *argv[])
{
    struct sigaction act;
    int flags, i = 1, fds[2];
    pid_t pid;

    if(pipe(fds) < 0)
    {
        fprintf(stderr, "pipe error:%s\n", strerror(errno));
        return 1;
    }
    if((pid = fork()) > 0)
    {
        close(fds[1]);
        dup2(fds[0], g_fd);

        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_RESTART;
        act.sa_handler = sigio_handler;
        if(sigaction(SIGIO, &act, NULL) == -1)
        {
            fprintf(stderr, "sigaction error:%s\n", strerror(errno));
            return 1;
        }

        if(fcntl(g_fd, F_SETOWN, getpid()) == -1)
        {
            fprintf(stderr, "fcntl F_SETOWN error:%s\n", strerror(errno));
            return 1;
        }

        flags = fcntl(g_fd, F_GETFL);
        if(fcntl(g_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
        {
            fprintf(stderr, "fcntl F_GETFL error:%s\n", strerror(errno));
            return 1;
        }
        while(1)
        {
            sleep(10);
        }
    }else
    {
        char buf[20] = {0};
        close(fds[0]);
        for(i = 0; i < 3; i++)
        {
            snprintf(buf, 20, "this is loop %d", i);
            write(fds[1], buf, strlen(buf));
            printf("loop %d\n", i);
            sleep(3);
        }
    }

    return 0;
}

    因为信号驱动IO属于边缘触发,所以上面以信号驱动来举例.从下面的输出可以得知:我们一次写入14个字节,但是一次我们每次只读取7字节,除非等到下一次数据写入不然不会再触发SIGIO信号,并且上一次未读完的数据会在下次继续被读取.

lxg@remoter:~/station$ ./demo_sigio 
loop 0
sigio recv:this is
loop 1
sigio recv: loop 0
loop 2
sigio recv:this is
sigio recv: loop 1
^C





  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值