socket网络编程--epoll小结

http://www.cnblogs.com/wunaozai/p/3895860.html

 以前使用的用于I/O多路复用为了方便就使用select函数,但select这个函数是有缺陷的。因为它所支持的并发连接数是有限的(一般小于1024),因为用户处理的数组是使用硬编码的。这个最大值为FD_SETSIZE,这是在<sys/select.h>中的一个常量,它说明了最大的描述符数。但是对于大多数应用程序而言,这个数是够用的,而且有可能还是太大的,多数应用程序只使用3~10个描述符。而如今的网络服务器小小的都有几万的连接,虽然可以使用多线程多进程(也就有N*1024个)。但是这样处理起来既不方面,性能又低。

  同时期有I/O多路复用的还有一个poll函数,这个函数类似于select,但是其应用程序接口有所不用。原型如下

  #include <poll.h>
  int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);

  //返回值: 准备就绪的描述符数,若超时则返回0,出错返回-1

  如果只是考虑性能的话,poll()也是不合适的,尽管它可以支持较高的TCP并发连接数,但是由于其采用“轮询”机制(遍历数组而已),但并发数较高时,其运行效率相当低(如果有10k个连接,单用于轮询的时间就需要1~10ms了),同时还可能存在I/O事件分配不均,导致部分TCP连接上的I/O出现“饥饿”现象。基于种种原因在Linux 2.5.44版本后poll被epoll取代。
  支持一个进程打开最大数目的 socket 描述符(FD)。select 最不能忍受的是一个进程所打开的FD 是有一定限制的,由 FD_SETSIZE 设置,默认值是 2048。对于那些需要支持的上万连接数目的 IM 服务器来说显然太少了。这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的 Apache 方案Process Per Connection,TPC方案 Thread Per Connection),不过虽然 linux 上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。不过 epoll 则没有这个限制,它所支持的 FD 上限是最大可以打开文件的数目,这个数字一般远大于 2048,举个例子,在 1GB 内存的机器上大约是 10 万左右,具体数目可以 cat /proc/sys/fs/file-max 察看,一般来说这个数目和系统内存关系很大。
由于epoll这个函数是后增加上的,造成现在很少有资料提及到,我看了APUE,UNPv1等书都没有找到相关的函数原型。所以我只能从网络上抄一些函数原型过来了。

   epoll用到的所有函数都是在头文件sys/epoll.h中声明,有什么地方不明白或函数忘记了可以去看一下或者man epoll。epoll和select相比,最大不同在于:

  epoll返回时已经明确的知道哪个sokcet fd发生了事件,不用再一个个比对(轮询)。这样就提高了效率。select的FD_SETSIZE是有限制的,而epoll是没有限制的只与系统资源有关。

  epoll_create函数

1 /* Creates an epoll instance.  Returns an fd for the new instance.
2    The "size" parameter is a hint specifying the number of file
3    descriptors to be associated with the new instance.  The fd
4    returned by epoll_create() should be closed with close().  */
5 extern int epoll_create (int __size) __THROW;

  该函数生成一个epoll专用的文件描述符。它其实是在内核申请空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。这个数没有select的1024约束。

  epoll_ctl函数

复制代码
 1 /* Manipulate an epoll instance "epfd". Returns 0 in case of success,
 2    -1 in case of error ( the "errno" variable will contain the
 3    specific error code ) The "op" parameter is one of the EPOLL_CTL_*
 4    constants defined above. The "fd" parameter is the target of the
 5    operation. The "event" parameter describes which events the caller
 6    is interested in and any associated user data.  */
 7 extern int epoll_ctl (int __epfd, int __op, int __fd,
 8                       struct epoll_event *__event) __THROW;
 9 /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). */
10 #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */
11 #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface. */
12 #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure. */
复制代码

  该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。 

  参数: epfd:由 epoll_create 生成的epoll专用的文件描述符; 
      op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除

      fd:关联的文件描述符; 
      event:指向epoll_event的指针; 
      返回值:如果调用成功返回0,不成功返回-1

  用到的数据结构

复制代码
 1 typedef union epoll_data
 2 {
 3   void *ptr;
 4   int fd;
 5   uint32_t u32;
 6   uint64_t u64;
 7 } epoll_data_t;
 8 
 9 struct epoll_event
10 {
11   uint32_t events;      /* Epoll events */
12   epoll_data_t data;    /* User data variable */
13 };
复制代码

  设置实例

