TCP多线程模型、IO模型(select、poll、epoll)

本文详细探讨了TCP并发模型,包括多线程模型的资源开销、阻塞IO、非阻塞IO、异步IO和多路复用IO(如select、poll和epoll)的工作原理及优缺点,展示了如何利用这些机制提高系统效率和并发处理能力。
摘要由CSDN通过智能技术生成

我要成为嵌入式高手之3月11日Linux高编第十九天!!
————————————————————————————

TCP并发模型

一、TCP多线程模型:

缺点:创建线程会带来资源开销,能够现的并发量比较有限

二、IO模型:

1、阻塞IO

        没有数据到来时,可以让任务挂起,节省CPU资源开销,提升系统效率

2、非阻塞IO

        fcntl

        程序未接收到数据时一直执行,效率很低

3、异步IO

        只能绑定一个文件描述符用来读取数据

4、多路复用IO

        多个IO用一个函数接口监测

        select

                1、select监听的集合中的文件描述符有上限限制

                2、select有内核层向用户层数据空间拷贝的过程,占用系统资源开销

                3、select必须轮询检测产生事件的文件描述符

                4、select只能工作在水平触发模式,无法工作在边沿触发(高速模式)

       /* According to POSIX.1-2001, POSIX.1-2008 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

        功能:监听文件描述符中是否有文件描述符变成ready状态

        参数:

                nfds:最大文件描述符的值+1;

                readfds:读文件描述符的首地址

                writefds:写文件描述符的首地址

                exceptfds:其余文件描述符集合

                timeout:等待的市场,NULL一直等待

        返回值:成功返回文件描述符集合中文件描述符的个数,失败返回-1;

       void FD_CLR(int fd, fd_set *set);

        功能:将文件描述符fd从集合中清除

        int  FD_ISSET(int fd, fd_set *set);

        功能:判断文件描述符fd是否能在集合中

        void FD_SET(int fd, fd_set *set);

        功能:将文件描述符fd加入到集合中

        void FD_ZERO(fd_set *set);

        将文件描述符集合清0;

写端:

#include "head.h"

int main(void)
{
    int fd = 0;
    char tmpbuff[4096] = {0};

    mkfifo("/tmp/myfifo", 0777);

    fd = open("/tmp/myfifo", O_WRONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    while (1)
    {
        gets(tmpbuff);
        write(fd, tmpbuff, strlen(tmpbuff));
    }
    close(fd);

    return 0;
}

读端:

#include "head.h"

int main(void)
{
    int fd = 0;
    int flags = 0;
    char *pret = NULL;
    ssize_t nsize = 0;
    char tmpbuff[4096] = {0};
    fd_set rdfds;
    fd_set tmpfds;
    int ret = 0;

    mkfifo("/tmp/myfifo", 0664);
    
    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    FD_ZERO(&rdfds);//将文件描述符集合清0
    FD_SET(fd, &rdfds);//将文件描述符fd加入到文件描述符集合中
    FD_SET(0, &rdfds);//将文件描述符0加入到文件描述符集合中

    while (1)
    {
        tmpfds = rdfds;
        ret = select(fd+1, &tmpfds, NULL, NULL, NULL);
        if (-1 == ret)
        {
            perror("fail to select");
            return -1;
        }

        if (FD_ISSET(fd, &tmpfds))//判断文件描述符fd是否还在文件描述符集合中
        {
            memset(tmpbuff, 0, sizeof(tmpbuff));
            read(fd, tmpbuff, sizeof(tmpbuff));
            printf("FIFO:%s\n", tmpbuff);
        }

        if (FD_ISSET(0, &tmpfds))
        {
            memset(tmpbuff, 0, sizeof(tmpbuff));
            gets(tmpbuff);
            printf("STDIN:%s\n", tmpbuff);
        }

    }

    close(fd);

    return 0;
}

 结果:

poll 

        1、poll监听的文件描述符没有上限的限制(内部实现是个链表)

        2、有内核层向用户层数据空间拷贝的过程,占用系统资源开销

        3、必须轮询检测产生事件的文件描述符

        4、只能工作在水平触发模式,无法工作在边沿触发(高速模式)

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

功能:监听文件描述符集合是否有事件发生

参数:

        fds:监听文件描述符集合数组的空间首地址

        nfds:监听文件描述符集合元素个数

        timeout:等待的时间(-1 一直等待)

返回值:成功返回产生事件的文件描述符的个数,失败返回-1;

           struct pollfd {
               int   fd;              /* file descriptor */(监听的文件描述符)
               short events;     /* requested events */(要监听的事件)
                        POLLIN:是否可读
                        POLLOUT:是否可写
               short revents;    /* returned events */(实际产生的事件)
           };

读端:

#include "head.h"

