epoll 轉kqueue的用法介紹和實例 實現跨平臺Macos

網上關于kqueue的博客很少 我來補充一個例子echo 的例子

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<sys/event.h>
#include<sys/types.h>
#include<unistd.h>
#include<ctype.h>#define MAXLEN 1024
#define SERV_PORT 8000
#define MAX_OPEN_FD 1024// 錯誤退出的工具函數
int quit(const char *msg){perror(msg);exit(1);
}void setNonBlock(int fd) {int flags = fcntl(fd, F_GETFL, 0);int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}const static int FD_NUM = 2; // 兩個文件描述符,分別為標準輸入與輸出
const static int BUFFER_SIZE = 1024; // 緩沖區大小// 完全以IO復用的方式讀入標準輸入流數據,輸出到標準輸出流中
int main(){int  listenfd,connfd,efd,ret;char buf[MAXLEN];struct sockaddr_in cliaddr,servaddr;socklen_t clilen = sizeof(cliaddr);struct kevent tep[2],ep[MAX_OPEN_FD];listenfd = socket(AF_INET,SOCK_STREAM,0);servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,20);struct kevent changes[FD_NUM];struct kevent events[FD_NUM];// 創建一個kqueueint kq;
//    if( (kq = kqueue()) == -1 ) quit("kqueue()");kq = kqueue();//kqueue(); 對應epoll_create// 設置為非阻塞setNonBlock(listenfd);// 注冊監聽事件int k = 0;// EV_SET代替epoll//tep.events = EPOLLIN;//tep.data.fd = connfd;EV_SET(&changes[k++], listenfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)listenfd);EV_SET(&changes[k++], listenfd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)listenfd);kevent(kq, changes, FD_NUM, NULL, 0, NULL);//kevent 可以同時代替epoll_ctl和epoll_wait 生成的實例也就是調用epoll_ctl的時候只需要第2第3 參數 而代替epoll_wait的時候需要第4第5參數int nev, nread, nwrote = 0; // 發生事件的數量, 已讀字節數, 已寫字節數char buffer[BUFFER_SIZE];int lastActive_;const int kMaxEvents = 2000;struct kevent activeEvs_[kMaxEvents];while(1){//lastActive_ 活躍的事件數量lastActive_ = kevent(kq, NULL, 0, activeEvs_, kMaxEvents, NULL); // 已經就緒的文件描述符數量 epoll_wait
//        if( nev <= 0 ) quit("kevent()");int i;for(i=0; i<lastActive_; i++){struct kevent event = activeEvs_[i];if( event.flags & EV_ERROR ) quit("Event error");int ev_fd = (int)(intptr_t)activeEvs_[i].udata;if (ev_fd == listenfd ){connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);printf("connfd=%d",connfd);setNonBlock(connfd);EV_SET(&changes[0], connfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)connfd);EV_SET(&changes[1], connfd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)connfd);kevent(kq, changes, 2, NULL, 0, NULL);}// 否則,讀取數據else{int bytes = read(ev_fd,buf,MAXLEN);// 客戶端關閉連接if (bytes == 0){close(ev_fd);printf("client[%d] closed\n", i);}else{for (int j = 0; j < bytes; ++j){buf[j] = toupper(buf[j]);//把小寫字母裝換為大寫}// 向客戶端發送數據write(ev_fd,buf,bytes);}}}}return 0;
}

struct kevent 結構體內容如下:

struct kevent {uintptr_t       ident;          /* identifier for this event,比如該事件關聯的文件描述符 */int16_t         filter;         /* filter for event,可以指定監聽類型,如EVFILT_READ,EVFILT_WRITE,EVFILT_TIMER等 */uint16_t        flags;          /* general flags ,可以指定事件操作類型,比如EV_ADD,EV_ENABLE, EV_DELETE等 */uint32_t        fflags;         /* filter-specific flags */intptr_t        data;           /* filter-specific data */void            *udata;         /* opaque user data identifier,可以攜帶的任意數據 */
};