复制代码
 1 struct epoll_event ev;
 2 //设置与要处理的事件相关的文件描述符
 3 ev.data.fd=listenfd;
 4 //设置要处理的事件类型
 5 ev.events=EPOLLIN|EPOLLET;
 6 //注册epoll事件
 7 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
 8 //常用的事件类型:
 9 EPOLLIN :表示对应的文件描述符可以读;
10 EPOLLOUT:表示对应的文件描述符可以写;
11 EPOLLPRI:表示对应的文件描述符有紧急的数据可读
12 EPOLLERR:表示对应的文件描述符发生错误;
13 EPOLLHUP:表示对应的文件描述符被挂断;
14 EPOLLET:表示对应的文件描述符有事件发生;
15 //具体可以看<sys/epoll.h>
复制代码

  epoll_wait函数

复制代码
 1 /* Wait for events on an epoll instance "epfd". Returns the number of
 2    triggered events returned in "events" buffer. Or -1 in case of
 3    error with the "errno" variable set to the specific error code. The
 4    "events" parameter is a buffer that will contain triggered
 5    events. The "maxevents" is the maximum number of events to be
 6    returned ( usually size of "events" ). The "timeout" parameter
 7    specifies the maximum wait time in milliseconds (-1 == infinite).
 8    This function is a cancellation point and therefore not marked with
 9    __THROW.  */
10 extern int epoll_wait (int __epfd, struct epoll_event *__events,
11                        int __maxevents, int __timeout);
12 
13 
14 /* Same as epoll_wait, but the thread's signal mask is temporarily
15    and atomically replaced with the one provided as parameter.
16    This function is a cancellation point and therefore not marked with
17    __THROW.  */
18 extern int epoll_pwait (int __epfd, struct epoll_event *__events,
19                         int __maxevents, int __timeout,
20                         __const __sigset_t *__ss);
复制代码

  该函数用于轮询I/O事件的发生;
  参数: epfd:由epoll_create 生成的epoll专用的文件描述符;
      epoll_event:用于回传代处理事件的数组;
      maxevents:每次能处理的事件数;
      timeout:等待I/O事件发生的超时值(单位应该是ms);-1相当于阻塞,0相当于非阻塞。一般用-1即可

      返回值:返回发生事件数。如出错则返回-1。

   下面给一个man手册里面的例子

复制代码
 1 #define MAX_EVENTS 10
 2 struct epoll_event ev, events[MAX_EVENTS];
 3 int listen_sock, conn_sock, nfds, epollfd;
 4 
 5 /* Set up listening socket, 'listen_sock' (socket(),
 6    bind(), listen()) */
 7 
 8 epollfd = epoll_create(10);
 9 if (epollfd == -1) {
10     perror("epoll_create");
11     exit(EXIT_FAILURE);
12 }
13 
14 ev.events = EPOLLIN;
15 ev.data.fd = listen_sock;
16 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
17     perror("epoll_ctl: listen_sock");
18     exit(EXIT_FAILURE);
19 }
20 
21 for (;;) {
22     nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
23     if (nfds == -1) {
24         perror("epoll_pwait");
25         exit(EXIT_FAILURE);
26     }
27 
28     for (n = 0; n < nfds; ++n) {
29         if (events[n].data.fd == listen_sock) {
30             conn_sock = accept(listen_sock,
31                     (struct sockaddr *) &local, &addrlen);
32             if (conn_sock == -1) {
33                 perror("accept");
34                 exit(EXIT_FAILURE);
35             }
36             setnonblocking(conn_sock);
37             ev.events = EPOLLIN | EPOLLET;
38             ev.data.fd = conn_sock;
39             if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
40                         &ev) == -1) {
41                 perror("epoll_ctl: conn_sock");
42                 exit(EXIT_FAILURE);
43             }
44         } else {
45             do_use_fd(events[n].data.fd);
46         }
47     }
48 }
复制代码

  下面这个是引用mmz_xiaokong

epoll函数
  常用模型的缺点
    如果不摆出来其他模型的缺点,怎么能对比出 Epoll 的优点呢。
  PPC/TPC 模型
  这两种模型思想类似,就是让每一个到来的连接一边自己做事去,别再来烦我 。只是 PPC 是为它开了一个进程,而 TPC 开了一个线程。可是别烦我是有代价的,它要时间和空间啊,连接多了之后,那么多的进程 / 线程切换,这开销就上来了;因此这类模型能接受的最大连接数都不会高,一般在几百个左右。
  select 模型
  1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Select 模型的最大并发数就被相应限制了。自己改改这个 FD_SETSIZE ?想法虽好,可是先看看下面吧 …
  2. 效率问题, select 每次调用都会线性扫描全部的 FD 集合,这样效率就会呈现线性下降,把 FD_SETSIZE 改大的后果就是,大家都慢慢来,什么?都超时了??!!
  3. 内核 / 用户空间 内存拷贝问题,如何让内核把 FD 消息通知给用户空间呢?在这个问题上 select 采取了内存拷贝方法。
  poll 模型
