webbench 代码阅读
简介:
Webbech能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况.webBech的标准测试可以向我们展示服务器的两项 内容:每秒钟相应请求数和每秒钟传输数据量.webbench不但能具有便准静态页面的测试能力,还能对动态页面(ASP,PHP,JAVA,CGI)进 行测试的能力.还有就是他支持对含有SSL的安全网站例如电子商务网站进行静态或动态的性能测试.
×××××××××××××××××××××××××吐槽,可忽略××××××××××××××××××××××
好吧 , 我承认好高端,但整个代码也就几百行,还是算上注释呀,看完之后,完全没有什么动态网页测试,SSL 安全网站测试什么选项呀!!!!
,只有选择http协议的请求头选项呀,什么GET,HEAD,之类的
×××××××××××××××××××××××××××××××××××××××××××××××××××××
了解代码,你先得了解它的业务流程
难点一:获取web服务器信息
解决这个问题,首先你的了解你要测试什么? 你要测试的是一个web服务器应对大量请求的能力,就是所说的压力测试。你要做的是同时构造大量的请求,达到测试目的。
请求是由 URL 地址连接 指示的,你要明白URL 地址连接由什么组成,你需要从其中获取那些必要的信息构成请求。url的一般模式为
http://myname:mypass@www.vimer.cn:80/mydir/myfile.html?myvar=myvalue#myfrag
URI部分 |
意义 |
http |
协议名称 |
myname |
用户名(可选) |
mypass |
密码(可选) |
www.vimer.cn |
主机网络地址 |
80 |
端口号(可选) |
/mydir/myfile.html |
资源路径 |
myvar=myvalue |
查询字符串(可选) |
myfrag |
锚点(可选) |
要从url 提取 host 和 request ,host是主机网络地址,reques 是利用http请求头和资源路径构成的一个http协议请求,web服务器会处理这个请求并且返回资源,一个例子:
输入: http://www.vimer.cn/2010/02/%e7%ae%80%e6%98%8ehttp%e5%8d%8f%e8%ae%ae.html host www.vimer.cn request GET /2010/02/%e7%ae%80%e6%98%8ehttp%e5%8d%8f%e8%ae%ae.html HTTP/1.0
如果你纵览了代码,可以说60%的功能都用来进行字符串操作,来获得 host 和 request 不无论是mian函数还build_request 都是来实现这个功能的。具体的细节实现可以参考代码注释,注意一下,使用了大量的字符串函数,随时要准备man一下。
难点二:如何测评,依据是什么
测试就的有一个标准,webbench的标准是成功使用stock建立的http链接,benchwork函数做的就是这样一项工作。webbench使用的是多进程操作,
它选用了通道作为parent和childern的联系方式,来告知parent,child成功或失败建立了几个连接,传送的总的字节数是多少。在博文的最后有一个benchwork函数的流程图,有兴趣的可以看一下
代码
webbench.c
1 /* 2 * (C) Radim Kolar 1997-2004 3 * This is free software, see GNU Public License version 2 for 4 * details. 5 * 6 * Simple forking WWW Server benchmark: 7 * 8 * Usage: 9 * webbench --help 10 * 11 * Return codes: 12 * 0 - sucess 13 * 1 - benchmark failed (server is not on-line) 14 * 2 - bad param 15 * 3 - internal error, fork failed 16 * 17 */ 18 #include "socket.c" 19 #include <unistd.h> 20 #include <sys/param.h> //描述系统参数 21 #include <rpc/types.h> 22 #include <getopt.h>//参数分析 23 #include <strings.h> 24 #include <time.h> 25 #include <signal.h>//信号处理 26 27 /* values */ 28 volatile int timerexpired=0; 29 /* volatile 30 * 提示编译器所定义的变量随时可能改变,因此编译后的程序每次需要存储或是i 31 * 读取该变量的时候,都会直接从变量地址读取数据。 32 * 之所以要这样做,是因为编译器会对代码的读取和存储进行优化,可能暂时使用 33 * 寄存器的值。 34 * */ 35 int speed=0; 36 int failed=0; 37 int bytes=0; 38 /* globals */ 39 int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ 40 /* Allow: GET, HEAD, OPTIONS, TRACE */ 41 #define METHOD_GET 0 42 #define METHOD_HEAD 1 43 #define METHOD_OPTIONS 2 44 #define METHOD_TRACE 3 45 #define PROGRAM_VERSION "1.5" 46 int method=METHOD_GET; 47 int clients=1;/* 测试进程数,默认1:w*/ 48 int force=0;/* 等待服务器返回标志 ,默认为等待返回 0 */ 49 int force_reload=0; 50 int proxyport=80;/* 服务器端口号,http协议默认为80端口*/ 51 char *proxyhost=NULL;//代理服务器 52 int benchtime=30;/* 运行时间,默认为30 */ 53 /* internal */ 54 int mypipe[2]; /* 通道 */ 55 char host[MAXHOSTNAMELEN];/* 域名 */ 56 #define REQUEST_SIZE 2048 57 char request[REQUEST_SIZE];/* http 请求头 */ 58 59 static const struct option long_options[]= 60 { 61 {"force",no_argument,&force,1}, 62 {"reload",no_argument,&force_reload,1}, 63 {"time",required_argument,NULL,'t'}, 64 {"help",no_argument,NULL,'?'}, 65 {"http09",no_argument,NULL,'9'}, 66 {"http10",no_argument,NULL,'1'}, 67 {"http11",no_argument,NULL,'2'}, 68 {"get",no_argument,&method,METHOD_GET}, 69 {"head",no_argument,&method,METHOD_HEAD}, 70 {"options",no_argument,&method,METHOD_OPTIONS}, 71 {"trace",no_argument,&method,METHOD_TRACE}, 72 {"version",no_argument,NULL,'V'}, 73 {"proxy",required_argument,NULL,'p'}, 74 {"clients",required_argument,NULL,'c'}, 75 {NULL,0,NULL,0} 76 }; 77 /* 静态函数 78 * 特性: 79 * 1.周期:整个程序,范围:本文件 80 * 2.使用static作为前缀,仅可以本文件函数调用, 81 * 不能被同一程序的其他文件调用 82 * 3.可以在不同文件里使用相同的函数名,不用担心冲突 83 * */ 84 /* prototypes */ 85 static void benchcore(const char* host,const int port, const char *request); 86 /* 测试 host 的连接 功能函数 */ 87 static int bench(void); 88 /* 测试 host 的前期准备 和 多进程操作与同行 */ 89 static void build_request(const char *url); 90 /* 解析 request */ 91 92 static void alarm_handler(int signal) 93 /* 和sig 和signaction 构成一个时钟 */ 94 { 95 timerexpired=1; 96 } 97 98 static void usage(void) 99 /* 帮助 */ 100 { 101 fprintf(stderr, 102 "webbench [option]... URL\n" 103 " -f|--force Don't wait for reply from server.\n" 104 " -r|--reload Send reload request - Pragma: no-cache.\n" 105 " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n" 106 " -p|--proxy <server:port> Use proxy server for request.\n" 107 " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n" 108 " -9|--http09 Use HTTP/0.9 style requests.\n" 109 " -1|--http10 Use HTTP/1.0 protocol.\n" 110 " -2|--http11 Use HTTP/1.1 protocol.\n" 111 " --get Use GET request method.\n" 112 " --head Use HEAD request method.\n" 113 " --options Use OPTIONS request method.\n" 114 " --trace Use TRACE request method.\n" 115 " -?|-h|--help This information.\n" 116 " -V|--version Display program version.\n" 117 ); 118 }; 119 int main(int argc, char *argv[]) 120 { 121 int opt=0; 122 int options_index=0; 123 char *tmp=NULL; 124 125 if(argc==1) 126 { 127 usage(); 128 return 2; 129 } 130 131 while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF ) 132 /* 使用getopt 函数 获取分析命令,对一些参数进行设置 */ 133 { 134 switch(opt) 135 { 136 case 0 : break; 137 case 'f': force=1;break;//Don't wait for reply from server 138 case 'r': force_reload=1;break;//Send reload request - Pragma: no-cache 139 case '9': http10=0;break; 140 case '1': http10=1;break; 141 case '2': http10=2;break; 142 case 'V': printf(PROGRAM_VERSION"\n");exit(0); 143 case 't': benchtime=atoi(optarg);break;//optarg[ getopt的全局变量 ] : 指向当前选项参数的指针 144 case 'p': 145 /* proxy server parsing server:port */ 146 tmp=strrchr(optarg,':'); 147 /* char * strrchr(const char * s,int c ) 148 * 返回 字符c 在字符串s末次出现的位置【返回值指向的是'c' 149 * */ 150 /* 返回的是端口号 151 * */ 152 proxyhost=optarg; 153 if(tmp==NULL) 154 { 155 break; 156 } 157 if(tmp==optarg) 158 { 159 fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg); 160 return 2; 161 }//只有 :port 162 if(tmp==optarg+strlen(optarg)-1) 163 { 164 fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg); 165 return 2; 166 }//只有 server: 167 *tmp='\0'; 168 proxyport=atoi(tmp+1);break;//获取端口号 169 case ':': 170 case 'h': 171 case '?': usage();return 2;break; 172 case 'c': clients=atoi(optarg);break; 173 } 174 } 175 176 if(optind==argc) { 177 /* optind getopt_long 函数的全局变量 ,表示下一个要解析的参数的位置 178 * 初始值为 1 179 180 可以这么理解: 181 如果一个程序有一个参数是必须需要的,则optind < argv,例如 webbench需要一个测试 182 目标URL,那么 webbench 会占用 0 的下标,而optind 已经默认初始化为1,可以预见 183 参数和参数的选项都会被getopt()操作后,optind 会指向下一个下标[无论是否存在], 184 不存在的话, optind就会和 argc 相等,相当于只输入了一个webbench , 185 optind == argc == 1 , 186 * */ 187 fprintf(stderr,"webbench: Missing URL!\n"); 188 usage(); 189 return 2; 190 } 191 192 if(clients==0) clients=1; 193 if(benchtime==0) benchtime=60; 194 /* Copyright */ 195 fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n" 196 "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n" 197 ); 198 build_request(argv[optind]); 199 /* print bench info */ 200 printf("\nBenchmarking: "); 201 switch(method) 202 { 203 case METHOD_GET: 204 default: 205 printf("GET");break; 206 case METHOD_OPTIONS: 207 printf("OPTIONS");break; 208 case METHOD_HEAD: 209 printf("HEAD");break; 210 case METHOD_TRACE: 211 printf("TRACE");break; 212 } 213 // printf(" %s",argv[optind]); 214 switch(http10) 215 { 216 case 0: printf(" (using HTTP/0.9)");break; 217 case 2: printf(" (using HTTP/1.1)");break; 218 } 219 printf("\n"); 220 if(clients==1) printf("1 client"); 221 else 222 printf("%d clients",clients); 223 224 printf(", running %d sec", benchtime); 225 if(force) printf(", early socket close"); 226 if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport); 227 if(force_reload) printf(", forcing reload"); 228 printf(".\n"); 229 return bench(); 230 } 231 232 void build_request(const char *url) 233 { 234 char tmp[10]; 235 int i; 236 237 bzero(host,MAXHOSTNAMELEN); 238 bzero(request,REQUEST_SIZE); 239 /* 初始化空间为0 */ 240 241 if(force_reload && proxyhost!=NULL && http10<1) http10=1; 242 if(method==METHOD_HEAD && http10<1) http10=1; 243 if(method==METHOD_OPTIONS && http10<2) http10=2; 244 if(method==METHOD_TRACE && http10<2) http10=2; 245 /* 不知所以 ,缺少fftp的基本的基本知识 */ 246 247 switch(method) 248 { 249 default: 250 case METHOD_GET: strcpy(request,"GET");break; 251 case METHOD_HEAD: strcpy(request,"HEAD");break; 252 case METHOD_OPTIONS: strcpy(request,"OPTIONS");break; 253 case METHOD_TRACE: strcpy(request,"TRACE");break; 254 } 255 256 strcat(request," "); 257 258 if(NULL==strstr(url,"://")) 259 { 260 fprintf(stderr, "\n%s: is not a valid URL.\n",url); 261 exit(2); 262 } 263 if(strlen(url)>1500) 264 { 265 fprintf(stderr,"URL is too long.\n"); 266 exit(2); 267 } 268 if(proxyhost==NULL) 269 if (0!=strncasecmp("http://",url,7)) 270 /* int strncasecmp(char *s1,char *s2,int i) 271 * 只比较s1的前 i 位和 s2的关系 272 * */ 273 { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n"); 274 exit(2); 275 } 276 /* protocol/host delimiter */ 277 i=strstr(url,"://")-url+3; 278 //printf("strstr(url,""): %p, url :%p, i = %d \n",strstr(url,"://"),url,i); 279 /* 根据 数组在内存中存放的形式, 可以利用 strstr(..) 求的地址A,同时利用 url的 280 * 地址 B, A-B = 4[ 从低位开始放,就像压栈一样 ], 281 * 最后的 i 表示 一个http网址中, :// 之后的第一字符的下标 282 * 例如: http;//www.yankanshu.com .i 表示 w的下标位置 283 * */ 284 285 if(strchr(url+i,'/')==NULL) { 286 fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n"); 287 exit(2); 288 } 289 if(proxyhost==NULL) 290 { 291 /* get port from hostname */ 292 if(index(url+i,':')!=NULL && 293 index(url+i,':')<index(url+i,'/')) 294 { 295 // printf("test 1\n "); 296 strncpy(host,url+i,strchr(url+i,':')-url-i); 297 bzero(tmp,10); 298 strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1); 299 /* printf("tmp=%s\n",tmp); */ 300 proxyport=atoi(tmp); 301 if(proxyport==0) proxyport=80; 302 } else 303 { 304 // printf("test 2\n "); 305 strncpy(host,url+i,strcspn(url+i,"/")); 306 } 307 // printf("Host=%s\n",host); 308 /* 最后的host 是不带 http: 和 最后的 \ 309 * 例如: http://baike.baidu.com/ 310 * host = baike.baidu.com 311 * */ 312 strcat(request+strlen(request),url+i+strcspn(url+i,"/")); 313 /*没有理解目的 : 314 * 根据测试: 315 * GET 变成了 GET /.....*/ 316 } else 317 { 318 // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport); 319 strcat(request,url); 320 } 321 if(http10==1) 322 strcat(request," HTTP/1.0"); 323 else if (http10==2) 324 strcat(request," HTTP/1.1"); 325 strcat(request,"\r\n"); 326 if(http10>0) 327 strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n"); 328 /* 字符串组合技巧 329 * */ 330 if(proxyhost==NULL && http10>0) 331 { 332 strcat(request,"Host: "); 333 strcat(request,host); 334 strcat(request,"\r\n"); 335 } 336 if(force_reload && proxyhost!=NULL) 337 { 338 strcat(request,"Pragma: no-cache\r\n"); 339 } 340 if(http10>1) 341 strcat(request,"Connection: close\r\n"); 342 /* add empty line at end */ 343 if(http10>0) strcat(request,"\r\n"); 344 // printf("Req=%s\n",request); 345 } 346 347 /* vraci system rc error kod */ 348 static int bench(void) 349 { 350 int i,j,k; 351 pid_t pid=0; 352 FILE *f; 353 354 /* check avaibility of target server */ 355 // printf("commmd: proxyhost= host %s proxhost %s\n",host,proxyhost); 356 i=Socket(proxyhost==NULL?host:proxyhost,proxyport); 357 //printf("commd: test3\n"); 358 359 /* 建立测试网址的套接字 360 * 可以后的host的直接使用host 361 * 不可以的利用gethostbyname函数通过域名解析获得host 362 * */ 363 if(i<0) { 364 fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n"); 365 return 1; 366 } 367 close(i); 368 /* create pipe */ 369 if(pipe(mypipe)) 370 /* 进程间的通信方式 371 * 通道 372 * */ 373 { 374 perror("pipe failed."); 375 return 3; 376 } 377 378 /* not needed, since we have alarm() in childrens */ 379 /* wait 4 next system clock tick */ 380 /* 381 cas=time(NULL); 382 while(time(NULL)==cas) 383 sched_yield(); 384 */ 385 386 /* fork childs */ 387 for(i=0;i<clients;i++) 388 { 389 pid=fork(); 390 if(pid <= (pid_t) 0)// 为了防止什么 ???? 391 { 392 /* child process or error*/ 393 sleep(1); /* make childs faster */ 394 break; 395 } 396 } 397 398 if( pid< (pid_t) 0) 399 { 400 fprintf(stderr,"problems forking worker no. %d\n",i); 401 perror("fork failed."); 402 return 3; 403 } 404 405 if(pid== (pid_t) 0) 406 { 407 printf("commnd:request:%s\n",request); 408 /* I am a child */ 409 if(proxyhost==NULL) 410 benchcore(host,proxyport,request); 411 else 412 benchcore(proxyhost,proxyport,request); 413 414 /* write results to pipe */ 415 f=fdopen(mypipe[1],"w"); 416 if(f==NULL) 417 { 418 perror("open pipe for writing failed."); 419 return 3; 420 } 421 /* fprintf(stderr,"Child - %d %d\n",speed,failed); */ 422 fprintf(f,"%d %d %d\n",speed,failed,bytes); 423 fclose(f); 424 return 0; 425 } else 426 { 427 f=fdopen(mypipe[0],"r"); 428 if(f==NULL) 429 { 430 perror("open pipe for reading failed."); 431 return 3; 432 } 433 /* 作为一个读的文件流,为什么要设置成 nobuf ???? 434 * */ 435 setvbuf(f,NULL,_IONBF,0); 436 speed=0; 437 failed=0; 438 bytes=0; 439 440 while(1) 441 { 442 pid=fscanf(f,"%d %d %d",&i,&j,&k); 443 if(pid<2) 444 { 445 fprintf(stderr,"Some of our childrens died.\n"); 446 break; 447 } 448 speed+=i; 449 failed+=j; 450 bytes+=k; 451 /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */ 452 if(--clients==0) break; 453 } 454 fclose(f); 455 456 printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n", 457 (int)((speed+failed)/(benchtime/60.0f)), 458 (int)(bytes/(float)benchtime), 459 speed, 460 failed); 461 } 462 return i; 463 } 464 465 void benchcore(const char *host,const int port,const char *req) 466 { 467 int rlen; 468 char buf[1500]; 469 int s,i; 470 struct sigaction sa; 471 472 /* setup alarm signal handler */ 473 sa.sa_handler=alarm_handler; 474 sa.sa_flags=0; 475 if(sigaction(SIGALRM,&sa,NULL)) 476 /* SIGALRM 是 定时器终止时发送给进程的信号 477 * SIG 是信号名的通用前缀,ALRM是alarm的缩写 478 * 通常作为长时间操作的超时信号 479 * 或者提供一种隔一定时间处理某些操作的方式 480 * 一般是在调用 alarm(t ) t秒后出现 481 * */ 482 exit(3); 483 alarm(benchtime); 484 /* sigcation 和 alarm 设置了一个闹钟,用来定时退出 */ 485 486 rlen=strlen(req); 487 nexttry:while(1) 488 { 489 if(timerexpired) 490 { 491 if(failed>0) 492 { 493 /* fprintf(stderr,"Correcting failed by signal\n"); */ 494 failed--; 495 } 496 return; 497 } 498 s=Socket(host,port); 499 if(s<0) { failed++;continue;} 500 /* 申请建立连接失败, fail++ 501 * */ 502 if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;} 503 /* 无法写入套接字,算是失败吧 ???? 504 * fail++ 505 * */ 506 if(http10==0) 507 /*http 0.9 进行关闭操作 ????*/ 508 if(shutdown(s,1)) 509 /* 1 终止 传送操作 */ 510 { failed++;close(s);continue;} 511 if(force==0) 512 /* 阻塞等待回应 513 */ 514 { 515 /* read all available data from socket */ 516 while(1) 517 { 518 if(timerexpired) break; 519 i=read(s,buf,1500); 520 //fprintf(stderr,"%d\n",i); 521 // fprintf(stderr,"%s",buf); 522 if(i<0) 523 { 524 failed++; 525 close(s); 526 goto nexttry; 527 } 528 else 529 if(i==0) break; 530 else 531 bytes+=i; 532 } 533 } 534 if(close(s)) {failed++;continue;} 535 speed++; 536 } 537 }
socket.c
1 /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 2 * 3 * This module has been modified by Radim Kolar for OS/2 emx 4 */ 5 6 /*********************************************************************** 7 module: socket.c 8 program: popclient 9 SCCS ID: @(#)socket.c 1.5 4/1/94 10 programmer: Virginia Tech Computing Center 11 compiler: DEC RISC C compiler (Ultrix 4.1) 12 environment: DEC Ultrix 4.3 13 description: UNIX sockets code. 14 ***********************************************************************/ 15 16 #include <sys/types.h> 17 #include <sys/socket.h> 18 #include <fcntl.h> 19 #include <netinet/in.h> 20 #include <arpa/inet.h> 21 #include <netdb.h> 22 #include <sys/time.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <stdarg.h> 28 29 int Socket(const char *host, int clientPort) 30 { 31 int sock; 32 unsigned long inaddr; 33 struct sockaddr_in ad; 34 struct hostent *hp; 35 36 // printf("%s\n",host); 37 memset(&ad, 0, sizeof(ad)); 38 ad.sin_family = AF_INET; 39 40 inaddr = inet_addr(host); 41 if (inaddr != INADDR_NONE) 42 /* 无符号长整型和负数比较 43 * 上面这句话有问题, iner_addr 返回一个FFFFFFFF,即-1为错误,在ip地址翻译上 44 * 255.255.255.255,这个也就是 为什么不建议使用 inet_addr的原因*/ 45 memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 46 else 47 { 48 // printf("test 4\n"); 49 hp = gethostbyname(host); 50 // printf("test 5\n"); 51 if (hp == NULL) 52 return -1; 53 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 54 } 55 ad.sin_port = htons(clientPort); 56 57 sock = socket(AF_INET, SOCK_STREAM, 0); 58 if (sock < 0) 59 return sock; 60 if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 61 return -1; 62 return sock; 63 }
**************************************************************************************************
最后放一些写个自己的东西
1. 我学到的字符串更加灵活的应用,尤其是几个以前没有见过的string.h库里的函数
2. 对http协议有了个初步的了解 : http简明解析
3. getopt函数的使用的了解
转载于:https://www.cnblogs.com/dilidingzhi/p/4298096.html
webbench 代码阅读相关推荐
- 代码阅读——十个C开源项目
代码阅读--十个C开源项目 1. Webbench Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性 ...
- ORB_SLAM2代码阅读(5)——Bundle Adjustment
ORB_SLAM2代码阅读(5)--Bundle Adjustment 1. 说明 2. Bundle Adjustment(BA)的物理意义 3. BA的数学表达 4. BA的求解方法 4.1 最速 ...
- ORB_SLAM2代码阅读(3)——LocalMapping线程
ORB_SLAM2代码阅读(3)--LocalMapping线程 1.说明 2.简介 3.处理关键帧 4. 地图点剔除 5. 创建新的地图点 6.相邻搜索 6.剔除冗余关键帧 1.说明 本文介绍ORB ...
- ORB_SLAM2代码阅读(4)——LoopClosing线程
ORB_SLAM2代码阅读(4)--LoopClosing线程 1.说明 2.简介 3.检测回环 4.计算Sim3 4.1 为什么在进行回环检测的时候需要计算相似变换矩阵,而不是等距变换? 4.2 累 ...
- ORB_SLAM2代码阅读(2)——tracking线程
ORB_SLAM2代码阅读(2)--Tracking线程 1. 说明 2. 简介 2.1 Tracking 流程 2.2 Tracking 线程的二三四 2.2.1 Tracking 线程的二种模式 ...
- ORB_SLAM2代码阅读(1)——系统入口
ORB_SLAM2代码阅读(1)--系统简介 1.说明 2.简介 3.stereo_kitti.cc 4.SLAM系统文件(System.cc) 4.1 构造函数System() 4.2 TrackS ...
- 深度学习项目代码阅读建议
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|机器学习实验室 犹豫很久要不要把读代码这个事情专门挑出来写 ...
- JavaScript权威Douglas Crockford:代码阅读和每个人都该学的编程
作者:Peter Seibel 关于JavaScript Seibel:在程序学习之路上有哪些令你后悔的事情? Crockford:我了解一些语言,但却一直没有机会使用.我花了不少时间学习APL并了解 ...
- MFC按钮CXPButton类,代码阅读起来还是挺不错的
在操手MFC的时候,经常会抱怨MFC界面不如其他的框架或语言,比如VB,C#等等,面对MS在系统上的不断更新换代,我们也越来越追求软件的视觉效果,譬如我们会更喜欢win7下的玻璃效果,看起来很炫. 在 ...
- 《代码阅读方法与实践之读书笔记之一》
<代码阅读方法与实践之读书笔记之一> 阅读代码是程序员的基本技能,同时也是软件开发.维护.演进.审查和重用过程中不可或缺的组成部分.<代码阅读方法与实践之读书笔记之一>这本书围 ...
最新文章
- DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子
- 【译文】怎样让一天有36个小时
- ggClusterNet---一条代码完成全内容微生物网络
- 2021-03-01 Matlab 多项式的根求解
- inodesusedpercent_Linux运维监控基础采集项
- 计算机一级电子表格怎么打开,xls文件怎么打开?其实很简单
- Java集合—PriorityQueue底层原理
- Stream流中的常用方法_count
- Python数据存储:pickle模块的使用讲解(测试代码)
- 小知识:vue中的name的作用
- Google 也要“勒紧腰带”过日子了!
- python产生随机数_python技能:random库的使用
- 大一计算机专业选修课,大学计算机类开什么公选课好?
- Linux_加密和安全详细介绍
- php识别名片,基于php的聚合数据名片识别api调用实例
- STM32的RS485通信
- 形式语言与自动机 第4章 正规文法和正规集的性质
- 如何快速学会三子棋游戏
- 网易云亮相GITC,聚合多样化通信与视频云平台
- proteus元件大全 仿真元件 电子元器件英文缩写
热门文章
- 数学--线性代数--奇异值分解(SVD)
- YOLO学习-2:win10(64位)+ python3.6 + TensorFlow-GPU + keras + yolov3测试实践(增加笔记本显卡GPU加速)
- java 下拉列表监听_javascript对下拉列表框(select)的操作
- 我为什么鼓励工程师写blog
- MFC获得主窗口和父窗口指针
- JC法在matlab,自贡自流井启闭机--四川闸门厂家产品中心
- 数组越界怎么判断_算法连载之求解两个有序数组的中位数
- SpringCloud实战与原理---快速入门
- rust-let 不可变绑定与可变绑定(4)
- go语言基础到提高(13)-同步