EV_SET 是用于初始化kevent結構的便利宏:

EV_SET(&kev, ident, filter, flags, fflags, data, udata);

kevent 是IO復用的函數,其簽名為:

int kevent(int kq, const struct kevent *changelist, // 監視列表int nchanges, // 長度struct kevent *eventlist, // kevent函數用于返回已經就緒的事件列表int nevents, // 長度const struct timespec *timeout); // 超時限制

附上原epoll的實例方便對比

#include<stdio.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<ctype.h>
#define MAXLEN 1024
#define SERV_PORT 8000
#define MAX_OPEN_FD 1024int main(int argc,char *argv[])
{int  listenfd,connfd,efd,ret;char buf[MAXLEN];struct sockaddr_in cliaddr,servaddr;socklen_t clilen = sizeof(cliaddr);struct epoll_event tep,ep[MAX_OPEN_FD];listenfd = socket(AF_INET,SOCK_STREAM,0);servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,20);// 創建一個epoll fdefd = epoll_create(MAX_OPEN_FD);tep.events = EPOLLIN;tep.data.fd = listenfd;// 把監聽socket 先添加到efd中ret = epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);// 循環等待for (;;){// 返回已就緒的epoll_event,-1表示阻塞,沒有就緒的epoll_event,將一直等待size_t nready = epoll_wait(efd,ep,MAX_OPEN_FD,-1);for (int i = 0; i < nready; ++i){// 如果是新的連接,需要把新的socket添加到efd中if (ep[i].data.fd == listenfd ){connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);tep.events = EPOLLIN;tep.data.fd = connfd;ret = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);}// 否則,讀取數據else{connfd = ep[i].data.fd;int bytes = read(connfd,buf,MAXLEN);// 客戶端關閉連接if (bytes == 0){ret =epoll_ctl(efd,EPOLL_CTL_DEL,connfd,NULL);close(connfd);printf("client[%d] closed\n", i);}else{for (int j = 0; j < bytes; ++j){buf[j] = toupper(buf[j]);}// 向客戶端發送數據write(connfd,buf,bytes);}}}}return 0;
}

redis源碼研究 里面 EV_SET的最后一個參數為什么是NULL 上面實例如果置為NULL會導致數據接收不到

static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct kevent ke;if (mask & AE_READABLE) {EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;}if (mask & AE_WRITABLE) {EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;}return 0;
}static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct kevent ke;if (mask & AE_READABLE) {EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);kevent(state->kqfd, &ke, 1, NULL, 0, NULL);}if (mask & AE_WRITABLE) {EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);kevent(state->kqfd, &ke, 1, NULL, 0, NULL);}
}

實際上就是個普通的賦值類似個構造函數 也就是我后面用到了udata 所以當然不賦值沒效果嘍

#define EV_SET(kevp, a, b, c, d, e, f) do {     \struct kevent *__kevp__ = (kevp);       \__kevp__->ident = (a);                  \__kevp__->filter = (b);                 \__kevp__->flags = (c);                  \__kevp__->fflags = (d);                 \__kevp__->data = (e);                   \__kevp__->udata = (f);                  \
} while(0)

根據伯克利大學的研究,kqueue的性能優于epoll,主要是因為epoll不支持在一個系統調用中進行多個興趣更新,而kqueue可以使用kevent()來實現這一點。在

還有一篇技術論文對二者的區別和性能進行了比較。在

http://www.eecs.berkeley.edu/~sangjin/2012/12/21/epoll-vs-kqueue.html
在這里插入圖片描述

實驗依據


在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447129.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447129.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447129.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

區塊鏈中的密碼學,使用ABE結合區塊鏈

ABE 密碼學&#xff0c;以及與區塊鏈結合的價值 背景 區塊鏈技術具備篡改難度高、使用成本低、分布式的優點&#xff0c;本應成為各行各業的重要助力。但是由于鏈上參與方擔心自己的核心數據外泄&#xff0c;不愿將自己的核心數據上鏈&#xff0c;這個原因成為阻止區塊鏈落地…