基本上效率和 select 是相同的, select 缺点的 2 和 3 它都没有改掉。
  Epoll 的提升
  把其他模型逐个批判了一下,再来看看 Epoll 的改进之处吧,其实把 select 的缺点反过来那就是 Epoll 的优点了。
  1. Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, 一般来说这个数目和系统内存关系很大 ,具体数目可以 cat /proc/sys/fs/file-max 察看。
  2. 效率提升, Epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。
  3. 内存拷贝, Epoll 在这点上使用了“共享内存 ”,这个内存拷贝也省略了。
  Epoll 为什么高效
  Epoll 的高效和其数据结构的设计是密不可分的(以空间换时间),这个下面就会提到。
  首先回忆一下 select 模型,当有 I/O 事件到来时, select 通知应用程序有事件到了快去处理,而应用程序必须轮询所有的 FD 集合,测试每个 FD 是否有事件发生,并处理事件;代码像下面这样:

复制代码
 1 int res = select(maxfd+1, &readfds, NULL, NULL, 120);
 2 if (res > 0)
 3 {
 4     for (int i = 0; i < MAX_CONNECTION; i++)
 5     {
 6         if (FD_ISSET(allConnection[i], &readfds))
 7         {
 8             handleEvent(allConnection[i]);
 9         }
10     }
11 }
12 // if(res == 0) handle timeout, res < 0 handle error    
复制代码

  Epoll 不仅会告诉应用程序有I/0 事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些信息应用程序就能直接定位到事件,而不必遍历整个FD 集合。

1 int res = epoll_wait(epfd, events, 20, 120);
2 for (int i = 0; i < res;i++)
3 {
4      handleEvent(events[n]);
5 }

   下面用一个实例来说明

  client.cpp

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <netinet/in.h>
 5 #include <sys/types.h>
 6 #include <sys/socket.h>
 7 #include <netdb.h>
 8 #include <unistd.h>
 9 
10 #define MAX_DATA_SIZE 4096
11 #define SERVER_PORT 12138
12 
13 
14 int main(int argc,char *argv[])
15 {
16     int sockfd;
17     struct hostent * host;
18     struct sockaddr_in servAddr;
19     int pid;
20     char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE];
21     int sendSize,recvSize;
22 
23     host=gethostbyname(argv[1]);
24     if(host==NULL)
25     {
26         perror("get host error");
27         exit(-1);
28     }
29 
30     sockfd=socket(AF_INET,SOCK_STREAM,0);
31     if(sockfd==-1)
32     {
33         perror("创建socket失败");
34         exit(-1);
35     }
36 
37     servAddr.sin_family=AF_INET;
38     servAddr.sin_port=htons(SERVER_PORT);
39     servAddr.sin_addr=*((struct in_addr *)host->h_addr);
40     bzero(&(servAddr.sin_zero),8);
41 
42     if(connect(sockfd,(struct sockaddr *)&servAddr,sizeof(struct sockaddr_in))==-1)
43     {
44         perror("connect 失败");
45         exit(-1);
46     }
47 
48     if((pid=fork())<0)
49     {
50         perror("fork error");
51     }
52     else if(pid>0)
53     {
54         while(1)
55         {
56             fgets(sendBuf,MAX_DATA_SIZE,stdin);
57             sendSize=send(sockfd,sendBuf,MAX_DATA_SIZE,0);
58             if(sendSize<0)
59                 perror("send error");
60             memset(sendBuf,0,sizeof(sendBuf));
61         }
62     }
63     else
64     {
65         while(1)
66         {
67             recvSize=recv(sockfd,recvBuf,MAX_DATA_SIZE,0);
68             if(recvSize<0)
69                 perror("recv error");
70             printf("接收到的信息:%s",recvBuf);
71             memset(recvBuf,0,sizeof(recvBuf));
72         }
73     }
74     return 0;
75 }
复制代码

  server.cpp

