月度归档:2014年01月

关于sigpipe

1. sigpipe产生的原因
简单讲来就是向连接已经断开的socket发送数据。更具体点就是:当A端close一个连接时,若B端接着发数据。根据TCP协议的规定,B端会收到一个RST响应,进程对接收了RST的socket发送数据时,内核会发出一个SIGPIPE信号给B端进程。

2. 工作中为啥没有遇到
虽然这问题很普遍,但是后台开发的工作中确很少遇到。仔细想了下,大致有3点原因:
1) 我们后台不是直接暴露给外部用户,通常是给内部系统调用(cgi或者其他后端),因此他们使用套接字收发消息都是很规范的。
2) 框架使用了zmq这样网络传输组件,屏蔽了tcp链接的这些细节。
3) server大多是req – rep模式,因此没收到包不会发包。

3. 解决办法
前人已经做了很多工作,这里汇总下。
1) 忽略SIGPIPE(很多os都支持),mongrel2用这种方式
2) 设置socket选项 SO_NOSIGPIPE(mac支持,linux不支持)
3) api中带上标志,send/sendto的flags加上MSG_NOSIGNAL(linux支持)

tcpcopy-0.9.6静态编译

1. 背景

受限于公司机器环境,ip_queue特性需要重编内核才能支持,这个不是我自己的机器,简直不可预期,故只能走nfqueue来支持。同时为了保证不污染环境,所有安装不能侵入系统库,所有安装不能修改非自己用户的数据。

2. 解决过程
为此初略有3种方案:
1) tcpcopy依赖动态库 – 为了编译通过
2) tcpcopy默认从当前目录找动态库 – 为了运行更方便
3) tcpcopy变成链静态库 – 为了完美解决工具类可执行文件的形态
1)可以很快搞定,2)需要链接时加上参数-Wl,-rpath,需要改编译配置,3)需要修改编译配置文件,由于预估2)3)的工作量差不多,就直接去研究3)了。

当将所直接依赖的libnetfilter_queue,间接依赖的libnfnetlink,libmnl都编译成静态库时,在tcpcopy的configure阶段就会出现”checking for nfqueue directory… configure: error: nfqueue required. Install libnetfilter_queue(download it from http://www.netfilter.org/projects/libnetfilter_queue/index.html) or specify its path using –with-nfqueue=/dir/”。于是去看了下configure.ac文件,进入enable_nfqueue这一段逻辑里面,发现AC_TRY_LINK这里会试着去编译一小段代码检查下nfqueue库是否可用,估计是这里出了问题,强制加上nfqueue_linked=yes。然后autoconf,编译则通过。后面细致的想了下出现这种情况的原因,应该是链接动态库libnetfilter_queue,里面已经包含了libmnl和libnfnetlink的动态库位置,而链接静态库时,就必须单独指定这两个库,否则就会失败。

3.附编译脚本

 

关于程序入口

今天研讨会涉及到了程序入口的处理,之前在libtask源码中看过这么一个案例,于是顺便做了点测试。

1. 程序默认入口
默认的真实入口是_start,顺序是__start -> __libc_start_main -> main
入口符号__start在/usr/lib/crt1.o中,__libc_start_main在libc的动态库中。

2. 自定义程序入口
a) 写一个程序来做测试

b) 编译执行
[gcc|g++] -nostartfiles -e mymain[1|2] forge_main.[c|cpp]

c) 结果分析
以mymain1为入口,return只是表示当前函数调用结束,并没有做进程退出的清理工作,所以core了,而正常的main入口则是会交由libc做清理工作。mymain2由于显示的调用了exit,会主动去完成进程退出的相关工作,因此正常结束了。另外,g++会对符号加扰,所以加上extern “C”声明,没有这个编译时会出警告且入口永远都为源文件中第一个函数(符号)。

3. 隐藏默认入口,暴露自定义入口
既然系统上的所有程序都实际上都是伪入口,那么对于应用程序就更加可以自由处理入口了。这里以libtask为例子。

main -> taskmainstart -> taskmain
libtask就是将默认入口main封装到了框架内部(动态库,静态库都可),这样既可以完成不方便暴露或者繁琐的初始化工作,又可以简化入口,同时又向应用程序暴露了taskmain这一接口。