VMware虛擬機安裝Ubuntu系統教程

所使用的文件如下&#xff1a; VMware Workstation 17 Pro ubuntu-22.04.3-desktop-amd64.iso 一、ubuntu 命名規則及各版本一覽表 1.ubuntu 命名規則&#xff1a; 例如&#xff1a;ubuntu 16.04 LTS 是長期維護版本&#xff1b;ubuntu 17.04 是新特性版本 前兩位數字為發…

C++靜態類型成員變量的初始化順序(單例模式)

對編譯器來說&#xff0c;靜態成員變量的初始化順序和析構順序是一個未定義的行為 #include <string> #include <iostream> using namespace std; class Base{ public:static int b;static int a;}; int Base::b 2; int Base::a b 1;int main() {Base base;cou…

區塊鏈中密碼學與安全技術

區塊鏈的定義 區塊鏈的定義&#xff0c;應當是&#xff1a;區塊鏈是一種按照時間順序將數據進行分布式存儲的塊鏈式數據結構&#xff0c;它利用共識機制進行數據驗證&#xff0c;利用密碼學進行數據保護和用戶安全訪問&#xff0c;利用智能合約來操作數據&#xff0c;從而成為…

面經:服務器相關

阻塞IO 當你去讀一個阻塞的文件描述符時&#xff0c;如果在該文件描述符上沒有數據可讀&#xff0c;那么它會一直阻塞(通俗一點就是一直卡在調用函數那里)&#xff0c;直到有數據可讀。當你去寫一個阻塞的文件描述符時&#xff0c;如果在該文件描述符上沒有空間(通常是緩沖區)…

如何用區塊鏈保障數據安全和承載數據確權

區塊鏈可以確保數據安全&#xff0c;體現在那些方面呢&#xff1f; 主要是兩個維度&#xff0c;一是數據的不可篡改性&#xff1b;另外一個就是數據的隱私安全性。區塊鏈技術本身并不解決任何的安全問題&#xff0c;因此需要搭配安全技術一起使用&#xff0c;比如非對稱加密、…

面經:單例模式

侯捷單例 和劍指不同 &#xfffc; 餓漢式 餓漢式的特點是一開始就加載了&#xff0c;如果說懶漢式是“時間換空間”&#xff0c;那么餓漢式就是“空間換時間”&#xff0c;因為一開始就創建了實例&#xff0c;所以每次用到的之后直接返回就好了。餓漢式有兩種常見的寫法&…

屬性加密技術及基于屬性的ABE算法的訪問控制技術介紹

屬性加密技術 基于身份的加密體制簡介 基于身份的加密體制可以看作一種特殊的公鑰加密&#xff0c;它有如下特點:系統中用戶的公鑰可以由任意的字符串組成。這些字符串可以是用戶在現實中的身份信息&#xff0c;如:身份證號碼、用戶姓名、電話號碼、Email地址等&#xff0c;因…

面經:http協議

總結HTTPS傳輸過程 客戶端先從服務器獲取到證書&#xff0c;證書中包含公鑰 客戶端將證書進行校驗 客戶端生成一個對稱密鑰&#xff0c;用證書中的公鑰進行加密&#xff0c;發送給服務器 服務器得到這個請求后用私鑰進行解密&#xff0c;得到該密鑰 客戶端以后發出后續的請求&…

基于屬性加密的ABE算法的應用場景思考展望

ABE算法先前使用在云計算場景中&#xff0c;和區塊鏈存在交叉應用場景&#xff0c;具體問題體現在 數據的異地存儲、云服務器提供商的不可信、管理員能否對自身數據擁有足夠的控制能力以及如何保證數據的安全有效共享都是亟需解決的問題。 研究背景&#xff1a; 云計算越來越…

面經:設計模式