复制代码
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <sys/socket.h>
  4 #include <sys/epoll.h>
  5 #include <string.h>
  6 #include <netinet/in.h>
  7 #include <string.h>
  8 #include <netdb.h>
  9 #include <arpa/inet.h>
 10 #include <unistd.h>
 11 
 12 #define SERVER_PORT 12138
 13 #define CON_QUEUE 20
 14 #define MAX_DATA_SIZE 4096
 15 #define MAX_EVENTS 500
 16 
 17 void AcceptConn(int sockfd,int epollfd);
 18 void Handle(int clientfd);
 19 
 20 int main(int argc,char *argv[])
 21 {
 22     struct sockaddr_in serverSockaddr;
 23     int sockfd;
 24 
 25     //创建socket
 26     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
 27     {
 28         perror("创建socket失败");
 29         exit(-1);
 30     }
 31     serverSockaddr.sin_family=AF_INET;
 32     serverSockaddr.sin_port=htons(SERVER_PORT);
 33     serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
 34     bzero(&(serverSockaddr.sin_zero),8);
 35 
 36     int on=0;
 37     setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
 38 
 39     if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1)
 40     {
 41         perror("绑定失败");
 42         exit(-1);
 43     }
 44 
 45     if(listen(sockfd,CON_QUEUE)==-1)
 46     {
 47         perror("监听失败");
 48         exit(-1);
 49     }
 50 
 51     //epoll初始化
 52     int epollfd;//epoll描述符
 53     struct epoll_event eventList[MAX_EVENTS];
 54     epollfd=epoll_create(MAX_EVENTS);
 55     struct epoll_event event;
 56     event.events=EPOLLIN|EPOLLET;
 57     event.data.fd=sockfd;//把server socket fd封装进events里面
 58 
 59     //epoll_ctl设置属性,注册事件
 60     if(epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event)<0)
 61     {
 62         printf("epoll 加入失败 fd:%d\n",sockfd);
 63         exit(-1);
 64     }
 65 
 66     while(1)
 67     {
 68         int timeout=300;//设置超时;在select中使用的是timeval结构体
 69         //epoll_wait epoll处理
 70         //ret会返回在规定的时间内获取到IO数据的个数,并把获取到的event保存在eventList中,注意在每次执行该函数时eventList都会清空,由epoll_wait函数填写。
 71         //而不清除已经EPOLL_CTL_ADD到epollfd描述符的其他加入的文件描述符。这一点与select不同,select每次都要进行FD_SET,具体可看我的select讲解。
 72         //epoll里面的文件描述符要手动通过EPOLL_CTL_DEL进行删除。
 73         int ret=epoll_wait(epollfd,eventList,MAX_EVENTS,timeout);
 74 
 75         if(ret<0)
 76         {
 77             perror("epoll error\n");
 78             break;
 79         }
 80         else if(ret==0)
 81         {
 82             //超时
 83             continue;
 84         }
 85 
 86         //直接获取了事件数量,给出了活动的流,这里就是跟selec,poll区别的关键 //select要用遍历整个数组才知道是那个文件描述符有事件。而epoll直接就把有事件的文件描述符按顺序保存在eventList中
 87         for(int i=0;i<ret;i++)
 88         {
 89             //错误输出
 90             if((eventList[i].events & EPOLLERR) || (eventList[i].events & EPOLLHUP) || !(eventList[i].events & EPOLLIN))
 91             {
 92                 printf("epoll error\n");
 93                 close(eventList[i].data.fd);
 94                 exit(-1);
 95             }
 96 
 97             if(eventList[i].data.fd==sockfd)
 98             {
 99                 //这个是判断sockfd的,主要是用于接收客户端的连接accept
100                 AcceptConn(sockfd,epollfd);
101             }
102             else //里面可以通过判断eventList[i].events&EPOLLIN 或者 eventList[i].events&EPOLLOUT 来区分当前描述符的连接是对应recv还是send
103             {
104                 //其他所有与客户端连接的clientfd文件描述符
105                 //获取数据等操作
106                 //如需不接收客户端发来的数据,但是不关闭连接。
107                 //epoll_ctl(epollfd, EPOLL_CTL_DEL,eventList[i].data.fd,eventList[i]);
108                 //Handle对各个客户端发送的数据进行处理
109                 Handle(eventList[i].data.fd);
110             }
111         }
112     }
113 
114     close(epollfd);
115     close(sockfd);
116     return 0;
117 }
118 
119 void AcceptConn(int sockfd,int epollfd)
120 {
121     struct sockaddr_in sin;
122     socklen_t len=sizeof(struct sockaddr_in);
123     bzero(&sin,len);
124 
125     int confd=accept(sockfd,(struct sockaddr *)&sin,&len);
126 
127     if(confd<0)
128     {
129         perror("connect error\n");
130         exit(-1);
131     }
132 
133     //把客户端新建立的连接添加到EPOLL的监听中
134     struct epoll_event event;
135     event.data.fd=confd;
136     event.events=EPOLLIN|EPOLLET;
137     epoll_ctl(epollfd,EPOLL_CTL_ADD,confd,&event);
138     return ;
139 }
140 
141 void Handle(int clientfd)
142 {
143     int recvLen=0;
144     char recvBuf[MAX_DATA_SIZE];
145     memset(recvBuf,0,sizeof(recvBuf));
146     recvLen=recv(clientfd,(char *)recvBuf,MAX_DATA_SIZE,0);
147     if(recvLen==0)
148         return ;
149     else if(recvLen<0)
150     {
151         perror("recv Error");
152         exit(-1);
153     }
154     //各种处理
155     printf("接收到的数据:%s \n",recvBuf);
156     return ;
157 }
复制代码

 


  epoll参考资料

  http://blog.csdn.net/mmz_xiaokong/article/details/8704988
  http://blog.csdn.net/mmz_xiaokong/article/details/8704455
  http://www.cppblog.com/converse/archive/2008/10/13/63928.html
  http://blog.csdn.net/haoahua/article/details/2037704
  https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/

  epoll为什么这么快: http://www.cppblog.com/converse/archive/2008/10/12/63836.html

 

  本文地址: http://www.cnblogs.com/wunaozai/p/3895860.html


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程间通信(匿名管道)

