在Solaris上编译redis(2.8.13)

最近由于工作需要,又要做redis相关的工作。正好今天早上redis发布了最新的2.8.13版本,于是就下载了最新的版本并试着在Solaris上编译了一下,还是发现了一些问题。于是在此记录下来,说不定对其他人会有帮助。

(1)Solaris 9

我先选了一台Solaris 9,结果一开始编译就遇到“IPV6_V6ONLY”未定义错误。上网查了一下,发现Solaris不支持这个socket选项。我改了一点代码,绕过去这个问题,结果再编译遇到了没有stdint.h文件这个错误。于是果断决定放弃Solaris 9,转投Solaris 10。

(2)Solaris 10

Solaris 10上的编译过程还是比较顺利的,但是链接时出了两个问题:

a)nanosleep函数没有定义:Solaris上调用nanosleep,需要链接“rt library”,即在Makefile里加上“-lrt”。我把这个patch报给了redis(https://github.com/NanXiao/redis/commit/232d77938add77d95c91a714462389e62e2bf126),不过不确定redis会不会采纳。

b)isinf没有定义,需要在调用make命令时传入MISSING_ISINF宏定义,即“make “CFLAGS+=-DMISSING_ISINF” ”。

Solaris搭建64位C语言开发环境

刚来公司时,公司的C程序还是32位的。后来我阅读了一些资料,觉得64位的程序才是真正的趋势,所以就开始尝试着开发64位的程序。这篇文章介绍如何在Solaris下搭建64位C语言开发环境,希望给需要的朋友一点帮助。

(1)gcc

Solaris的/usr/sfw/bin/gcc可以用来编译64位C程序,但是需要加-m64编译选项,否则默认编译出来的是32位程序。此外也可以从gcc的官网下载gcc源代码,自行编译安装,但是要注意编译出来的gcc需要是64位的。

(2)gdb

调试64位C程序需要64位的gdb,gdb的安装步骤如下(以7.6版本为例):

1)gunzip gdb-7.6.tar.gz
2)tar xvf gdb-7.6.tar
3)export CC=”/usr/sfw/bin/gcc -m64″
4)mkdir build_gdb
5)cd build_gdb
6)../gdb-7.6/configure –prefix=“/…/…(a folder path)”
7)make
8)make install

需要注意以下两点:

a)Solaris下的tar程序不支持”-z”选项,所以只能先调用gunzip,再调用tar,不能一步搞定:tar -xzf gdb-7.6.tar.gz。

b)目前gdb的最新版本是7.7,在Solaris下编译会有错误。解决办法也很简单,可以参考这篇文章

(3)参考资料

个人认为Oracle的这本《Solaris 64-bit Developer’s Guide》,是在Solaris下开发64位C程序最好的资料。每一位C语言开发者都应该看一下,相信都能受益匪浅。

一个变量越界导致破坏堆栈的bug

前一段时间在商用系统上出现了core dump,原因是由于一个局部变量写越界导致堆栈被破坏了。在这里,我把这个bug分享一下,希望给需要的朋友一点帮助。简化的代码如下:

typedef struct
{
    ......
} A;

void func1(char *p)
{
    ......
}

void main(void)
{
    A a;
    A *p = &a;
    char b[10];

    ......
    /* 
     * 这个程序运行在SPARC处理器上, 而SPARC处理器的堆栈生长
     * 方向是从高地址向低地址生长的.
     * 这个函数里会把b这个数组写越界,导致p这个变量被破坏, 
     * 结果在下面访问p时,是一个无效的值,导致core dump.
     */
    func1(b);
    printf("%p\n", p);
    ......

    return;
}

通过分析这个bug,分享一下我的心得体会:
(1)SPARC处理器是当年SUN公司开发的一款RISC处理器,如果有朋友从事Solaris软件开发,应该还是会接触到的。我一直觉得作为底层软件工程师,还是应该对处理器结构(包括汇编语言)有尽可能多的了解,这样对我们分析和解决问题都会有很大帮助。比方说,在分析这个bug时,如果知道SPARC处理器的堆栈生长方向是从高地址向低地址生长的,就会让我们把注意力集中在变量定义在p后面的变量上,这样就有了分析问题的方向。在这里,我推荐一篇介绍SPARC处理器的文章(http://web.cecs.pdx.edu/~apt/cs577_2008/sparc_s.pdf),有兴趣的朋友可以看一下。
(2)当遇到堆栈上的变量被莫名其妙改变时,首先还是检查那些地址比被破坏变量地址低的变量。因为一般有可能引起内存越界的代码,像memcpy,都会破坏高地址的值。以这个bug为例。变量p在一开始就赋了值,以后对p都是读操作,没有写操作。现在p变成了一个无效的值,那么首先就应该怀疑访问定义在p后面的变量出了问题。

Solaris操作系统网络编程经验分享

自从2010年我开始在Solaris操作系统进行应用程序开发算起,到现在已经超过3年的时间了。在这里我想把在Solaris操作系统上做网络编程开发的一些特有的经验分享出来,希望给别人有所帮助。

经验1:链接动态库选项:-lresolv -lnsl -lsocket。
也许在其它的UNIX系统上,一个”-lsocket”链接选项就能解决所有问题。但是在Solaris系统上,要链接这三个库。

经验2:不支持SO_SNDTIMEO和SO_RCVTIMEO socket选项。
在我用过的Solaris 9和Solaris 10上,这两个socket选项都不支持(我没用过Open Solaris,所以不确定Open Solaris是否支持)。所以尽管这两个宏定义在sys/socket.h,但是却是不起作用的。如果在程序中用到这两个选项,一定要注意这一点。我在使用MySQL,hiredis这些开源代码时都遇到过这个问题。

经验3:使用sctp_bindx之前,一定要先调用bind。
这个可能也是Solaris系统特有的了。我曾经遇到一个使用SCTP的应用程序,在其它系统上跑的好好的,到Solaris上就不行。后来查了一下man手册,才发现需要在调用sctp_bindx之前,要先调用bind。

经验4:shutdown()函数作用于一个listen socket时,会产生ENOTCONN错误
我曾经写过下面的程序:
第一个线程:

{  
    ......  
    FD_ZERO(&fd_sets);  
    FD_SET(sock_fd, &fd_sets);  

    ret_val = select(sock_fd + 1, &fd_sets, NULL, NULL, NULL);  

    if (ret_val > 0)  
    {  
         accept(sock_fd, NULL, NULL);  
         ......  
    }  
    else  
    {  
        ......  
    }  
}  

第二个线程:

{  
    ......  
    shutdown(sock_fd, SHUT_RD);  
    ......  
} 

其中第一个线程阻塞在select函数,sock_fd是一个listen socket。我本意是在第二个线程中调用shutdown函数,使第一个线程的select返回,结果却发现没有生效,后来才发现shutdown返回了一个ENOTCONN错误。感兴趣的朋友也可以参考这里

以上就是我在Solaris上进行网络程序开发的一点心得,希望分享出来给需要的朋友一点帮助。如果大家有更好的经验,也欢迎分享出来。