什么是接口隔離原則&#xff08;Interface Segregation Principle&#xff09; 定義&#xff1a;客戶端不應該依賴它不需要的接口&#xff1b;一個類對另一個類的依賴應該建立在最小的接口上。概括的說就是&#xff1a;建立單一接口&#xff0c;不要建立臃腫龐大的接口。&…

區塊鏈、密碼和銀行之間的衍生關系

銀行場景中密碼服務 設置密碼 用戶在注冊的時候&#xff0c;如果使用弱密碼&#xff0c;系統會檢測出來。我的猜測是將弱密碼的hash運算和用戶輸入的密碼hash比對&#xff0c;如果一致&#xff0c;禁止用戶注冊。 1、不要設置簡單密碼&#xff0c;您設置的密碼必須符合中信銀…

面經:多線程 線程池

使用線程池 當進程被初始化后&#xff0c;主線程就被創建了。對于絕大多數的應用程序來說&#xff0c;通常僅要求有一個主線程&#xff0c;但也可以在進程內創建多個順序執行流&#xff0c;這些順序執行流就是線程&#xff0c;每一個線程都是獨立的。 線程是進程的組成部分&am…

AIgorand區塊鏈中VRF隨機函數的應用

VRF&#xff08;Verifiable Random Function&#xff09; 可驗證隨機函數可以看作是一個隨機預言機&#xff0c;即可以通過任意的一個輸入&#xff0c;獲得一個隨機數輸出&#xff1a;輸出的結果&#xff08;Output&#xff09;是一個隨機數&#xff0c;其數值會均勻分布在值域…

AIgorand的相關學習參考鏈接

相關具體的開發者與SDK鏈接如下&#xff1a; GoSDKJavaScript SDK 網頁鏈接 測試網申請鏈接Github存儲庫鏈接開發者網址AIgorand官網Telegram電報群綜合白皮書MediumNaver Blog領英Linkedin區塊鏈瀏覽器INC公示錢包地址基金會公示錢包地址Telegram電報群官方 Github地址 相關…

操作系統 內核棧

視頻哈工大李治軍老師&#xff1a;https://www.bilibili.com/video/BV1d4411v7u7?p12 參考文檔&#xff1a;https://blog.csdn.net/SakuraA6/article/details/108810916 學長在我大一推薦我看&#xff0c;p12和p13的內容真的有那么難嗎&#xff0c;現在已經是我看的第三遍了還…

區塊鏈技術指南 序章理解感悟

序二 誤區一&#xff1a; 區塊鏈是一種顛覆性的新技術。區塊鏈不是一個新的技術&#xff0c;而是一個新的技術的組合。其關鍵的技術&#xff0c;包括P2P動態組網、基于密碼學的共享賬本、共識機制&#xff08;拜占庭將軍問題&#xff0c;分布式場景下的一致性問題&#xff09…

面經:紅黑樹 B樹 B+樹 哈希表

1.對于插入&#xff0c;刪除&#xff0c;查找 以及 輸出有序序列 這幾個操作&#xff0c;紅黑樹也可以完成&#xff0c;時間復雜度 與 用跳表實現是相同的。 但是&#xff0c;對于按照區間查找數據這個操作&#xff08;比如 [20,300]&#xff09;,紅黑樹的效率沒有跳表高&#…

回溯法和dfs的區別

值得注意&#xff0c;回溯法以深度優先搜索的方式搜索解空間&#xff0c;并且在搜索過程中用剪枝函數避免無效搜索。那為何 回溯算法 深度優先搜索 剪枝函數這一說法沒有錯&#xff1f; 因為樹是特殊的圖。簡單來說&#xff0c;樹是廣義的圖。再簡單來說&#xff0c;樹是圖。…

C++學習筆記 簡單部分

C 數據類型 使用變量來存儲各種信息&#xff0c;變量保留的是它所存儲的值的內存位置。這意味著&#xff0c;當創建一個變量時&#xff0c;就會在內存中保留一些空間。這段內存空間可以用于存儲各種數據類型&#xff08;比如字符型、寬字符型、整型、浮點型、雙浮點型、布爾型…