linux多路IO--epoll(一)--水平触发和边沿触发

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
在这里插入图片描述
**

一 why

**
考虑这样一种场景,假设某个文件描述符发生了可读事件,一次可读的文件大小为1000Bytes,但是呢,实际上我们每次只会从这个文件描述符中读取500 Bytes;显然这个时候,该文件中还会剩下500个Bytes,这个时候,我们应该如何去读这个文件描述符?或者说此时epoll_wait函数会有正确的返回值吗(假设我们设置epoll_wait为永久等待方式)。
回答上面的问题,就需要引入epoll的触发方式了,分别是水平触发和边沿触发。
**

二 what

**
epoll水平触发: 只要监听的文件描述符中有数据,就会触发epoll_wait有返回值,这是默认的epoll_wait的方式;
epoll边沿触发 : 只有监听的文件描述符的读/写事件发生,才会触发epoll_wait有返回值;
通过epoll_ctl函数,设置该文件描述符的触发状态即可

//水平触发
evt.events = EPOLLIN;    // LT 水平触发 (默认) EPOLLLT
evt.data.fd = pfd[0];

//边沿触发
evt.events = EPOLLIN | EPOLLET;    // ET 边沿触发
evt.data.fd = pfd[0];

三 how
代码思想
(1) 创建一个无名管道,供父进程和子进程间通信
(2) 创建一个子进程,向管道中写入数据
(3) 父进程读取管道数据
(1) epoll水平触发代码如下

#include "stdio.h"
#include "stdlib.h"
#include <string.h>
#include <unistd.h>
#include "sys/types.h"
#include <sys/epoll.h>

#define MAXLINE 10

int main(void)
{
    pid_t pid;
    int epfd = -1, i, rval;
    int pfd[2];
    char buf[MAXLINE], ch = 'a';

    //创建一个无名管道
    pipe(pfd);

    pid = fork();

    if (pid == 0) {  // child process
        close(pfd[0]);

        while (1) {
            // aaaa\n
            for (i = 0; i < MAXLINE / 2; i++) {
                buf[i] = ch;
            }
            buf[i-1] = '\n';
            ch++;

            // bbbb\n
            for (; i < MAXLINE; i++) {
                buf[i] = ch;
            }
            buf[i-1] = '\n';
            ch++;
            // aaaa\nbbbb\n
            write(pfd[1], buf, sizeof(buf));
            sleep(5);
        }
        close(pfd[1]);
    } else if (pid > 0) {  // parent process
        struct epoll_event evt;
        struct epoll_event evts[10];
        int res, len;
        
        close(pfd[1]);
        epfd = epoll_create(10);
        if (epfd < 0) {
            perror("epoll_create error");
        }

        evt.events = EPOLLIN;    // LT 水平触发 (默认)
        evt.data.fd = pfd[0];
        rval = epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &evt);
        if (rval < 0) {
            perror("epoll_ctl error");
            return 0;
        }

        while (1) {
            memset(buf, 0, sizeof(buf));
            res = epoll_wait(epfd, evts, 10, -1);
            printf("res %d\n", res);
            if (res < 0) {
                perror("epoll_wait error");
                return 0;
            }
            if (evts[0].data.fd == pfd[0]) {
                len = read(pfd[0], buf, MAXLINE/2);
                write(STDOUT_FILENO, buf, len);
            }
        }
        close(pfd[0]);
    }

    while(1);
    return 0;
}

在子进程中,我们一次向管道写入10个字符数据,为"aaaa\nbbbb\n";每隔5s写入10个字符数据;
在父进程中,我们从管道中一次读取5个字符数据,因为我们采用的是水平触发方式,因此在5s的周期内,会先读取5个字符数据,读完之后,因为文件描述符中仍然有数据,epoll_wait会立即返回,会继续读取接下来的5个数据,试验现象就是会先打印如下:
在这里插入图片描述
隔5s之后,继续打印"cccc\ndddd\n",如下
在这里插入图片描述
在这种情况下,管道中的数据如下,每次子进程写完管道之后,父进程会立即将管道中的数据读出来,在5s周期以内的剩余时间内,管道中的数据都为空
在这里插入图片描述
(2) epoll边沿触发代码

#include "stdio.h"
#include "stdlib.h"
#include <string.h>
#include <unistd.h>
#include "sys/types.h"
#include <sys/epoll.h>

#define MAXLINE 10

int main(void)
{
    pid_t pid;
    int epfd = -1, i, rval;
    int pfd[2];
    char buf[MAXLINE], ch = 'a';

    //创建一个无名管道
    pipe(pfd);

    pid = fork();

    if (pid == 0) {  // child process
        close(pfd[0]);

        while (1) {
            // aaaa\n
            for (i = 0; i < MAXLINE / 2; i++) {
                buf[i] = ch;
            }
            buf[i-1] = '\n';
            ch++;

            // bbbb\n
            for (; i < MAXLINE; i++) {
                buf[i] = ch;
            }
            buf[i-1] = '\n';
            ch++;
            // aaaa\nbbbb\n
            write(pfd[1], buf, sizeof(buf));
            sleep(5);
        }
        close(pfd[1]);
    } else if (pid > 0) {  // parent process
        struct epoll_event evt;
        struct epoll_event evts[10];
        int res, len;
        
        close(pfd[1]);
        epfd = epoll_create(10);
        if (epfd < 0) {
            perror("epoll_create error");
        }

        evt.events = EPOLLIN | EPOLLET;    // ET 边沿触发
        evt.data.fd = pfd[0];
        rval = epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &evt);
        if (rval < 0) {
            perror("epoll_ctl error");
            return 0;
        }

        while (1) {
            memset(buf, 0, sizeof(buf));
            res = epoll_wait(epfd, evts, 10, -1);
            printf("res %d\n", res);
            if (res < 0) {
                perror("epoll_wait error");
                return 0;
            }
            if (evts[0].data.fd == pfd[0]) {
                len = read(pfd[0], buf, MAXLINE/2);
                write(STDOUT_FILENO, buf, len);
            }
        }
        close(pfd[0]);
    }

    while(1);
    return 0;
}

因为此时采用的是边沿触发方式,当父进程先读完管道中的5个字符后,由于子进程没有立即向管道中写入字符(间隔5s后才会第二次写入),所以此时父进程会先读到5个字符"aaaa\n",
在这里插入图片描述
隔5s之后,再读到5个字符"bbbb\n"
在这里插入图片描述
在这种情况下,管道中的数据如下,显然随着时间越来越长,管道中的数据会越来越多。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值