C++线程安全分析 除了通过code review或者test来检测线程安全,有没有其它更好的办法? 答案是存在的,通过clang即可以进行这种检测,编译器对存在的线程安全问题会给出警告。 这种分析方法由Google开发,已经被Google大规模应用,参考 https://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/42958.pdf 示例example.cpp: 通过然后指定-Wthread-safety编译选项,即clang -c -Wthread-safety example.cpp命令编译即可以给出警告。 更多细节可以参考: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
Author: orient
Supporting IPv6 DNS64/NAT64 Networks
从6月1日起,苹果审核要求支持IPv6 DNS64/NAT64网络了。 我们服务器和客户端都是Socket编程,IPv4地址。最后参照https://developer.apple.com/library/mac/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW1,在客户端处理下即可。 需要说明的是, 在上述文档Apple给出的Listing 10-1 Using getaddrinfo to resolve an IPv4 address literal中,error = getaddrinfo(ipv4_str, “http”, &hints, &res0); 经测试,在IOS下,第一个参数为IPv4地址的情况下,第二个参数为http,ftp等常用字符串,服务器此时监听端口需改为协议默认端口才可能;或者第一个参数为域名,第二个参数为数字端口; 不支持第一个参数为IPv4地址,第二个参数为数字端口。坑爹。有两种方法解决这个问题 : 1) 域名+数字端口。 2) 先getaddrinfo, 再获取sockaddr_in或者sockaddr_in6,设置sin_port或sin6_port。 关于IPv6,可以参考下几篇文章: https://tools.ietf.org/html/rfc4038 https://github.com/WeMobileDev/article/blob/master/IPv6%20socket编程.md
深入了解死锁
死锁通常是因为获取锁时形成了环路。 死锁发生前 尽量不要使用共享 例如使用消息通讯,例如Erlang里的变量不变 尽量使用并发组件 例如java.util.concurrent 尽量使用非递归锁(不可重入锁) 例如pthread_mutex_t默认非递归 控制顺序 thread 1: Lock(a), Lock(b) thread 2: Lock(b), Lock(a) 转变为统一的顺序如Lock(a), Lock(b) 运行期警告 把Lock(历史)形成一个graph,看是否存在回路 超时机制 如带有timeout的wait, innodb_lock_wait_timeout等 工具测试 例如Intel inspector, Valgrind-Helgrind(pthread)等 PS. 银行家算法 由Edsger Dijkstra爷爷为设计THE操作系统创建,其使用场景也有些局限性,比如需要知道进程请求资源的可能数量,以及假设进程数量是固定的等,因此基本不具有实用性。 死锁发生后 堆栈查看 pstack(其实就是gdb thread apply all bt) jstack, jconsole ps. CPU100%也可以通过这种方法 检测机制 例如innodb的wait-for graph,形成环路(或者层次太深时也会认为死锁)时,此时会选择Undo量小的事务进行回滚。
定时器(timer)与时间轮(timewheel)
1 定时器(timer) 1.1 定时器使用场景 通常,定时是系统一个比较常用的事件。定时器要解决的问题是,隔一定时间(short-one)或者每隔一段时间(repeated)触发一个定时事件。 无论是系统还是应用,都会用到定时功能,例如, 系统层面: 屏幕刷新,TCP等 应用层面: 网络库实现的定时功能比如libuv 1.2 应用层定时器 应用层的定时通常有几种方式: 可以在某个循环(比如Windows GetMessage, DispatchMessage后面)判断时间,通常循环里可能会sleep一下,或者有类似poll,wait之类的超时设置; 借助操作系统提供的timer函数(POSIX timer函数或者windows timer消息),或者系统提供的定时任务如crond; 利用poll相关函数,借助timerfd等(linux); 数据结构方面,可以使用链表或者堆。 1.3 系统层定时器 系统定时器有低分辨率定时器,也有高分辨率定时器。 低分辨率定时器一般为毫秒级,典型分辨率为4毫秒。通常采用timer wheel实现。 高分辨率定时器可以达到纳秒级。通常采用红黑树实现。 底层使用时钟硬件(如HPET)提供的时钟中断来提供tick。 2 时间轮(timewheel) timewheel思想十分简单。想象我们的时钟,分辨率为秒,能实现从1秒到43200(12*60*60)秒的定时。然而定时轮并不需要12*60*60大小的数组,只需要用到12+60+60=132个大小的数组。 3 参考 《Professional Linux Kernel Architechure》
基于java nio的server设计
最近由于业务需要,写了一个 java server用来与我们的游戏服务器实现交互。 server只实现了最基本的功能: 维护多个长连接,发送与接收消息;连接建立及断开(主动与被动)。 在实现的过程中,最有意思的是java nio里的buffer,但我认为这是一个失败的设计。 java nio buffer三个主要的属性,position, limit, capacity. 通常来讲,传统的ringbuffer都会有一个read index和write index, 但buffer nio将两者统一为postion,然后弄出了读写模式切换的概念。 当从buffer里取出数据,即buffer.getXXX或者channel.write(buffer), 当往buffer里放入数据,即buffer.putXX或者channel.read(buffer), 的前后,经常要考虑模式的切换,比如调用flip,这样的设计增加了复杂性,对库的使用者带来困难(笔者才疏学浅,一直疑惑他们为什么这么设计,求高人指教),这本质上是因为position属性既要当作readindex来使用,又要当作writeindex来使用。 另外一点就是由于postion的大小不能超过limit和capacity, 导致使用的过程中经常需要compact, 每次compact会导致额外的内存拷贝;传统的ringbuffer会在writeindex < readindex在的时候才可能涉及到额外内存的拷贝。 也难怪,netty里抛弃了这种设计。