精华内容
下载资源
问答
  • 从上篇可以看到,由于每次poll都需要pollfd的数组地址,所以整个设计里面还包含与Client...EPoll就真的很刁了,不会随连接数的增多而有额外的性能损耗,每次分配给线程的是此次需要处理的event,使用起来也非常简单,不

    从上篇可以看到,由于每次poll都需要pollfd的数组地址,所以整个设计里面还包含与Client*的对应关系,移除的时候还需要去维护,让人感到十分的不便,就和IOEvent,select一样的感觉。每次还需要把整个集合分配给线程是遍历,虽说比select好,但仍有不足。EPoll就真的很刁了,不会随连接数的增多而有额外的性能损耗,每次分配给线程的是此次需要处理的event,使用起来也非常简单,不再需要自己去维护一个结构数组,其他基本可以和select poll保持一致的基本结构,主要就那几个函数,和ET LT两种模式需要介绍下。

    epoll_create(int size) 这个size现在看来没什么用了,据说在2.4.32 之前epoll是采用哈希表管理所有文件描述符,所以最开始是用来初始化哈希表的。但2.6.10后用的是红黑树管理,所以无论多少都无所谓了。epoll.h 对size的描述如下: The "size" parameter is a hint specifying the number of file descriptors to be associated with the new instance,可见英文翻译过来也是对要关注的描述符数目的暗示,可能是给程序员看的而不具有任何的约束力了。还有就动态创建销毁epoll_create的场景,不用了的话一定要close

    int epoll_ctl (int __epfd, int __op, int __fd,
         struct epoll_event *__event) __THROW; 这个没什么说的,就是注册事件、修改事件、删除事件

    /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */
    #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface.  */
    #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface.  */
    #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure.  */

    int epoll_wait (int __epfd, struct epoll_event *__events,
          int __maxevents, int __timeout);主要就想说下将timeout设为-1的时候 最开始返回-1 errno==4 并且提示interrupt system call ,我查了下,这是因为慢系统调用。适用于慢系统调用的基本规则是:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应处理函数返回时,该系统调用可能返回一个EINTR错误。所以,我们必须对慢系统调用返回的EINTR有所准备。在这个地方直接continue无视掉这个错误就行。


    Edge Triggered (ET)  有信号的时候仅返回一次,需要循环处理recv accept send,必须为非阻塞。ET模式处理起来真的非常麻烦,recv需要判断是否是断开还是接收完成或者接收中途断开,send需要判断缓冲区是否已被写满等等。当然,其实也更加灵活,比如发送缓冲区被写满了,正常来说可能是网络问题,直接将这个socket剔除或者移到阻塞处理策略上,从而不影响其他正常网络连接的用户
    Level Triggered (LT)  只要内核缓冲区有东西,那每次都会返回,同时可以采用阻塞的办法就避免上述的问题


    其实我这代码还有很大的优化空间,可以参照IOCP进一步设计,我这为了简便仍然是同步IO,若想榨干CPU,那就一定得用异步IO进行处理


    #include <iostream>
    #include<list>
    #include<map>
    #include<vector>
    #include<deque>
    #include<mutex>
    #include <atomic>
    #include<condition_variable>
    #include<sys/socket.h>
    #include<sys/epoll.h>
    #include<netinet/in.h>
    #include <pthread.h>
    #include<unistd.h>
    #include<memory.h>
    #include<fcntl.h>
    using namespace std;
    struct Client
    {
    	int id;
    };
    
    void* CreateServ(void* args);
    void* Proc(void* args);
    using namespace std;
    const int _thread_count = 8;
    int _epfd;
    int _events_num = -1;
    int sockSrv;
    int id=0;
    epoll_event _events[1024]={0};
    epoll_event ev[_thread_count];
    mutex m;
    deque<epoll_event*> _deque;
    
    map<int,Client*> _sockMap;
    list<int> _removeList;
    mutex lock4cv;
    mutex lock4cv2;
    condition_variable cv;
    condition_variable cv2;
    int _thread_unfinish;
    vector<int> _vec;
    char* buf2 = "hello client";
    int main()
    {
    	pthread_t tid;
    	for (int i = 0; i < _thread_count; i++)
    	{
    		_vec.push_back(0);
    	}
    	pthread_create(&tid, 0, CreateServ, 0);
    	for (int i = 0; i < _thread_count; i++)
    	{
    		int* temp = new int(i);
    		pthread_create(&tid, 0, Proc, temp);
    	}
    	cin.get();
    	cin.get();
    	return 0;
    }
    bool _isFinish()
    {
    	return _thread_unfinish <= 0;
    }
    
    
    void* Proc(void* args)
    {
    	int* I = (int*)args;
    
    	while (true)
    	{
    		{
    			unique_lock<mutex> l(lock4cv);
    			if (_vec[*I] <= 0)
    			{
    				cv.wait(l);
    			}
    			_vec.at(*I) = 0;
    		}
    		while(true)
    		{
    			m.lock();
    			if(_deque.size()==0)
    			{
    				m.unlock();
    				break;
    			}
    			epoll_event* e = _deque.front();
    			_deque.pop_front();
    			m.unlock();
    
    			if (e->data.fd == -1)
    			{
    				continue;
    			}
    			if (e->data.fd == sockSrv)
    			{
    
    			}
    			else if (e->events == EPOLLIN)
    			{
    				int fd = e->data.fd;
    				cout << "proc by:" << *I << endl;
    				char buf[128];
    				int len = 0;
    				bool needRemove = false;
    				do
    				{
    					int len2 = recv(fd, buf + len, 128, 0);
    					if (len2 <= 0)
    					{
    						if (len2 == 0 || errno == 11)
    						{
    							needRemove = true;
    							break;
    						}
    					}
    					len += len2;
    				} while (len < 128);
    				if (needRemove)
    				{
    					e->data.fd = -1;
    					close(fd);
    					_removeList.push_back(fd);
    					continue;
    				}
    				send(fd, buf2, 128, 0);
    				//cout << buf << endl;
    				e->events = 0;
    			}
    
    		}
    		//此处一定要加锁。当wait还未准备好,但_isFinish刚刚检测完时_thread_unfinish -= 1;cv2.notify_all();这两句恰巧在之后执行,那就GG了,两边进入互相等待的状态
    		lock4cv2.lock();
    		_thread_unfinish -= 1;
    		cv2.notify_all();
    		lock4cv2.unlock();
    	}
    
    }
    void release(int fd)
    {
    	//cout << "release:"<<fd << endl;
    	ev[0].data.fd = fd;
    	epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &ev[0]);
    	for(auto iter=_sockMap.begin();iter!=_sockMap.end();++iter)
    	{
    		if(iter->first==fd)
    		{
    			delete iter->second;
    			_sockMap.erase(iter);
    			break;
    		}
    	}
    
    }
    
    void* CreateServ(void* args) {
    	sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    
    	int nSendBufLen = 16 * 1024 * 1024;
    	setsockopt(sockSrv, SOL_SOCKET, SO_SNDBUF, (const char*)&nSendBufLen, sizeof(int));
    
    	fcntl(sockSrv, F_SETFL, O_NONBLOCK);
    	//	struct in_addr s;
    	//	inet_pton(AF_INET, "127.0.0.1",(void*)&s);
    	sockaddr_in addrSrv;
    	addrSrv.sin_addr.s_addr = htonl(INADDR_ANY);
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_port = htons(6001);
    
    	::bind(sockSrv, (sockaddr*)&addrSrv, sizeof(sockaddr));
    
    	int err = listen(sockSrv, 100);
    	if (err == -1) {
    		cout << "listen failed" << endl;
    		return 0;
    	}
    
    	_epfd=epoll_create(1024);
    	ev[0].data.fd = sockSrv;
    	ev[0].events = EPOLLIN|EPOLLET;
    	epoll_ctl(_epfd, EPOLL_CTL_ADD, sockSrv, &ev[0]);
    	//accept loop
    	while (true) {
    
    		_events_num =epoll_wait(_epfd, _events, 1024, -1);
    
    		if(_events_num<0)
    		{
    			continue;
    		}
    		while (true) {
    			int s = accept(sockSrv, 0, 0);
    			if(s<=0)
    			{
    				break;
    			}
    			fcntl(s, F_SETFL, O_NONBLOCK);
    			Client* c = new Client;
    			c->id = id;
    			id += 1;
    			_sockMap.insert(pair<int, Client*>(s, c));
    			ev[0].data.fd = s;
    			ev[0].events = EPOLLIN | EPOLLET;
    			epoll_ctl(_epfd, EPOLL_CTL_ADD, s, &ev[0]);
    		}
    
    		_thread_unfinish = _thread_count;
    
    		for(int i=0;i<_events_num;i++)
    		{
    			_deque.push_back(&_events[i]);
    		}
    
    		for(int i=0;i<_thread_count;i++)
    		{
    			_vec.at(i) = 1;
    		}
    		cv.notify_all();
    
    		unique_lock<mutex> l(lock4cv2);
    		cv2.wait(l, _isFinish);
    
    		for (auto iter = _removeList.begin(); iter != _removeList.end(); ++iter)
    		{
    			release(*iter);
    			id-=1;
    		}
    		_removeList.clear();
    
    	}
    }



    客户端 与C++ SOCKET通信模型(一)相同




    展开全文
  • epoll印象中是同步的io, 那么nginx或一些其他软件是如何使用同步EPOLL实现异步非阻塞的?
  • epoll

    2020-02-18 15:42:07
    但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用...

    select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

    select的几大缺点:

    (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

    (2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

    (3)select支持的文件描述符数量太小了,默认是1024

    epoll既然是对select和poll的改进,就应该能避免上述的三个缺点。那epoll都是怎么解决的呢?在此之前,我们先看一下epoll和select和poll的调用接口上的不同,select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。

    对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。

    对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。

    对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

    总结:

    (1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

    (2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

    展开全文
  • 多线程操作同一个epoll fd同步

    千次阅读 2015-08-23 19:49:50
    自己以前做一个接口服务器时候,这种场景下我的设计是多个线程操作同一个epoll fd。彼时,我的理由是epoll的系列函数是线程安全的。  当然有人不理解为什么会有多个线程操作同一个epoll fd的情形,这里稍微铺陈...

    自己以前做一个接口服务器时候,这种场景下我的设计是多个线程操作同一个epoll fd。彼时,我的理由是epoll的系列函数是线程安全的。

           当然有人不理解为什么会有多个线程操作同一个epoll fd的情形,这里稍微铺陈一下接口服务器的场景。epoll fd有线程1维护,监听服务端端口的socket的accept出来的acceptor(即新的socket fd)也放在这个epoll fd中。当收到客户端链接请求时候,线程2从连接池connector pool中挑选出来一个connector,connector的作用是转发请求,此时connector会把acceptor缓存起来。如果connector收到回复后,connector会通过acceptor向客户端返回一些数据后,线程2此时需要把acceptor在add进epoll fd中。

          以前我以为epoll fd是多线程安全的,我就直接通过epoll_ctl(epoll fd,acceptor,add)把acceptor放进epoll fd中。

          现在再回首看看,自己是想当然的这样操作了,没有任何依据。孟子曰,“行有不得,反求诸己”。既然自己无法解开困惑,那就求助伟大的man了。通过“man epoll_wait”后,得到这么一句话:

    1. NOTES  
    2.      While one thread is blocked in a call to epoll_pwait(), it is possible for another thread to add a file descriptor to the waited-upon epoll instance.  If the new file descriptor becomes ready, it will cause the epoll_wait() call to unblock.  
    3.      For a discussion of what may happen if a file descriptor in an epoll instance being monitored by epoll_wait() is closed in another thread, see select(2).  

           翻译后就是:如果一个线程正阻塞在epoll_pwait上,此时可能有另外一个线程要把一个socket fd添加到这个epoll fd上,如果这个这个新的socket fd被添加进去后处于ready状态,那么epoll_wait就不会再处于阻塞状态。如果由epoll fd监控的一个socket fd被另外一个线程close掉,此时系统处于何种状态请参考select(2)。通过"man 2 select"后,得到如下一段话:   

    1. Multithreaded applications  
    2.     If a file descriptor being monitored by select() is closed in another thread, the result is unspecified.  On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready (a subsequent I/O operation will likely fail with an error, unless another the file descriptor reopened between the time select() returned  and the I/O operations was performed).  On Linux (and some other systems), closing the file descriptor in another thread has no effect on select().  In summary, any application that relies on a particular behavior in this scenario must be considered buggy.  

           翻译后,其意义为:如果一个线程中由select管理的socket被另外一个线程close掉,将会发生什么只有天晓得。在一些UNIX系统中,select会结束阻塞态并返回,它会标识这个socket处于ready状态(后面对这个socket的操作会失败,os也会给出错误提示,除非在select返回和进程对这个socket进行读写这段时间段内,os又把同一个socket fd分配出去了)。在linux(和其他同类的系统)上,这种行为不会影响select(即有阻塞态变为非阻塞态)。总之,如果一个程序中这种行为应该被认为是一个bug(就不应有这种行为操作)。

           通过以上两段man大神的神示,除了一个线程在epoll或者select中监控一个socket时候另外一个线程对这个socket进行close这种情况,我就可以认为多个线程操作同一个epoll fd的行为是安全的,即我上面的操作是没有问题的。

    展开全文
  • 这篇文章的结论就是epoll属于同步非阻塞模型 这是一次概念的纠结过程,对写代码没有太大意义。 过程是这样的: 首先,我的概念里往往只有同步和异步,没有太多去区别同异步IO和同异步通知两种。 另外还...
    这篇文章的结论就是epoll属于同步非阻塞模型,这个东西貌似目前还是有争议,在新的2.6内核之后,epoll应该属于异步io的范围了,golang的高并发特性就是底层封装了epoll模型的函数,但也有文章指出epoll属于“伪AIO”,真正的推动力实际在系统内核,另外mmap的应用加快了用户层和内核层的消息交换,对并发效率也有极大的提升。
    还有一点,在DMA控制器的帮助下,实际上算是异步了,所以epoll可以说就是异步非阻塞。

    《UNIX网络编程:卷一》第六章——I/O复用。书中向我们提及了5种类UNIX下可用的I/O模型:

    • 阻塞式I/O;

    • 非阻塞式I/O;

    • I/O复用(select,poll,epoll...);

    • 信号驱动式I/O(SIGIO);

    • 异步I/O(POSIX的aio_系列函数);

    阻塞式I/O模型:默认情况下,所有套接字都是阻塞的。怎么理解?先理解这么个流程,一个输入操作通常包括两个不同阶段:

    (1)等待数据准备好;
    (2)从内核向进程复制数据。


    对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用程序缓冲区。 好,下面我们以阻塞套接字的recvfrom的的调用图来说明阻塞

    &amp;lt;img src=&quot;https://pic2.zhimg.com/50/e83d68da03da2e8c1568b4b4b630edfd_hd.jpg&quot; data-rawwidth=&quot;1058&quot; data-rawheight=&quot;556&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;1058&quot; data-original=&quot;https://pic2.zhimg.com/e83d68da03da2e8c1568b4b4b630edfd_r.jpg&quot;&amp;gt;

    标红的这部分过程就是阻塞,直到阻塞结束recvfrom才能返回。

    非阻塞式I/O: 以下这句话很重要:进程把一个套接字设置成非阻塞是在通知内核,当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠,而是返回一个错误。看看非阻塞的套接字的recvfrom操作如何进行

    &amp;lt;img src=&quot;https://pic1.zhimg.com/50/4bc31cab27a9a732ab7d1ba9e674ed64_hd.jpg&quot; data-rawwidth=&quot;1064&quot; data-rawheight=&quot;631&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;1064&quot; data-original=&quot;https://pic1.zhimg.com/4bc31cab27a9a732ab7d1ba9e674ed64_r.jpg&quot;&amp;gt;

    可以看出recvfrom总是立即返回。

    I/O多路复用:虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。如图

    &amp;lt;img src=&quot;https://pic1.zhimg.com/50/b1ec6a4f16844a27c175d5a6a94cd7f8_hd.jpg&quot; data-rawwidth=&quot;1136&quot; data-rawheight=&quot;732&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;1136&quot; data-original=&quot;https://pic1.zhimg.com/b1ec6a4f16844a27c175d5a6a94cd7f8_r.jpg&quot;&amp;gt;

    信号驱动式I/O:用的很少,就不做讲解了。直接上图

    &amp;lt;img src=&quot;https://pic1.zhimg.com/50/6294fb7f7f5c22e39187a490c35ac6f0_hd.jpg&quot; data-rawwidth=&quot;1139&quot; data-rawheight=&quot;711&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;1139&quot; data-original=&quot;https://pic1.zhimg.com/6294fb7f7f5c22e39187a490c35ac6f0_r.jpg&quot;&amp;gt;

    异步I/O:这类函数的工作机制是告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到用户空间)完成后通知我们。如图:

    &amp;lt;img src=&quot;https://pic2.zhimg.com/50/5819fd0fdff2bd4fdc9652291aca1831_hd.jpg&quot; data-rawwidth=&quot;1109&quot; data-rawheight=&quot;603&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;1109&quot; data-original=&quot;https://pic2.zhimg.com/5819fd0fdff2bd4fdc9652291aca1831_r.jpg&quot;&amp;gt;

    注意红线标记处说明在调用时就可以立马返回,等函数操作完成会通知我们。

    等等,大家一定要问了,同步这个概念你怎么没涉及啊?别急,您先看总结。 其实前四种I/O模型都是同步I/O操作,他们的区别在于第一阶段,而他们的第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。相反,异步I/O模型在这两个阶段都要处理。

    &amp;lt;img src=&quot;https://pic4.zhimg.com/50/8244d924a12eaf42d61b41718c559bff_hd.jpg&quot; data-rawwidth=&quot;3200&quot; data-rawheight=&quot;1800&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;3200&quot; data-original=&quot;https://pic4.zhimg.com/8244d924a12eaf42d61b41718c559bff_r.jpg&quot;&amp;gt;

    再看POSIX对这两个术语的定义:

    • 同步I/O操作:导致请求进程阻塞,直到I/O操作完成;

    • 异步I/O操作:不导致请求进程阻塞。

    好,下面我用我的语言来总结一下阻塞,非阻塞,同步,异步

    • 阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待;

    • 同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。




    这是一次概念的纠结过程,对写代码没有太大意义。

    过程是这样的:
    首先,我的概念里往往只有同步和异步,没有太多去区别同异步IO和同异步通知两种。
    另外还记得apu(2rd)中有一句“select和poll可以实现异步形式的通知”。

    接着,听到了epoll是同步IO这个概念,比较意外。坚持了一下后,查到如下概念:
    在unp(3rd)里的定义是:
    第一是IO操作的概念:
    IO操作包括:
    1.等待数据准备好。
    2.从内核到进程拷贝数据。

    第二就是是同步IO和异步IO的区别:
    同步IO导致请求进程阻塞,直到IO操作完成。
    异步IO不导致请求进程阻塞。

    得到的结论:
    阻塞IO模型,非阻塞IO模型,IO复用模型,信号驱动IO模型都是同步IO。
    epoll也是IO复用模型,应该是同步IO。

    此时又意外了,再看到一个解释:
    更为重要的是, epoll 因为采用 mmap的机制, 使得 内核socket buffer和 用户空间的 buffer共享, 从而省去了 socket data copy, 这也意味着, 当epoll 回调上层的 callback函数来处理 socket 数据时, 数据已经从内核层 "自动" 到了用户空间, 虽然和 用poll 一样, 用户层的代码还必须要调用 read/write, 但这个函数内部实现所触发的深度不同了.

    用 poll 时, poll通知用户空间的Appliation时, 数据还在内核空间, 所以Appliation调用 read API 时, 内部会做 copy socket data from kenel space to user space.

    而用 epoll 时, epoll 通知用户空间的Appliation时?, 数据已经在用户空间, 所以 Appliation调用 read API 时?, 只是读取用户空间的 buffer, 没有 kernal space和 user space的switch了.

    于是想了一下:
    明显没有IO操作的拷贝数据到内核空间了,stevens应该在99年就挂了,2.6内核的epoll才采用mmap机制,书籍偏旧了吧。
    那么epoll是异步IO了吧。

    然后再一看,你妹的,这还是不符合异步IO啊,毕竟epoll在告知OK前,是阻塞了,虽然是拷贝数据结束了。
    看来好像应该修正的是IO操作定义的第二步才对,而不是那个区别。

    好吧,你就暂时属于同步IO了,专心看代码,不纠结概念了。

    展开全文
  • 但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的(可能通过while循环来检测内核将数据准备的怎么样了, 而不是属于内核的一种通知
  • 1 Epoll vs. IOCP Epoll 和 IOCP 都是为高性能网络服务器而设计的高效 I/O 模型;都是基于事件驱动的。事件驱动有个著名的好莱坞原则(“不要打电话给我们,我们会打电话给你”)。   不同之处在于: 1. ...
  • 这篇文章的结论就是epoll属于同步非阻塞模型,这个东西貌似目前还是有争议,在新的2.6内核之后,epoll应该属于异步io的范围了,golang的高并发特性就是底层封装了epoll模型的函数,但也有文章指出epoll属于“伪AIO”...
  • 针对IO,总是涉及到阻塞、非阻塞、异步、同步以及select/poll和epoll的一些描述,那么这些东西到底是什么,有什么差异? 一般来讲一个IO分为两个阶段: 等待数据到达把数据从内核空间拷贝到用户空间 现在...
  • 总结记录一波 同步、异步、阻塞、非阻塞、异步I/O、select、poll、epoll等等,纯粹纯粹纯粹为了应付面试哈。1 同步与异步首先来解释同步和异步的概念,这两个概念与消息的通知机制有关。也就是同步与异步主要是从...
  • 1、五种I/O模型 任何I/O过程都包含了等待和拷贝两个步骤;...如果没有把数据一次性读写完(如读写缓冲区太小),下次调用epoll_wait(),不会通知上次没读写完,直到该文件描述符状态发生改变才会通知。
  • IO多路复用之select、poll、epoll综述 目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O...但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写...
  • epoll貌似是同步的吧! 那nginx所谓的异步非阻塞到底异步在哪里? 为什么使用了epoll却叫异步? epoll确实是阻塞的. nginx的异步并不是指epoll的wait是异步的,而是指对于每个socketfd来说,它的handle是异步的. ...
  • 这将是我关于Linux Epoll服务器开发系列文章中的第一篇,关于线程同步的操作,大部分是原创,少部分是借鉴。 命名风格近似于Google C++ Coding Style,但根据自己的习惯做了调整。 这些代码将用于构建一个高性能的...
  • socket阻塞与非阻塞,同步与异步 作者:huangguisu 1. 概念理解  在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步:  所谓...
  • EPOLL模型

    2016-08-31 22:39:39
    什么是epollepoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in...同步I/O:导致请求进程阻塞,直到I
  • Linux中的多路复用技术---epoll的详解 epoll完整例子
  • epoll详解

    2018-05-17 23:08:43
    首先要知道,select,poll,...但select,poll,epoll本质上都是同步非阻塞I/O,因为他们都需要在读写事件就绪后自己负责进行读写,而异步I/O则无需自己负责进行读写,内核来完成,然后会收到内核的信号,进程就会知...
  • 无论epoll处于LT模式还是ET模式下,一个socket上的某个事件都可能会被触发多次。这在并发编程中会引起一个问题,比如一个线程在读取完某个socket上的数据后开始处理数据,而在处理数据的过程中又触发可读事件,这样...
  • epoll学习

    2017-05-24 12:16:06
    epoll是什么? 一个提供多路复用的系统调用学习要点 参考:http://blog.csdn.net/yz764127031/article/details/72459158 (1)理解关键字:同步异步,非阻塞阻塞(2)设置socket为非阻塞,然后利用多路复用检测...
  • 对IO,总是涉及到阻塞、非阻塞、异步、同步以及select/poll和epoll的一些描述,那么这些东西到底是什么,有什么差异? 一般来讲一个IO分为两个阶段: 等待数据到达把数据从内核空间拷贝到用户空间 现在...
  • //call cb int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL); //printf("epoll_ctl DEL --> sockfd:%d\n", sockfd); close(sockfd); ///// free(data); } } } struct async_context *...
  • 阻塞、非阻塞、异步、同步以及select/poll和epoll标签: 异步 epoll针对IO,总是涉及到阻塞、非阻塞、异步、同步以及select/poll和epoll的一些描述,那么这些东西到底是什么,有什么差异?一般来讲一个IO分为两个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,682
精华内容 672
关键字:

同步epoll