1.进程通信的目的 (1) 数据传输: 一个进程需要将它的数据传输给另一个进程     (2) 资源共享: 多个进程之间共享同样的资源     (3) 通知事件: 一个进程需要向另一个或一组进程发送消息, 通知它们发生了什么事情 2.管道 管道是一种进程之间通信的一种方式, 我们把从…

线性基入门

今天学习了神奇的线性基&#xff0c;主要是在解决异或问题时比较有用。 详细的解释和证明有大佬珠玉在前&#xff0c;如果感兴趣可以移步 补充一下自己的理解&#xff1a; 可以联系线性代数极大无关组进行理解&#xff0c;线性基就相当于异或的向量空间中的极大无关组&#xff…

单例模式及C++实现代码

http://www.cnblogs.com/cxjchen/p/3148582.html 单例模式 单例模式&#xff0c;可以说设计模式中最常应用的一种模式了&#xff0c;据说也是面试官最喜欢的题目。但是如果没有学过设计模式的人&#xff0c;可能不会想到要去应用单例模式&#xff0c;面对单例模式适用的情况&am…

UVALive - 8512——线段树维护线性基

【题目描述】 UVALive - 8512XOR 【题目分析】 这种区间线性基的问题我们可以考虑用线段树维护&#xff0c;线性基的合并的话就直接暴力合并 找到所在区间的线性基后再查找最大的数&#xff0c;我看网上的博客要说消除k的影响什么的&#xff0c;我觉得没有什么必要&#xff0c;…

命名管道

1.命名管道的创建 (1) 通过命令创建 mkfifo filename (2)在程序中创建 int mkfifo(const char* filename, mode_t mode); 2. 命名管道和匿名管道的区别 (1)匿名管道由pipe函数创建并且打开     (2)命名管道有mkfifo函数创建由open函数打开     (3) fifo 之间的两…

HYSBZ - 1101——莫比乌斯反演

【题目描述】 HYSBZ - 1101 【题目分析】 昨天测试出了一道差不多的题目&#xff0c;我只能想到暴力&#xff0c;各种优化&#xff0c;最后都是运行了好久TLE&#xff0c;最后才知道要用到莫比乌斯反演&#xff0c;就想着今天研究一下&#xff0c;得出的结论就是&#xff0c;我…

Linux下I/O多路转接之select --fd_set

http://blog.csdn.net/li_ning_/article/details/52165993 fd_set 你终于还是来了&#xff0c;能看到这个标题进来的&#xff0c;我想&#xff0c;你一定是和我遇到了一样的问题&#xff0c;一样的疑惑&#xff0c;接下来几个小时&#xff0c;我一定竭尽全力&#xff0c;写出我…

BZOJ 2844 | HYSBZ - 2844albus就是要第一个出场——线性基