int main(void)
{
	int fd = 0;
	int flags = 0;
	char *pret = NULL;
	ssize_t nsize = 0;
	char tmpbuff[4096] = {0};
	struct pollfd fds[2];
	int nready = 0;

	mkfifo("/tmp/myfifo", 0664);

	fd = open("/tmp/myfifo", O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}
	
	fds[0].fd = fd;
	fds[0].events = POLLIN;
	fds[1].fd = 0;
	fds[1].events = POLLIN;

	while (1)
	{
		nready = poll(fds, 2, -1);
		if (-1 == nready)
		{
			perror("fail to poll");
			return -1;
		}

		if (fds[0].revents & POLLIN)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			read(fd, tmpbuff, sizeof(tmpbuff));
			printf("FIFO:%s\n", tmpbuff);
		}
	
		if (fds[1].revents & POLLIN)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			gets(tmpbuff);
			printf("STDIN:%s\n", tmpbuff);
		}
	}

	close(fd);
}

写端:

#include "head.h"

int main(void)
{
	int fd = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/myfifo", 0664);

	fd = open("/tmp/myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	while (1)
	{
		gets(tmpbuff);
		write(fd, tmpbuff, strlen(tmpbuff));
	}

	close(fd);

	return 0;
}

结果:

 epoll

1、epoll没有文件描述符上限限制

2、epoll创建内核监听事件表,所以只需要在内核空间完成数据拷贝即可

3、epoll会将产生事件的文件描述符对应的事件直接返回(retenv)

4、epoll不仅可以工作在水平触发模式(默认低速模式),还可以触发在边沿触发模式(高速模式)(EPOLLIN | EPOLLET)

1、epoll_create

#include <sys/epoll.h>

int epoll_create(int size);

功能:创建一张内核事件表

参数:size:事件的个数

返回值:成功返回文件描述符,失败返回-1;

2、epoll_ctl

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:维护epoll事件表

参数:

        epfd:事件表的文件描述符

        op:

                EPOLL_CTL_ADD添加事件

                EPOLL_CTL_MOD修改事件

                EPOLL_CTL_DEL删除事件

        fd:操作的文件描述符

        event:事件对应的事件

           typedef union epoll_data {
               void           *ptr;
               int              fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;        /* Epoll events */
               epoll_data_t data;        /* User data variable */
           };

返回值:成功0,失败-1;

events:

data:要操作的文件的文件描述符 

3、epoll_wait

#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

功能:监听事件表中的事件

参数:

        epfd:事件表的文件描述符

        events:存放实际产生事件的数组空间首地址

        maxevents:最多存放事件的个数

        timeout:设定监听的时间(超过该时间不再监听)-1一直监听直到有事件发生

返回值:

        成功返回实际产生事件的文件描述符的个数

        时间到达还没有事件产生返回0

        失败返回-1

read.c

#include "head.h"

int main(void)
{
    int fd = 0;
    int epfd = 0;
    struct epoll_event env;//epoll_ctl需要的事件的结构体
    int nready = 0;
    struct epoll_event retenv[2];
    int i = 0;
    ssize_t nsize = 0;
    char *pret = NULL;
    char tmpbuff[4096] = {0};


    mkfifo("/tmp/myfifo", 0664);
    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    /*创建一张2个事件的内核事件表*/
    epfd = epoll_create(2);
    if (epfd == -1)
    {
        perror("fail to create");
        return -1;
    }

    /*设置事件结构体的属性*/
    env.events = EPOLLIN;
    env.data.fd = fd;
    /*操作事件*/
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);

    env.events = EPOLLIN;
    env.data.fd = 0;
    epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &env);

    while (1)
    {
        /*监听事件表中的事件*/
        nready = epoll_wait(epfd, retenv, 2, -1);//第二个参数是存放实际产生事件的结构体, 最多存放的个数, 设置监听时间,-1一直监听直到有事件发生
        if (-1 == nready)
        {
            perror("fail to nready");
            return -1;
        }

        for (i = 0; i < nready; ++i)
        {
            if (retenv[i].data.fd == 0)//判断要操作的流是否为从终端输入
            {
                memset(tmpbuff, 0, sizeof(tmpbuff));
                gets(tmpbuff);
                printf("STDIN: %s\n", tmpbuff);
            }

            if (retenv[i].data.fd == fd)
            {
                memset(tmpbuff, 0, sizeof(tmpbuff));
                read(fd, tmpbuff, sizeof(tmpbuff));
                printf("FIFO: %s\n", tmpbuff);
            }
        }
    }

    close(fd);

    return 0;
}

write.c

#include "head.h"

int main(void)
{
    int fd = 0;
    char tmpbuff[4096] = {0};

    mkfifo("/tmp/myfifo", 0664);

    fd = open("/tmp/myfifo", O_WRONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }
    
    while (1)
    {
        gets(tmpbuff);
        write(fd, tmpbuff, strlen(tmpbuff));
    }
    close(fd);

    return 0;
}

  • 35
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值