day40——IO模型补充

2.4 epoll模型

epoll模型只有linux系统才有

epoll模型只有从linxu内核2.4版本之后才有

epoll从2.4内核到目前的4.X内核,没有更新的模型了,说明epoll模型本身已经很完美了

select的问题:

监视列表无法扩容

监视列表和返回的激活列表混在了一起

效率低下:

select需要自己管理激活的套接字

select查询哪个套接字激活了是一个双重循环,效率低下

select的内核部分,监视的套接字也是一个数组,查询哪个套接字激活了,效率也是低的

poll的问题:

效率低下:

select的内核部分,监视的套接字也是一个数组,查询哪个套接字激活了,效率也是低的

epoll彻底结局了select 和 poll的问题

epoll允许自动扩容

epoll的内核部分是以二叉树存储所有的描述符,所在查看哪个描述符激活的时候,效率很高

epoll会把所有激活的描述符,放在一个数组中,直接提供给用户,编程效率高

原型:int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
函数调用:int count = epoll_wait(fd,数组,监视的描述符的数量,-1)
功能描述:阻塞并等待监视列表中的描述符激活,并且将所有激活的描述符,放在一个数组中提供给我们
参数解析:
    参数 epfd:监视列表
        epoll要求先创建一个文件,然后将所有的要监视的描述符,写入这个文件中,并监视
    参数 events:用来存放所有激活的描述符的数组
    参数 maxevents:最大的监视描述符的数量
    参数 timeout:阻塞时长,单位为毫秒,
        0:表示不阻塞
        -1:表示常阻塞
返回值:激活的描述符的数量     

 epoll如何创建监视列表

原型:int epoll_create(int size);
调用:int epfd = epoll_create(想要监视的描述符数量)
功能描述:创建一个文件,该文件最多能够监视 size个字节的描述符
参数解析:
    参数 size:监视的描述符的最大值
返回值:返回创建出来的文件的描述符
 
 
原型:int epoll_create1(int flags);
调用:int epfd = epoll_create1(EPOLL_CLOEXEC
)
功能描述:创建一个文件,用来监视描述符,该文件能够监视的描述符数量能够自动扩容
epoll如何将被监视的描述符写入文件中

 epoll如何将被监视的描述符写入文件中

原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
调用:epoll_ctl();
功能描述:操作监视列表,可以删除,可以添加,可以指定监视类型(监视可读,监视可写)
参数解析:
    参数 epfd:监视列表
    参数 op:具体针对监视列表的操作行为
        EPOLL_CTL_ADD:将参数 fd 描述符,添加进入epfd监视列表,并且由参数event决定,以何种形式进行监视
        EPOLL_CTL_DEL:将参数 fd 描述符,从epfd监视列表中移除,此时event参数被忽略,可以直接写NULL
        EPOLL_CTL_MOD:根据参数 event 更改 fd描述符的监视类型
    参数 fd:等待操作的描述符
    参数 event:一个结构体指针,结构如下
        struct epoll_event {
            uint32_t     events;      /* Epoll events */ 监视的事件类型
                EPOLLIN:监视描述符是否可读
                EPOLLOUT:监视描述符是否可写
            epoll_data_t data;        /* User data variable */
        };
        
        data 也是一个结构体,结构如下
        
        typedef union epoll_data {
            void        *ptr;
            int          fd;
            uint32_t     u32;
            uint64_t     u64;
        } epoll_data_t;
        
        其中关键数据是:fd
            epoll_event 这个结构体,在 epoll_wait的时候,传入的是这个结构体的数组
            我们判断哪个描述符激活,就是依靠这个fd来判断的
            
            epoll_ctl函数调用的时候,这个events.data.fd 必须和 参数 fd保持一致
#include <myhead.h>

int main(int argc, const char *argv[])
{
    if (argc != 2)
    {
        printf("请输入正确的端口号\n");
        return 1;
    }
    int port = atoi(argv[1]);

    int epfd = epoll_create1(EPOLL_CLOEXEC);
    if (epfd == -1)
    {
        perror("epoll_create1");
        return 1;
    }

    int server = socket(AF_INET, SOCK_STREAM, 0);
    if (server == -1)
    {
        perror("socket");
        return 1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        perror("bind");
        return 1;
    }

    if (listen(server, 100) == -1)
    {
        perror("listen");
        return 1;
    }

    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = server;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, server, &event) == -1)
    {
        perror("epoll_ctl");
        return 1;
    }

    int fd_count = 100;
    struct epoll_event readfds[fd_count];
    while (1)
    {
        int signal_count = epoll_wait(epfd, readfds, fd_count, -1);
        for (int i = 0; i < signal_count; i++)
        {
            int fd = readfds[i].data.fd;
            if (fd == server)
            {
                int client = accept(server, NULL, NULL);
                event.events = EPOLLIN;
                event.data.fd = client;
                if (epoll_ctl(epfd, EPOLL_CTL_ADD, client, &event) == -1)
                {
                    perror("epoll_ctl");
                    close(client);
                }
                else
                {
                    printf("有新客户端连接\n");
                }
            }
            else
            {
                char buf[128] = {0};
                int res = read(fd, buf, 128);
                if (res == 0)
                {
                    printf("有客户端断开连接\n");
                    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
                    close(fd);
                }
                else
                {
                    printf("接收到客户端的消息:%s\n", buf);
                }
            }
        }
    }

    close(server);
    return 0;
}

epoll的代码模型

int epfd = epoll_create1(EPOLL_CLOEXEC)
 
struct epoll_event event
event.events = EPOLLIN(监视类型)
event.data.fd = 想要监视的描述符
epoll_ctl(epfd,EPOLL_CTL_ADD,想要监视的描述符,&event)
 
struct epoll_event readfds;
while(1){
    int signal_count = epoll_wait(epfd,readfds,监视描述符的数量,-1)
    for(遍历readfds){
            判断哪个描述符激活了{
                执行对应的逻辑cv            
            }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值