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.pswp.cn/news/384108.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384108.shtml
英文地址,請注明出處:http://en.pswp.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。其中的原因…