epoll 驚群處理

来源:http://www.cnblogs.com/MaAce/archive/2017/11/09/7755749.html
-Advertisement-
Play Games

1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <sys/epoll.h> 4 #include <netdb.h> 5 #include <string.h> 6 #include <stdio.h> 7 #include ...


  1 #include <sys/types.h>
  2 #include <sys/socket.h>
  3 #include <sys/epoll.h>
  4 #include <netdb.h>
  5 #include <string.h>
  6 #include <stdio.h>
  7 #include <unistd.h>
  8 #include <fcntl.h>
  9 #include <stdlib.h>
 10 #include <errno.h>
 11 #include <sys/wait.h>
 12 #include <unistd.h>
 13 #include <semaphore.h>
 14 #include <sys/shm.h>
 15 #define IP   "127.0.0.1"
 16 #define PORT  8888
 17 #define PROCESS_NUM 4
 18 #define MAXEVENTS 64
 19 
 20 static int
 21 create_and_bind ()
 22 {
 23     int fd = socket (PF_INET, SOCK_STREAM, 0);
 24     struct sockaddr_in serveraddr;
 25     serveraddr.sin_family = AF_INET;
 26     inet_pton (AF_INET, IP, &serveraddr.sin_addr);
 27     serveraddr.sin_port = htons (PORT);
 28     bind (fd, (struct sockaddr *) &serveraddr, sizeof (serveraddr));
 29     return fd;
 30 }
 31 
 32 static int
 33 make_socket_non_blocking (int sfd)
 34 {
 35     int flags, s;
 36     flags = fcntl (sfd, F_GETFL, 0);
 37     if (flags == -1)
 38     {
 39         perror ("fcntl");
 40         return -1;
 41     }
 42     flags |= O_NONBLOCK;
 43     s = fcntl (sfd, F_SETFL, flags);
 44     if (s == -1)
 45     {
 46         perror ("fcntl");
 47         return -1;
 48     }
 49     return 0;
 50 }
 51 
 52 void
 53 worker (int sfd, int efd, struct epoll_event *events, int k, sem_t * sem)
 54 {
 55     /* The event loop */
 56     struct epoll_event event;
 57     // struct epoll_event *events;
 58     efd = epoll_create (MAXEVENTS);
 59     if (efd == -1)
 60     {
 61         perror ("epoll_create");
 62         abort ();
 63     }
 64     int epoll_lock = 0;
 65     while (1)
 66     {
 67         int n, i;
 68         int s;
 69         event.data.fd = sfd;
 70         event.events = EPOLLIN;
 71         if (0 == sem_trywait (sem))
 72         {
 73             //拿到鎖的進程將listen 描述符加入epoll
 74             if (!epoll_lock)
 75             {
 76                 fprintf (stderr, "%d  >>>get lock\n", k);
 77                 s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
 78                 if (s == -1)
 79                 {
 80                     perror ("epoll_ctl");
 81                     abort ();
 82                 }
 83                 epoll_lock = 1;
 84             }
 85         }
 86         else
 87         {
 88             fprintf (stderr, "%d not lock\n", k);
 89             //沒有拿到鎖的進程 將lisfd 從epoll 中去掉
 90             if (epoll_lock)
 91             {
 92                 fprintf (stderr, "worker  %d return from epoll_wait!\n", k);
 93                 if (-1 == epoll_ctl (efd, EPOLL_CTL_DEL, sfd, &event))
 94                 {
 95                     if (errno == ENOENT)
 96                     {
 97                         fprintf (stderr, "EPOLL_CTL_DEL\n");
 98                     }
 99                 }
100                 epoll_lock = 0;
101             }
102         }
103         //epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
104         // fprintf(stderr, "ok\n");
105         //不能設置為-1  為了能讓拿不到鎖的進程再次拿到鎖
106         n = epoll_wait (efd, events, MAXEVENTS, 300);
107         for (i = 0; i < n; i++)
108         {
109             if (sfd == events[i].data.fd)
110             {
111                 /* We have a notification on the listening socket, which means one or more incoming connections. */
112                 struct sockaddr in_addr;
113                 socklen_t in_len;
114                 int infd;
115                 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
116                 in_len = sizeof in_addr;
117                 while ((infd = accept (sfd, &in_addr, &in_len)) > 0)
118                 {
119                     fprintf(stderr, "get one\n");
120                     close (infd);
121                 }
122             }
123         }
124         if (epoll_lock)
125         {
126             //這裡將鎖釋放
127             sem_post (sem);
128             epoll_lock = 0;
129             epoll_ctl (efd, EPOLL_CTL_DEL, sfd, &event);
130         }
131     }
132 }
133 
134 int
135 main (int argc, char *argv[])
136 {
137     int shmid;
138     sem_t *acctl;
139     //建立共用記憶體
140     shmid = shmget (IPC_PRIVATE, sizeof (sem_t), 0600);
141     acctl = (sem_t *) shmat (shmid, 0, 0600);
142     //進程間信號量初始化   要用到上面的共用記憶體
143     sem_init (acctl, 1, 1);
144     int sfd, s;
145     int efd;
146     // struct epoll_event event;
147     // struct epoll_event *events;
148     sfd = create_and_bind ();
149     if (sfd == -1)
150     {
151         abort ();
152     }
153     s = make_socket_non_blocking (sfd);
154     if (s == -1)
155     {
156         abort ();
157     }
158     s = listen (sfd, SOMAXCONN);
159     if (s == -1)
160     {
161         perror ("listen");
162         abort ();
163     }
164     efd = 0;
165     int k;
166     for (k = 0; k < PROCESS_NUM; k++)
167     {
168         printf ("Create worker %d\n", k + 1);
169         int pid = fork ();
170         if (pid == 0)
171         {
172             struct epoll_event *events;
173             events = calloc (MAXEVENTS, sizeof (struct epoll_event));
174             worker (sfd, efd, events, k, acctl);
175             break;
176         }
177     }
178     int status;
179     wait (&status);
180     close (sfd);
181     return EXIT_SUCCESS;
182 }
183 /*
184  * 這裡處理驚群 用到了進程的鎖(信號量, 共用記憶體), 根據試驗的結果多個進程時accept接收客戶端連接的效率並沒有提高太多
185  * 但是處理其他可讀可寫(非監聽描述符)時, 要比單個進程要快很多。
186 */
187 
188  
View Code

  在早期的kernel中, 多線程或多進程調用accept就會出現如下情況, 當前多個進程阻塞在accept中, 此時有客戶端連接時, 內核就會通知阻塞在accept的所有進程, 這時就會造成驚群現象, 也就是所有accept都會返回 但是只有一個能拿到有效的文件描述符, 其他進程最後都會返回無效描述符。但在linux kernel 版本2.6 以上時, accept驚群的問題已經解決, 大致方案就是選一個阻塞在accept的進程返回。

  但是在IO復用中, select/poll/epoll 還是存在這種現象,其原因就是這些阻塞函數造成了以上同樣的問題。這裡就給出了類似Nginx的解決方案, 給監聽描述符競爭枷鎖, 保證只有一個進程處理監聽描述符。 這裡還可以控制鎖的頻率,如果一個進程接受連接的數量達到一定數量就不再申請鎖。  這裡要註意的是epoll_create的位置要在fork之後的子進程中, 這是因為若在父進程中create 那麼fork之後子進程保留這個描述符副本,epoll_create其實是內核中建立的文件系統 保存在內核中, 那麼其子進程都會共用這個文件系統, 那麼多任務的epoll_ctl就會出現問題。子進程中建立各自的epoll fd 就可以避免這種情況。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • (1)sudo add-apt-repository ppa:fkrull/deadsnakes(2)sudo apt-get update(3)sudo apt-get install python3.5(4)sudo cp /usr/bin/python /usr/bin/python_bak, ...
  • 1 什麼是Nginx Nginx ("engine x") 是一個高性能的 HTTP和反向代理伺服器,也是一個 IMAP/POP3/SMTP 伺服器。 <!--polaris office 7540 --> 很多大網站都是使用nginx做反向代理,應用非常廣泛。 Nginx是一款高性能的http 服 ...
  • 本文轉載於的tietao的博客!!!http://blog.csdn.net/tietao/article/details/8172411 *******防止自己忘記,固備之。 一句話:基於速度問題,電腦使用硬碟存儲程式,運行時,在記憶體中分配空間給變數,載入程式到記憶體中,在記憶體中執行程式。單片機使用 ...
  • man命令是Linux下的幫助指令,通過man指令可以查看Linux中的指令幫助、配置文件幫助和編程幫助等信息 可以按章節來搜索內容: man 1: 用戶命令(ls,cd,cp,rm,tar等) man 2: 系統調用(與linux內核相關的,比如open函數,read函數,poll函數等) man ...
  • 在上一篇隨筆中記錄瞭如何在Centos7上安裝MongoDB資料庫,這一篇我們就一起來學學基本的操作命令。 安裝完成後,shell互動式下輸入mongo就可以直接無密碼登錄到資料庫。 創建一個test資料庫例子: db.dropDatabase(); #刪除當前使用資料庫 db.stats(); # ...
  • stm32燒錄常用的方式一般為ST-LINK(或者J-tag)下載模擬和ISP下載 一、模擬器下載 模擬器分為J-TAG和SWD模擬,SWD模擬只需要4根線(VCC、GND、CLK、DATA)就可以了,傳輸速率也相當更快,是模擬調試的首選。模擬器的軟體設置網上一大堆,這裡不再贅述。J-TAG模擬用到 ...
  • Nmon(得名於 Nigel 的監控器)是IBM的員工 Nigel Griffiths 為 AIX 和 Linux 系統開發的一款電腦性能系統監控工具。Nmon 可以把操作系統的統計數據展示在屏幕上或者存儲到一份數據文件里,來幫助瞭解電腦資源的使用情況、調整方向和系統瓶頸。這個系統基準測試工具只 ...
  • 1、firewalld的基本使用 啟動: systemctl start firewalld 查看狀態: systemctl status firewalld 停止: systemctl disable firewalld 禁用: systemctl stop firewalld 2.systemc ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...