我经常访问Xorg的官网,希望能找到一些对理解Linux和X Window有用的东西。结果也确实是偶有所得。比如,在Xorg的官网中就建议大家不用Xlib了,改用XCB。不可否认,Xlib确实是一个比较老的东西,老到最新的一本关于Xlib的书都已经是N多年前出版的了。有了Xorg官方的指导,我自然不用学Xlib了,直接上XCB。
经过这一段时间的学习,对XCB有了一定的了解。我的学习是根据XCB官方的教程来的,当然,如果有一点点在GUI编程领域的经验和悟性学习起来会更加事半功倍。在XCB的官方教程中,一开篇就提出XCB比Xlib先进的地方,同时给出一个示例程序证明XCB比Xlib快多少。但是,对于没有一点Xlib和XCB基础的人来说,要理解这个程序确实有点难。但是现在回过头去看这个教程的开头,一切都是那么一目了然。
XCB取代Xlib大致上有这样一些理由:
1、Xlib太重量级了,不易转型,而XCB则轻量级得多;
2、虽然说Xlib是最靠近X协议的底层开发库,但是其实它还不够底层,因为它提供了缓存机制,而XCB没有缓存,一切都是直接操作X协议,比Xlib还要底层;
3、据说Xlib对新的X扩展支持不好;
4、Xlib对多线程编程支持不好,虽然它努力过,但是基础没打好,所以怎么努力也干不好这件事,而XCB没有历史负担,适用于多线程编程;
5、Xlib和X Server的通讯时同步的,而XCB是异步的,所以XCB比Xlib要快得多。
我这里重点展示第5点。在前面一篇中我对XCB的异步机制有具体的展示。XCB的异步机制其实就是向服务器提交一个请求后,不用等待服务器的答复就可以继续提交后面的请求,因此,不会因为数据在网络上传输的时间而影响程序的效率。而Xlib向X Server提交一个请求后,必须等到服务器答复,才能提交下一个请求。
下面是一个示例程序,在这个程序中,分别以好的方式和坏的方式使用XCB和Xlib,请求服务器创建500个Atom,比较XCB和Xlib分别花的时间。很幸运,关于Atom上一篇中也提到了,所以不陌生。使用Xlib的坏的方式是一个一个地调用XInternAtom()函数,每次都要等服务器返回后才能创建下一个Atom,所以这是最慢的方式,而使用Xlib的好的方式是使用XInternAtoms()函数一次性创建500个Atom。而使用XCB的坏的方式是,每次调用xcb_intern_atom()函数后,再调用xcb_intern_atom_reply(),确认服务器答复后再创建下一个Atom,很显然这是一个较慢的方式,而且,由于和服务器的一次对话需要调用两个函数,所以使用起来比Xlib还要麻烦。而使用XCB的好的方式,当然是一股脑调用500次xcb_intern_atom()函数,这500次调用完成之后再来关注服务器的答复。示例代码如下,我基本照抄了XCB教程中的代码,只改了最后的几句printf。
1 /* It‘s a good idea to paste this and other long code examples 2 into a text editor for easier reading */ 3 4 #include <stdlib.h> 5 #include <stdio.h> 6 #include <string.h> 7 #include <sys/time.h> 8 #include <xcb/xcb.h> 9 #include <X11/Xlib.h> 10 #define NUM_NAMES 500 11 /* 12 NOTE: For concision, we‘re going to be cheesy and use arrays where real code 13 would use points and memory allocation.s 14 */ 15 #ifndef __GNUC__ 16 char* strdup(const char* s) { 17 int n = strlen(s) + 1; 18 19 char *dup = malloc(n); 20 21 if(dup) 22 strcpy(dup, s); 23 24 return dup; 25 } 26 #endif 27 28 /* 29 return interval of time (uses time.h) 30 */ 31 double 32 get_time (void) { 33 struct timeval timev; 34 gettimeofday(&timev, NULL); 35 return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); 36 } 37 38 /* 39 40 */ 41 void 42 useXlib (char **names, 43 Display *display ) { 44 45 Atom atoms[NUM_NAMES]; 46 for (int i = 0; i < NUM_NAMES; ++i) { 47 atoms[i] = XInternAtom(display, names[i], 0); 48 } 49 } 50 51 /* 52 Request all atoms at once. 53 */ 54 void 55 useXlibProperly (char **names, 56 Display *display ) { 57 58 Atom atoms[NUM_NAMES]; 59 if(!XInternAtoms(display, names, NUM_NAMES, 0, atoms)) 60 fprintf(stderr, "XInternAtoms failed\n"); 61 } 62 63 /* 64 65 */ 66 void 67 useXCBPoorly (char **names, 68 xcb_connection_t *connection ) { 69 xcb_atom_t atoms[NUM_NAMES]; 70 // in this bad use of xcb, we use the cookie immediately after posting the request with xcb_intern_atom 71 for (int i = 0; i < NUM_NAMES; ++i) { 72 /* make request */ 73 xcb_intern_atom_cookie_t cookie = xcb_intern_atom (connection, 74 0, 75 strlen(names[i]), 76 names[i] ); 77 /* get response */ 78 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply (connection, 79 cookie, 80 NULL ); // normally a pointer to receive error, but we‘ll 81 82 just ignore error handling 83 if (reply) { 84 atoms[i] = reply->atom; 85 free (reply); 86 } 87 } 88 // now we have our atoms (replies), but this is just a demo, so we do nothing with them 89 } 90 91 /* 92 */ 93 void 94 useXCBProperly (char **names, 95 xcb_connection_t *connection ) { 96 xcb_atom_t atoms[NUM_NAMES]; 97 xcb_intern_atom_cookie_t cookies[NUM_NAMES]; 98 // in this good example, we make all our requests before checking for 99 // replies because it‘s best to queue requests when we have many at once 100 /* make requests */ 101 for (int i = 0; i < NUM_NAMES; ++i) { 102 cookies[i] = xcb_intern_atom (connection, 103 0, 104 strlen (names[i]), 105 names[i] ); 106 } 107 /* get responses */ 108 for (int i = 0; i < NUM_NAMES; ++i) { 109 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply (connection, 110 cookies[i], 111 NULL ); // normally a pointer to receive errors, but we‘ll 112 113 just ignore error handling 114 if (reply) { 115 atoms[i] = reply->atom; 116 free (reply); 117 } 118 } 119 // now we have our atoms (replies), but this is just a demo, so we do nothing with them 120 } 121 122 int 123 main () { 124 /* setup names for tests */ 125 char (**names) = malloc(NUM_NAMES*sizeof(*names)); 126 // init names to "NAME0", "NAME1", "NAME2" ... and so on 127 for (int i = 0; i < NUM_NAMES; ++i) { 128 char buf[100]; 129 sprintf (buf, "NAME%d", i); 130 names[i] = strdup (buf); 131 } 132 133 /* do tests */ 134 double start, XlibTime, XlibGoodTime, XCBBadTime, XCBGoodTime; 135 136 /* test Xlib */ 137 Display *display = XOpenDisplay (NULL); 138 start = get_time (); 139 useXlib (names, display); 140 XlibTime = get_time () - start; 141 start = get_time (); 142 useXlibProperly (names, display); 143 XlibGoodTime = get_time () - start; 144 XCloseDisplay (display); 145 146 /* test XCB */ 147 xcb_connection_t *connection = xcb_connect (NULL, NULL); 148 start = get_time (); 149 useXCBPoorly (names, connection); 150 XCBBadTime = get_time () - start; 151 start = get_time (); 152 useXCBProperly (names, connection); 153 XCBGoodTime = get_time () - start; 154 xcb_disconnect (connection); 155 156 /* report times */ 157 printf ("Bad Xlib time : %f\n", XlibTime); 158 printf ("Good Xlib time : %f\n", XlibGoodTime); 159 printf ("Bad xcb time : %f\n", XCBBadTime); 160 printf ("Good xcb time : %f\n", XCBGoodTime); 161 printf ("ratio of bad xcb time to good xcb time: %f\n", XCBBadTime / XCBGoodTime); 162 printf ("ratio of bad Xlib time to good Xlib time: %f\n", XlibGoodTime / XlibTime); 163 printf ("ratio of bad Xlib time to good XCB time: %f\n", XlibTime / XCBGoodTime); 164 printf ("ratio of good Xlib time to good xcb time: %f\n", XlibGoodTime / XCBGoodTime); 165 return 0; 166 }
运行效果如下图:
从图中可以看出,XCB的异步方式比同步的方式要快几十倍,Xlib好的方式比坏的方式要快100多倍。由于XCB默认就是异步的,而Xlib天生是同步的,所以最好的和最坏的比较,效率相差300多倍。这就是XCB取代Xlib的理由。
(京山游侠于2014-08-01发布于博客园,转载请注明出处。)
使用XCB编写X Window程序(06):XCB取代Xlib的理由,布布扣,bubuko.com