【题目描述】 BZOJ 2844 | HYSBZ - 2844albus 【题目分析】 题目的意思大概是给一个数列&#xff0c;他有2n个子集&#xff0c;每个子集的元素的异或和构成新的一个数列&#xff0c;排序后问数字Q在这个序列里面的下标。 假如题目是求所有元素的异或和构成一个集合就好弄了&…

CodeForces - 641ELittle Artem and Time Machine——map+树状数组

【题目描述】 CodeForces - 641ELittle Artem and Time Machine 【题目分析】 题目的意思大概是有三种操作 1.在时间t加入一个数字x 2.在时间t删除一个数字x 3.询问在时间t集合里面x的个数 虽然题目描述很简单&#xff0c;但是t和x的范围都是109&#xff0c;我一开始想到的是主…

I/O多路转接之poll 函数

http://blog.csdn.net/li_ning_/article/details/52167224 poll 一、poll()函数&#xff1a; 这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数&#xff0c;自认为poll和select大同小异&#xff0c;下面是这个函数的声明&#xff1a; [cpp] view plaincopy …

链表相关笔试面试题

1.判断两个链表是否相交 两个链表是否相交可分为以下几种情况     &#xff08;1&#xff09;两个链表都不带环&#xff0c;此时两个链表所对应的最后一个节点是相等的     &#xff08;2&#xff09;两个链表一个带环&#xff0c;一个不带环&#xff0c;两个链表一定…

Linux经典问题—五哲学家就餐问题

http://m.blog.csdn.net/aspenstars/article/details/70149038 一、问题介绍 由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。该问题是描述有五个哲学家共用一张圆桌&#xff0c;分别坐在周围的五张椅子上&#xff0c;在圆桌上有五…

修改之前的myshell使之支持输入输出重定向

1.open函数     这个函数是打开一个文件&#xff08;文件名叫pathname),以 flag 权限打开&#xff0c;flag 包括了以下几种 O_RDONLY&#xff08;只读&#xff09;, O_WRONLY&#xff08;只写&#xff09;, O_RDWR&#xff08;读写&#xff09;&#xff0c;当文件打开成…

HDU - 6621 K-th Closest Distance——主席树+二分

【题目描述】 HDU - 6621 K-th Closest Distance 【题目分析】 因为看到第kkk大的要求&#xff0c;刚开始的时候一直都在想怎么运用第kkk大来解决问题&#xff0c;但是后来看其他人的博客才发现并不需要用第k大&#xff0c;但是主席树维护权值线段树还是需要的&#xff0c;这…

链表相关的算法题大汇总 — 数据结构之链表奇思妙想

http://blog.csdn.net/lanxuezaipiao/article/details/22100021基本函数&#xff08;具体代码实现见后面&#xff09; 1&#xff0c;构造节点 //定义节点类型 struct Node { int value; Node*next; }; 2&#xff0c;分配节点 //之所以要分配节点原因是需要在分配函数中…

CodeForces - 372CWatching Fireworks is Fun+DP+单调队列优化

【题目描述】 CodeForces - 372CWatching Fireworks is Fun 题目的大概意思就是在一个编号为1…n的街道上现在按照时间顺序放烟花&#xff0c;每个烟花获得的幸福感为b−abs(a−x)b-abs(a-x)b−abs(a−x)&#xff0c;x为观看烟花的位置&#xff0c;为了提升我们的幸福感&#x…

双向链表的基本操作

1.双向链表的数据结构 typedef char DLinkType;typedef struct DLinkNode { DLinkType data; struct DLinkNode* next; struct DLinkNode* prev; }DLinkNode; 双向带头结点的链表有三个成员&#xff0c; 一个是数据&#xff0c; 一个是指针 next 指向当前结点的下一个结点&…

匿名管道

1.进程通信的目的 (1) 数据传输: 一个进程需要将它的数据传输给另一个进程     (2) 资源共享: 多个进程之间共享同样的资源     (3) 通知事件: 一个进程需要向另一个或一组进程发送消息, 通知它们发生了什么事情 2.管道 管道是一种进程之间通信的一种方式, 我们把从…

Currency Exchange——最短路Bellman-Ford算法

【题目描述】 Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and performs exchange operations only with these currencies. There can be several points specializing in the sam…

C++实现String类

http://blog.csdn.net/randyjiawenjie/article/details/6709539 C实现String类&#xff0c;还没有完成&#xff0c;待继续。 有以下注意的点&#xff1a; &#xff08;1&#xff09;赋值操作符返回的是一个MyString&&#xff0c;而重载的返回的是一个MyString。其中的原因…