环形缓冲区为什么是lock-free的?
回答这个问题之前,首先解释一下,什么叫lock-free?lock-free就是绝对无锁。那么为什么在环形缓冲区中不需要上锁呢?下面我将从三个方面来解释这个问题,首先先解释什么是环形缓冲区,然后再来了解lock-free的三个必要前提,最后才解释在环形缓冲区绝对无锁的原因。
环形缓冲区的本质所谓的环形缓冲区,其实就是使用一个环形的缓冲区域作为数据循环结构。可以插入数据、删除数据,通过改变了队列中的首尾来达到空间的分配和释放。
lock-free的三个前提这其中有三个前提,一是cpu支持内存栅栏,二是数据的地址必须是四对齐的,三是必须是一个生产者对应一个消费者。这三个必要前提缺一不可,否则就没有办法实现lock-free了。
lock-free存在的原因所谓的环形缓冲区实际上就相当于一个队列,在这个队列中,只有一个生产者和一个消费者,但是不管是生产者还是消费者,都不需要完全独自霸占整个队列,他们都只是移动首尾,也就是数据的输入和删除,变化的只是环形缓冲区中空间的位置分配。所以不存在满的情况,也不需要对整个数据结构进行加锁。
以上就是我对这个问题的简单解释。因个人经验有限,所以其中有问题的地方,也欢迎大家指出。
Windows网络与通信程序设计(第2版) 王艳平 这本书写的非常好,我有本王艳平写的 windows程序设计,写得很好,我自己不做网络开发,就没有买网络的那本书!不过推荐你看看,真的很不错!
本书将编程方法、网络协议和应用实例有机结合起来,详细阐明Windows网络编程的各方面内容。本书首先介绍Windows平台上进行网络编程的基础知识,包括网络硬件、术语、协议、Winsock编程接口和各种I/O方法等;然后通过具体实例详细讲述当前流行的高性能可伸缩服务器设计、IP多播和Internet广播、P2P程序设计、原始套接字、SPI、协议驱动的开发和原始以太数据的发送、ARP欺骗技术、LAN和WAN上的扫描和侦测技术、个人防火墙与网络封包截获技术等;最后讲述IP帮助函数和E-mail的开发方法。 本书结构紧凑,内容由浅入
第1章 计算机网络基础 1
11 网络的概念和网络的组成 1
12 计算机网络参考模型 2
121 协议层次 2
122 TCP/IP参考模型 2
123 应用层(Application Layer) 3
124 传输层(Transport Layer) 3
125 网络层(Network Layer) 3
126 链路层(Link Layer) 4
127 物理层(Physical Layer) 4
13 网络程序寻址方式 4
131 MAC地址 4
132 IP地址 5
133 子网寻址 6
134 端口号 8
135 网络地址转换(NAT) 8
14 网络应用程序设计基础 10
141 网络程序体系结构 10
142 网络程序通信实体 11
143 网络程序开发环境 12
第2章 Winsock编程接口 13
21 Winsock库 13
211 Winsock库的装入和释放 13
212 封装CInitSock类 14
22 Winsock的寻址方式和字节顺序 14
221 Winsock寻址 14
222 字节顺序 16
223 获取地址信息 17
23 Winsock编程详解 20
231 Winsock编程流程 20
232 典型过程图 23
233 TCP服务器和客户端程序举例 24
234 UDP编程 26
24 网络对时程序实例 28
241 时间协议(Time Protocol) 28
242 TCP/IP实现代码 29
第3章 Windows套接字I/O模型 31
31 套接字模式 31
311 阻塞模式 31
312 非阻塞模式 31
32 选择(select)模型 32
321 select函数 32
322 应用举例 33
33 WSAAsyncSelect模型 36
331 消息通知和WSAAsyncSelect函数 36
332 应用举例 37
34 WSAEventSelect模型 40
341 WSAEventSelect函数 40
342 应用举例 42
343 基于WSAEventSelect模型的服务器设计 44
35 重叠(Overlapped)I/O模型 53
351 重叠I/O函数 53
352 事件通知方式 56
353 基于重叠I/O模型的服务器设计 56
第4章 IOCP与可伸缩网络程序 67
41 完成端口I/O模型 67
411 什么是完成端口(completion port)对象 67
412 使用IOCP的方法 67
413 示例程序 69
414 恰当地关闭IOCP 72
42 Microsoft扩展函数 72
421 GetAcceptExSockaddrs函数 73
422 TransmitFile函数 73
423 TransmitPackets函数 74
424 ConnectEx函数 75
425 DisconnectEx函数 76
43 可伸缩服务器设计注意事项 76
431 内存资源管理 76
432 接受连接的方法 77
433 恶意客户连接问题 77
434 包重新排序问题 78
44 可伸缩服务器系统设计实例 78
441 CIOCPServer类的总体结构 78
442 数据结构定义和内存池方案 82
443 自定义帮助函数 85
444 开启服务和停止服务 88
445 I/O处理线程 93
446 用户接口和测试程序 99
第5章 互联网广播和IP多播 100
51 套接字选项和I/O控制命令 100
511 套接字选项 100
512 I/O控制命令 102
52 广播通信 103
53 IP多播(Multicasting) 105
531 多播地址 105
532 组管理协议(IGMP) 105
533 使用IP多播 106
54 基于IP多播的组讨论会实例 110
541 定义组讨论会协议 110
542 线程通信机制 111
543 封装CGroupTalk类 111
544 程序界面 117
第6章 原始套接字 121
61 使用原始套接字 121
62 ICMP编程 121
621 ICMP与校验和的计算 121
622 Ping程序实例 124
623 路由跟踪 126
63 使用IP头包含选项 129
631 IP数据报格式 129
632 UDP数据报格式 131
633 原始UDP封包发送实例 133
64 网络嗅探器开发实例 134
641 嗅探器设计原理 135
642 网络嗅探器的具体实现 136
643 侦听局域网内的密码 138
65 TCP通信开发实例 140
651 创建一个原始套接字,并设置IP头选项 140
652 构造IP头和TCP头 140
653 发送原始套接字数据报 142
654 接收数据 146
第7章 Winsock服务提供者接口(SPI) 147
71 SPI概述 147
72 Winsock协议目录 148
721 协议特性 149
722 使用Winsock API函数枚举协议 150
723 使用Winsock SPI函数枚举协议 151
73 分层服务提供者(LSP) 153
731 运行原理 153
732 安装LSP 154
733 移除LSP 158
734 编写LSP 159
735 LSP实例 161
74 基于SPI的数据报过滤实例 165
75 基于Winsock的网络聊天室开发 171
751 服务端 171
752 客户端 171
753 聊天室程序的设计说明 172
754 核心代码分析 172
第8章 Windows网络驱动接口标准(NDIS)和协议驱动的开发 176
81 核心层网络驱动 176
811 Windows 2000及其后产品的网络体系结构 176
812 NDIS网络驱动程序 177
813 网络驱动开发环境 178
82 WDM驱动开发基础 181
821 UNICODE字符串 181
822 设备对象 181
823 驱动程序的基本结构 183
824 I/O请求包(I/O request packet,IRP)和I/O堆栈 183
825 完整驱动程序示例 186
826 扩展派遣接口 188
827 应用举例(进程诊测实例) 191
83 开发NDIS网络驱动预备知识 198
831 中断请求级别(Interrupt Request Level,IRQL) 198
832 旋转锁(Spin Lock) 198
833 双链表 199
834 封包结构 199
84 NDIS协议驱动 200
841 注册协议驱动 200
842 打开下层协议驱动的适配器 201
843 协议驱动的封包管理 202
844 在协议驱动中接收数据 203
845 从协议驱动发送封包 204
85 NDIS协议驱动开发实例 204
851 总体设计 204
852 NDIS协议驱动的初始化、注册和卸载 206
853 下层NIC的绑定和解除绑定 209
854 发送数据 217
855 接收数据 219
856 用户IOCTL处理 225
第9章 网络扫描与检测技术 233
91 网络扫描基础知识 233
911 以太网数据帧 233
912 ARP 234
913 ARP格式 236
914 SendARP函数 237
92 原始以太封包的发送 238
921 安装协议驱动 238
922 协议驱动用户接口 238
923 发送以太封包的测试程序 244
93 局域网计算机扫描 245
931 管理原始ARP封包 246
932 ARP扫描示例 249
94 互联网计算机扫描 253
941 端口扫描原理 253
942 半开端口扫描实现 254
95 ARP欺骗原理与实现 259
951 IP欺骗的用途和实现原理 259
952 IP地址冲突 260
953 ARP欺骗示例 261
第10章 点对点(P2P)网络通信技术 264
101 P2P穿越概述 264
102 一般概念 265
1021 NAT术语 265
1022 中转 265
1023 反向连接 266
103 UDP打洞 267
1031 中心服务器 267
1032 建立点对点会话 267
1033 公共NAT后面的节点 267
1034 不同NAT后面的节点 268
1035 多级NAT后面的节点 269
1036 UDP空闲超时 270
104 TCP打洞 271
1041 套接字和TCP端口重用 271
1042 打开点对点的TCP流 271
1043 应用程序看到的行为 272
1044 同步TCP打开 273
105 Internet点对点通信实例 273
1051 总体设计 273
1052 定义P2P通信协议 274
1053 客户方程序 275
1054 服务器方程序 287
1055 测试程序 291
第11章 核心层网络封包截获技术 294
111 Windows网络数据和封包过滤概述 294
1111 Windows网络系统体系结构图 294
1112 用户模式下的网络数据过滤 295
1113 内核模式下的网络数据过滤 296
112 中间层网络驱动PassThru 296
1121 PassThru NDIS中间层驱动简介 296
1122 编译和安装PassThru驱动 297
113 扩展PassThru NDIS IM驱动——添加IOCTL接口 297
1131 扩展之后的PassThru驱动(PassThruEx)概况 297
1132 添加基本的DeviceIoControl接口 298
1133 添加绑定枚举功能 302
1134 添加ADAPT结构的引用计数 307
1135 适配器句柄的打开/关闭函数 308
1136 句柄事件通知 315
1137 查询和设置适配器的OID信息 315
114 扩展PassThru NDIS IM驱动——添加过滤规则 323
1141 需要考虑的事项 323
1142 过滤相关的数据结构 324
1143 过滤列表 326
1144 网络活动状态 327
1145 IOCTL控制代码 328
1146 过滤数据 331
115 核心层过滤实例 339
第12章 Windows网络防火墙开发技术 342
121 防火墙技术概述 342
122 金羽(Phoenix)个人防火墙浅析 343
1221 金羽(Phoenix)个人防火墙简介 343
1222 金羽(Phoenix)个人防火墙总体设计 344
1223 金羽(Phoenix)个人防火墙总体结构 345
123 开发前的准备 345
1231 常量的定义 346
1232 访问规则 348
1233 会话结构 348
1234 文件结构 349
1235 UNICODE支持 355
124 应用层DLL模块 356
1241 DLL工程框架 356
1242 共享数据和IO控制 362
1243 访问控制列表ACL(Access List) 364
1244 查找应用程序访问权限的过程 367
1245 类的接口——检查函数 370
125 核心层SYS模块 373
126 主模块工程 375
1261 I/O控制类 375
1262 主应用程序类 377
1263 主对话框中的属性页 380
1264 主窗口类 381
127 防火墙页面 383
1271 网络访问监视页面 383
1272 应用层过滤规则页面 387
1273 核心层过滤规则页面 397
1274 系统设置页面 403
第13章 IP帮助函数 406
131 IP配置信息 406
1311 获取网络配置信息 406
1312 管理网络接口 408
1313 管理IP地址 412
132 获取网络状态信息 415
1321 获取TCP连接表 415
1322 获取UDP监听表 418
1323 获取IP统计数据 420
133 路由管理 427
1331 获取路由表 427
1332 管理特定路由 431
1333 修改默认网关的例子 432
134 ARP表管理 433
1341 获取ARP表 433
1342 添加ARP入口 434
1343 删除ARP入口 434
1344 打印ARP表的例子 434
135 进程网络活动监视实例 438
1351 获取通信的进程终端 438
1352 Netstate源程序代码 439
第14章 Email协议及其编程 444
141 概述 444
142 电子邮件介绍 445
1421 电子邮件Internet的地址 445
1422 Internet邮件系统 445
1423 电子邮件信头的结构及分析 446
143 SMTP原理 448
1431 SMTP原理分析 448
1432 SMTP工作机制 449
1433 SMTP命令码和工作原理 449
1434 SMTP通信模型 450
1435 SMTP的命令和应答 451
144 POP3协议原理 452
1441 POP3协议简介 452
1442 POP3工作原理 453
1443 POP3命令原始码 454
1444 POP3会话实例 459
145 实例分析与程序设计 460
1451 总界面设计 460
1452 SMTP客户端设计 461
1453 POP3客户端设计 473
对这一行不熟悉不敢乱判断。猜测一下可能的原因:
如果用windows当服务器,IOCP很成熟所以选择C++
C#本身带有内存回收机制,对于某些类型的服务器需要自己管理内存回收
技术上没问题,听说过用C#当网页游戏服务器的成功案例
用C#的成本在这一行不算低(综合服务器,开发效率,招人难度等)
现成有许多成熟的公司框架不需要自己重新写,大家跳跳槽也都有了……
(一)首先,介绍几种常见的I/O模型及其区别,如下:
blocking I/O
nonblocking I/O
I/O multiplexing (select and poll)
signal driven I/O (SIGIO)
asynchronous I/O (the POSIX aio_functions)—————异步IO模型最大的特点是 完成后发回通知。
阻塞与否,取决于实现IO交换的方式。
异步阻塞是基于select,select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄
异步非阻塞直接在完成后通知,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
1 blocking I/O
这个不用多解释吧,阻塞套接字。下图是它调用过程的图示:
重点解释下上图,下面例子都会讲到。首先application调用 recvfrom()转入kernel,注意kernel有2个过程,wait for data和copy data from kernel to user。直到最后copy complete后,recvfrom()才返回。此过程一直是阻塞的。
2 nonblocking I/O:
与blocking I/O对立的,非阻塞套接字,调用过程图如下:
可以看见,如果直接操作它,那就是个轮询。。直到内核缓冲区有数据。
3 I/O multiplexing (select and poll)
最常见的I/O复用模型,select。
select先阻塞,有活动套接字才返回。与blocking I/O相比,select会有两次系统调用,但是select能处理多个套接字。
4 signal driven I/O (SIGIO)
只有UNIX系统支持,感兴趣的课查阅相关资料
与I/O multiplexing (select and poll)相比,它的优势是,免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。
5 asynchronous I/O (the POSIX aio_functions)
很少有nix系统支持,windows的IOCP则是此模型
完全异步的I/O复用机制,因为纵观上面其它四种模型,至少都会在由kernel copy data to appliction时阻塞。而该模型是当copy完成后才通知application,可见是纯异步的。好像只有windows的完成端口是这个模型,效率也很出色。
6 下面是以上五种模型的比较
可以看出,越往后,阻塞越少,理论上效率也是最优。
=====================分割线==================================
5种模型的比较比较清晰了,剩下的就是把select,epoll,iocp,kqueue按号入座那就OK了。
select和iocp分别对应第3种与第5种模型,那么epoll与kqueue呢?其实也于select属于同一种模型,只是更高级一些,可以看作有了第4种模型的某些特性,如callback机制。
为什么epoll,kqueue比select高级?
答案是,他们无轮询。因为他们用callback取代了。想想看,当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。
windows or nix (IOCP or kqueue/epoll)?
诚然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX下。而且正如上面所述,kqueue/epoll 与 IOCP相比,就是多了一层从内核copy数据到应用层的阻塞,从而不能算作asynchronous I/O类。但是,这层小小的阻塞无足轻重,kqueue与epoll已经做得很优秀了。
提供一致的接口,IO Design Patterns
实际上,不管是哪种模型,都可以抽象一层出来,提供一致的接口,广为人知的有ACE,Libevent(基于reactor模式)这些,他们都是跨平台的,而且他们自动选择最优的I/O复用机制,用户只需调用接口即可。说到这里又得说说2个设计模式,Reactor and Proactor。见:Reactor模式--VS--Proactor模式。Libevent是Reactor模型,ACE提供Proactor模型。实际都是对各种I/O复用机制的封装。
Java nio包是什么I/O机制?
现在可以确定,目前的java本质是select()模型,可以检查/jre/bin/niodll得知。至于java服务器为什么效率还不错。。我也不得而知,可能是设计得比较好吧。。-_-。
=====================分割线==================================
总结一些重点:
只有IOCP是asynchronous I/O,其他机制或多或少都会有一点阻塞。
select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
epoll, kqueue、select是Reacor模式,IOCP是Proactor模式。
java nio包是select模型。。
(二)epoll 与select的区别
1 使用多进程或者多线程,但是这种方法会造成程序的复杂,而且对与进程与线程的创建维护也需要很多的开销。(Apache服务器是用的子进程的方式,优点可以隔离用户) (同步阻塞IO)
2一种较好的方式为I/O多路转接(I/O multiplexing)(貌似也翻译多路复用),先构造一张有关描述符的列表(epoll中为队列),然后调用一个函数,直到这些描述符中的一个准备好时才返回,返回时告诉进程哪些I/O就绪。select和epoll这两个机制都是多路I/O机制的解决方案,select为POSIX标准中的,而epoll为Linux所特有的。
区别(epoll相对select优点)主要有三:
1select的句柄数目受限,在linux/posix_typesh头文件有这样的声明:#define __FD_SETSIZE 1024 表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。
2epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll是维护一个队列,直接看队列是不是空就可以了。epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。但是如果绝大部分的I/O都是“活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。
3使用mmap加速内核与用户空间的消息传递。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
关于epoll工作模式ET,LT
epoll有两种工作方式
ET:Edge Triggered,边缘触发。仅当状态发生变化时才会通知,epoll_wait返回。换句话,就是对于一个事件,只通知一次。且只支持非阻塞的socket。
LT:Level Triggered,电平触发(默认工作方式)。类似select/poll,只要还有没有处理的事件就会一直通知,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll支持阻塞和不阻塞的socket。
三 Linux并发网络编程模型
1 Apache 模型,简称 PPC ( Process Per Connection ,):为每个连接分配一个进程。主机分配给每个连接的时间和空间上代价较大,并且随着连接的增多,大量进程间切换开销也增长了。很难应对大量的客户并发连接。
2 TPC 模型( Thread Per Connection ):每个连接一个线程。和PCC类似。
3 select 模型:I/O多路复用技术。
1 每个连接对应一个描述。select模型受限于 FD_SETSIZE即进程最大打开的描述符数linux2635为1024,实际上linux每个进程所能打开描数字的个数仅受限于内存大小,然而在设计select的系统调用时,却是参考FD_SETSIZE的值。可通过重新编译内核更改此值,但不能根治此问题,对于百万级的用户连接请求 即便增加相应 进程数, 仍显得杯水车薪呀。
2select每次都会扫描一个文件描述符的集合,这个集合的大小是作为select第一个参数传入的值。但是每个进程所能打开文件描述符若是增加了 ,扫描的效率也将减小。
3内核到用户空间,采用内存复制传递文件描述上发生的信息。
4 poll 模型:I/O多路复用技术。poll模型将不会受限于FD_SETSIZE,因为内核所扫描的文件 描述符集合的大小是由用户指定的,即poll的第二个参数。但仍有扫描效率和内存拷贝问题。
5 pselect模型:I/O多路复用技术。同select。
6 epoll模型:
1)无文件描述字大小限制仅与内存大小相关
2)epoll返回时已经明确的知道哪个socket fd发生了什么事件,不用像select那样再一个个比对。
3)内核到用户空间采用共享内存方式,传递消息。
四 :FAQ
1、单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。 所以你有还是必要建立线程池来发挥更大的效能。
2、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。
3、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。
4、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。
5、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。
6、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。
Nginx 结合FastCGI 程序可以搭建高性能web service程序。(a)Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。(b)当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接收到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端。这就是Nginx+FastCGI的整个运作过程,如图1所示。FastCGI接口方式在脚本解析服务器(CGI应用程序服务器)上启动一个或者多个守护进程对动态脚本进行解析,这些进程就是FastCGI进程管理器,或者称为FastCGI引擎。 spawn-fcgi与PHP-FPM都是FastCGI进程管理器(支持PHP和C/C++)。
0条评论