毛毛's profileWindows Live 共享空间PhotosBlogListsMore ![]() | Help |
Windows Live 共享空间 |
||||||
|
November 04 computer system a programmer's perspective 深入理解计算机系统 Randal E.Bryant--(一) 看过Randy Pausch的最后一课的话可能会注意到结尾的the other Randy,很幽默谦逊的一个老人,其实他是CMU计算机科学主任教授和计算机学院长,IEEE和ACM的fellow。computer system a programmer's perspective的作者。
http://www.cs.cmu.edu/~bryant/这是他的个人主页,有兴趣的话可以上去瞻仰一下牛人。关于这本书有一个专门的网页http://csapp.cs.cmu.edu/,可以在上面下载相关的资料。 自己花半个月时间粗粗的过了一遍这本书,受益匪浅,澄清了许多以前的困惑。而且这本书条理及其的清楚,解释的也很深入到位,一点都不晦涩。所以把一些看书中的体会纪录下来。个人强烈推荐想学计算机编程的都认真阅读这本书,感受一下大师的风采。 September 05 WinPcap编程(五)——杂碎(4)几个抓包函数pcap_loop(),pcap_dispatch(); pcap_next(),pcap_next_ex(). 当适配器被打开,捕获工作就可以用 pcap_dispatch() 或 pcap_loop()进行。 这两个函数非常的相似,区别就是 pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回,只有当 cnt 数据包被捕获,所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_loop()对于我们这个简单的范例来说,可以满足需求,不过, pcap_dispatch() 函数一般用于比较复杂的程序中。这两个函数都有一个 回调 参数, packet_handler指向一个可以接收数据包的函数。 这个函数会在收到每个新的数据包并收到一个通用状态时被libpcap所调用 ( 与函数 pcap_loop() 和 pcap_dispatch() 中的 user 参数相似),数据包的首部一般有一些诸如时间戳,数据包长度的信息,还有包含了协议首部的实际数据。 注意:冗余校验码CRC不再支持,因为帧到达适配器,并经过校验确认以后,适配器就会将CRC删除,与此同时,大部分适配器会直接丢弃CRC错误的数据包,所以,WinPcap没法捕获到它们。 int #ifdef HAVE_REMOTE for (;;) { int #ifdef HAVE_REMOTE return p->read_op(p, cnt, callback, user); 两个函数抓包的核心都在p->read_op(p, cnt, callback, user);不同的是pcap_loop是一个循环,直到read_op返回0值;而pcap_dispatch直接调用一次read_op就返回。那read_op是个什么函数,loop和dispatch形参列表的pcap_handler有啥用呢。 首先read_op是个函数指针,以前看pcap_t的定义时候也看到了,再啰嗦一次。 int (*read_op)(pcap_t *, int cnt, pcap_handler, u_char *); 实际loop和dispatch里面的read_op是指向什么函数呢?这个函数指针其实是在打开设备的时候赋值的,我没有找到pcap_open_dead和pcap_open_offline两个函数的具体赋值过程,不过pcap_open_live里写得很清楚。 p->read_op = pcap_read_nit; static int pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) cc = p->cc; /* nh = (struct nit_hdr *)bp; switch (nh->nh_state) { case NIT_CATCH: case NIT_NOMBUF: case NIT_SEQNO: default: caplen = nh->nh_wirelen; 大体上的过程是这样的, n = 0; if (p->break_loop) { if (bpf_filter(fcode, cp, nh->nh_wirelen, caplen)) 如果捕获的数据包符合设备定义的过滤器。开始正式的处理过程 (*callback)(user, &h, cp);呵呵我们在pcap_loop和pcap_dispatch定义的回调函数原来在这里。 if (++n >= cnt && cnt >= 0) { while循环结束后还有一个return语句,这个return可能返回0值并且只有这一处可能返回0值,当且仅当设备缓存当中没有符合条件的数据包。 所以pcap_loop()两种情况下可以返回:1是break_loop()并且当前没有处理到任何符合过滤规则的数据包(如果已经开始读取一个缓存则只有等这个缓存处理完毕才能退出);2是当前缓冲当中不存在符合过滤规则的数据包。
pcap_next()和pcap_next_ex() pcap_next()其实就是pcap_dispatch(),只不过它的回调函数是已经定义好的。pcap_next() 有一些不好的地方。首先,它效率低下,尽管它隐藏了回调的方式,但它依然依赖于函数 pcap_dispatch()。第二,它不能检测到文件末尾这个状态(EOF),因此,如果数据包是从文件读取来的,那么它就不那么有用了。 static void const u_char * s.hdr = h; pcap_next_ex(),其实它的回调函数和pcap_next是一样一样一样的啊~ static void *sp->hdr = *h; int s.hdr = &p->pcap_header; /* Saves a pointer to the packet headers */ #ifdef HAVE_REMOTE return pcap_read_nocb_remote(p, pkt_header, (u_char **) pkt_data); if (p->sf.rfile != NULL) { /* We are on an offline capture */ /* /* September 04 WinPcap编程(四)(1)pcap_t:一个已打开的捕捉实例的描述符。这个结构体对用户来说是不透明的,它通过wpcap.dll提供的函数,维护了它的内容。 这到底是个啥东西呢,看看吧 <pcap.h>定义pcap_t, typedef struct pcap pcap_t <pcap-int.h>定义pcap struct pcap { int break_loop; /* flag set to force break from packet-reading loop */ #ifdef PCAP_FDDIPAD #ifdef MSDOS struct pcap_sf sf; /* /* /* We're accepting only packets in this direction/these directions. */ /* /* char errbuf[PCAP_ERRBUF_SIZE + 1]; struct pcap_pkthdr pcap_header; /* This is needed for the pcap_next_ex() to work */ #ifdef HAVE_REMOTE SOCKET rmt_sockctrl; //!< socket ID of the socket used for the control connection 所以一般只用pcap_t的指针,因为头文件<pcap.h>里面没定义嘛。 (2)文档上的一个错误? typedef void(*) pcap_handler (u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) 实际pcap.h中的定义是 typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *); 可能函数指针能象前者那么定义?我反正是没见过。 (3)pcap_open() 新的设备打开函数,注意:仅在win32平台上可用,开发小组建议用这个函数来替代所有的pcap_open_xxx()函数,下面看看这个bt函数吧。 首先注意你include<pcap.h>不一定能调用成功哦,为啥呢?因为它不在pcap.h里声明啊,笨!又被文档骗了!实际上它是在头文件remote-ext.h里声明的,而函数的定义在pcap-new.c里面。这样的函数一共五个: pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf); 那为啥有时候include<pcap.h>能调用成功呢?看看pcap.h源代码吧: #ifdef HAVE_REMOTE 明白了?原来还得定义HAVE_REMOTE。否则老老实实写include<remote-ext.h>吧. WinPcap编程(二)3.工作原理 数据包抓取和过滤一般包括两部分,一是内核级的抓包过滤模块,二是用户级的数据传输模块。WinPcap的结构是基于BSD Capturing Componets。下图是两者的结构图。 可以看到两者抓取数据包和过滤数据包的方式基本是相同的,首先通过设备驱动程序获取数据包,其次通过过滤讲用户感兴趣的数据包拷贝到内核的缓存当中,最后用户程序通过两者提供的接口将这些数据读入到用户数据缓存当中做相应的处理。 那么两者的不同点在于: 1.BSD当中内核级抓包过滤模块BPF当中的内核缓存是包括两部分,一部分用来读取网络数据store,一部分向用户缓存输出hold;而WinPcap当中的相应部分NPF当中的内核缓存是一块环行的缓存。相比较而言NPF的结构内存利用率和效率相对更高一些。而且NPF当中的缓存是固定的32KB,而BPF的内核缓存是可变的,默认值是1MB。 2.统计信息监控的0-COPY机制,NPF支持内核级的数据包监控,数据包不需要拷贝到用户缓存当中,监控结果定期上报给用户应用程序,提高了效率。 4.实现 监控WinPcap包括三个模块:一是内核级模块NPF用来完成数据包过滤;二是低级的用户级模块packet.dll,packet.dll可以直接访问驱动的函数,并且依赖于微软操作系统的可编程接口,用来帮助用户级应用和低层内核进行通信;三是高级的用户级模块wpcap.dll,wpcap.dll不依赖于系统和网络硬件,它通过调用和封装packet.dll中的函数来提供一些应用开发所需的高级功能,如数据包过滤器的生成和用户级数据缓存等。一般来讲wpcap.dll更方便用户级应用程序的开发,当然直接使用packet.dll与内核进行功能交互也是可行的,但是一般不这样做。下图说明了三个模块间的关系。 非常重要的注意事项,请仔细阅读!Packet.dll 的源代码是开放的,并且有完整的文档。 然而,packet.dll应该被认为是核心API,因为它建立在WinPcap内部的目的,就是为真正的公共API:wpcap.dll建立一层"隔离墙"。 由于应用程序使用WinPcap的常规(normal)方式和推荐(suggested)方式,是通过wpcap.dll,所以,我们不保证packet.dll的API不会在未来的WinPcap的发行版中被修改,并且,我们不提供这个API的支持。 [1]Fulvio Risso, Loris Degioanni, An Architecture for High Performance Network Analysis, Proceedings of the 6th IEEE Symposium on Computers and Communications (ISCC 2001), Hammamet, Tunisia, July 2001 |
|||||
|
|