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


    我要成为嵌入式高手之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

           /* According to earlier standards */
           #include
           #include
           #include

           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;

    写端:

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int fd = 0;
    5. char tmpbuff[4096] = {0};
    6. mkfifo("/tmp/myfifo", 0777);
    7. fd = open("/tmp/myfifo", O_WRONLY);
    8. if (-1 == fd)
    9. {
    10. perror("fail to open");
    11. return -1;
    12. }
    13. while (1)
    14. {
    15. gets(tmpbuff);
    16. write(fd, tmpbuff, strlen(tmpbuff));
    17. }
    18. close(fd);
    19. return 0;
    20. }

    读端:

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int fd = 0;
    5. int flags = 0;
    6. char *pret = NULL;
    7. ssize_t nsize = 0;
    8. char tmpbuff[4096] = {0};
    9. fd_set rdfds;
    10. fd_set tmpfds;
    11. int ret = 0;
    12. mkfifo("/tmp/myfifo", 0664);
    13. fd = open("/tmp/myfifo", O_RDONLY);
    14. if (-1 == fd)
    15. {
    16. perror("fail to open");
    17. return -1;
    18. }
    19. FD_ZERO(&rdfds);//将文件描述符集合清0
    20. FD_SET(fd, &rdfds);//将文件描述符fd加入到文件描述符集合中
    21. FD_SET(0, &rdfds);//将文件描述符0加入到文件描述符集合中
    22. while (1)
    23. {
    24. tmpfds = rdfds;
    25. ret = select(fd+1, &tmpfds, NULL, NULL, NULL);
    26. if (-1 == ret)
    27. {
    28. perror("fail to select");
    29. return -1;
    30. }
    31. if (FD_ISSET(fd, &tmpfds))//判断文件描述符fd是否还在文件描述符集合中
    32. {
    33. memset(tmpbuff, 0, sizeof(tmpbuff));
    34. read(fd, tmpbuff, sizeof(tmpbuff));
    35. printf("FIFO:%s\n", tmpbuff);
    36. }
    37. if (FD_ISSET(0, &tmpfds))
    38. {
    39. memset(tmpbuff, 0, sizeof(tmpbuff));
    40. gets(tmpbuff);
    41. printf("STDIN:%s\n", tmpbuff);
    42. }
    43. }
    44. close(fd);
    45. return 0;
    46. }

     结果:

    poll 

            1、poll监听的文件描述符没有上限的限制

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

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

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

    #include

    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 */(实际产生的事件)
               };

    读端:

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int fd = 0;
    5. int flags = 0;
    6. char *pret = NULL;
    7. ssize_t nsize = 0;
    8. char tmpbuff[4096] = {0};
    9. struct pollfd fds[2];
    10. int nready = 0;
    11. mkfifo("/tmp/myfifo", 0664);
    12. fd = open("/tmp/myfifo", O_RDONLY);
    13. if (-1 == fd)
    14. {
    15. perror("fail to open");
    16. return -1;
    17. }
    18. fds[0].fd = fd;
    19. fds[0].events = POLLIN;
    20. fds[1].fd = 0;
    21. fds[1].events = POLLIN;
    22. while (1)
    23. {
    24. nready = poll(fds, 2, -1);
    25. if (-1 == nready)
    26. {
    27. perror("fail to poll");
    28. return -1;
    29. }
    30. if (fds[0].revents & POLLIN)
    31. {
    32. memset(tmpbuff, 0, sizeof(tmpbuff));
    33. read(fd, tmpbuff, sizeof(tmpbuff));
    34. printf("FIFO:%s\n", tmpbuff);
    35. }
    36. if (fds[1].revents & POLLIN)
    37. {
    38. memset(tmpbuff, 0, sizeof(tmpbuff));
    39. gets(tmpbuff);
    40. printf("STDIN:%s\n", tmpbuff);
    41. }
    42. }
    43. close(fd);
    44. }

    写端:

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int fd = 0;
    5. char tmpbuff[4096] = {0};
    6. mkfifo("/tmp/myfifo", 0664);
    7. fd = open("/tmp/myfifo", O_WRONLY);
    8. if (-1 == fd)
    9. {
    10. perror("fail to open");
    11. return -1;
    12. }
    13. while (1)
    14. {
    15. gets(tmpbuff);
    16. write(fd, tmpbuff, strlen(tmpbuff));
    17. }
    18. close(fd);
    19. return 0;
    20. }

    结果:

     epoll

    1、epoll_create

    #include

    int epoll_create(int size);

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

    参数:size:事件的个数

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

    2、epoll_ctl

    #include

    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

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

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

    参数:

            epfd:事件表的文件描述符

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

            maxevents:最多存放事件的个数

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

    返回值:

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

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

            失败返回-1

    read.c

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int fd = 0;
    5. int epfd = 0;
    6. struct epoll_event env;//epoll_ctl需要的事件的结构体
    7. int nready = 0;
    8. struct epoll_event retenv[2];
    9. int i = 0;
    10. ssize_t nsize = 0;
    11. char *pret = NULL;
    12. char tmpbuff[4096] = {0};
    13. mkfifo("/tmp/myfifo", 0664);
    14. fd = open("/tmp/myfifo", O_RDONLY);
    15. if (-1 == fd)
    16. {
    17. perror("fail to open");
    18. return -1;
    19. }
    20. /*创建一张2个事件的内核事件表*/
    21. epfd = epoll_create(2);
    22. if (epfd == -1)
    23. {
    24. perror("fail to create");
    25. return -1;
    26. }
    27. /*设置事件结构体的属性*/
    28. env.events = EPOLLIN;
    29. env.data.fd = fd;
    30. /*操作事件*/
    31. epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);
    32. env.events = EPOLLIN;
    33. env.data.fd = 0;
    34. epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &env);
    35. while (1)
    36. {
    37. /*监听事件表中的事件*/
    38. nready = epoll_wait(epfd, retenv, 2, -1);//第二个参数是存放实际产生事件的结构体, 最多存放的个数, 设置监听时间,-1一直监听直到有事件发生
    39. if (-1 == nready)
    40. {
    41. perror("fail to nready");
    42. return -1;
    43. }
    44. for (i = 0; i < nready; ++i)
    45. {
    46. if (retenv[i].data.fd == 0)//判断要操作的流是否为从终端输入
    47. {
    48. memset(tmpbuff, 0, sizeof(tmpbuff));
    49. gets(tmpbuff);
    50. printf("STDIN: %s\n", tmpbuff);
    51. }
    52. if (retenv[i].data.fd == fd)
    53. {
    54. memset(tmpbuff, 0, sizeof(tmpbuff));
    55. read(fd, tmpbuff, sizeof(tmpbuff));
    56. printf("FIFO: %s\n", tmpbuff);
    57. }
    58. }
    59. }
    60. close(fd);
    61. return 0;
    62. }

    write.c

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int fd = 0;
    5. char tmpbuff[4096] = {0};
    6. mkfifo("/tmp/myfifo", 0664);
    7. fd = open("/tmp/myfifo", O_WRONLY);
    8. if (-1 == fd)
    9. {
    10. perror("fail to open");
    11. return -1;
    12. }
    13. while (1)
    14. {
    15. gets(tmpbuff);
    16. write(fd, tmpbuff, strlen(tmpbuff));
    17. }
    18. close(fd);
    19. return 0;
    20. }

     

     

  • 相关阅读:
    【笔试】2022/9/4 网易互联网开发岗满分
    3分钟带你掌握Spring Boot中的定时调度服务
    PyTorch中collate_fn的应用
    pytorch下载离线包的网址
    ChatGPT:理解HTTP请求数据格式:JSON、x-www-form-urlencoded和form-data
    docker 具体操作
    原来Linux这么牛:称霸全球超级电脑 500 强!
    为什么蘑菇街会选择上云?是被动选择还是主动出击?
    如何使用CompletableFuture
    构建OPC UA 可执行模型(2)
  • 原文地址:https://blog.csdn.net/weixin_71850179/article/details/136614068