标签 GNU 下的文章

也谈Configure脚本问题的解决

开了一个下午的技术交流会,回到办公室时离下班时间已经不远,天气预报说今晚有暴雪,外面阴沉的天气似乎也证实了这一点。这时一个同事遇到了一个软件包编译的问题,一时无法解决,向我求助。

这是一个libmemcached的编译问题,我们用的是libmemcached 0.34版本,我的同事在PC Solaris上执行libmemcached的configure脚本时遇到如下错,Configure脚本提示:

checking for pthread-config… no
configure: error: could not find libpthread

但经过确认系统中明明在/usr/lib下有pthread相关库的存在:
Tony Bai-[~/libmemcached-0.34]526:>ll /usr/lib|grep pthread
lrwxrwxrwx   1 root     root          26 2009   9月 10 llib-lpthread.ln -> ../../lib/llib-lpthread.ln
lrwxrwxrwx   1 root     root          23 2009   9月 10 llib-lpthread -> ../../lib/llib-lpthread
lrwxrwxrwx   1 root     root          25 2009   9月 10 libpthread.so.1 -> ../../lib/libpthread.so.1*
lrwxrwxrwx   1 root     root          25 2009   9月 10 libpthread.so -> ../../lib/libpthread.so.1*

又确认了一下用户的环境变量设置,LD_LIBRARY_PATH也包含了这些库的目录。

经验告诉我,这个错误是假象,向上翻Configure的输出结果,的确发现些奇怪的Check结果,如下:

checking for ANSI C header files… no
checking for sys/types.h… no
checking for sys/stat.h… no
checking for stdlib.h… no
checking for string.h… no
checking for memory.h… no
checking for strings.h… no
checking for inttypes.h… no
checking for stdint.h… no
checking for unistd.h… no

第一感觉,这怎么可能呢?这些标准C库头文件居然都Check失败了!在网上用“checking for ANSI C header files… no”搜了一下,也没有找到很好的答案。

我对Configure了解也不多,但是还是让我发现了config.log这根救命稻草。config.log这个文件详细地记录了Configure的每一步校验的执行内容和结果,其中对于标准C头文件的Check是这样做的:

configure:4827: checking for ANSI C header files
configure:4857: gcc -c -g -O2 -m64  conftest.c >&5
conftest.c:1: sorry, unimplemented: 64-bit mode not compiled in
configure:4864: $? = 1
configure: failed program was:
| /* confdefs.h.  */
| #define PACKAGE_NAME "libmemcached"
| #define PACKAGE_TARNAME "libmemcached"
| #define PACKAGE_VERSION "0.34"
| #define PACKAGE_STRING "libmemcached 0.34"
| #define PACKAGE_BUGREPORT "http://tangent.org/552/libmemcached.html"
| /* end confdefs.h.  */
| #include
| #include
| #include
| #include
|
| int
| main ()
| {
|
|   ;
|   return 0;
| }
configure:4995: result: no

再往下看,检测sys/types.h等标准库头文件的错误都是:
conftest.c:1: sorry, unimplemented: 64-bit mode not compiled in
configure:5047: $? = 1

看来并非是系统没有包含标准头文件,而是Configure采用了64-bit编译的方法去测试头文件存在的时候出错。随意创建一个testm64.c的源文件,输入:

/* testm64.c */
int main() {
    ;
    return 0;
}
用gcc -g -m64 testm64.c执行编译,得到与之前相同的错误结果:
testm64.c:1: sorry, unimplemented: 64-bit mode not compiled in

查看Gcc版本,发现是3.4.6,突然恍然大悟,这不是之前发现在Solaris 10 for x86上Gcc 64位编译的一个问题吗,在Solaris 10 for x86上如果要进行64位编译,要使用/usr/sfw/bin下的gcc 3.4.3版本,不能用3.4.6版本。

除了更换Gcc之外,如果你想编译32位版本的话,还可以这样来做:修改Configure脚本,打开Configure,将-m64字样全部删除。这样Configure后编译libmemcached就一切顺利了。

以上关于Configure脚本问题的解决方法,有一定的通用性,因此记之。

简说GLIBC strncpy实现

比较以下两组代码,你认为哪组运行的更快些呢?
Example1:
        int n   = 100;
        int n4  = n >> 2;
        int i   = 0;

        int a[100];

        for (i = 0; i < n4 ;i += 4) {
                a[i] = i;
                a[i+1] = i+1;
                a[i+2] = i+2;
                a[i+3] = i+3;
        }

Example2:
       for (i = 0;i < 100;i++) {
             a[i] = i;
       }

其实这个问题在"代码大全2nd"中也有讨论,从"代码大全"中的统计结果来看,一般来说Example1更占有优势。我在solaris上做了测试,在未开优化的情况下:两者运行时间分别为2ms和6ms;在打开-O2优化后,两者均为1ms。这种通过减少循环次数的方法在GLIBC中也有体现,比如说strncpy的实现:

下面是strncpy的GLIBC源码:
char *
x_strncpy (s1, s2, n)
        char *s1;
        const char *s2;
        size_t n;
{
        reg_char c;
        char *s = s1;

        –s1;

        if (n >= 4)
        {
                size_t n4 = n >> 2; /* n4 = n / 4, n4表示下面的循环执行的次数*/

                for (;;)
                {
                        c = *s2++;
                        *++s1 = c;
                        if (c == '')
                                break;
                        c = *s2++;
                        *++s1 = c;
                        if (c == '')
                                break;
                        c = *s2++;
                        *++s1 = c;
                        if (c == '')
                                break;
                        c = *s2++;
                        *++s1 = c;
                        if (c == '')
                                break;
                        if (–n4 == 0)
                                goto last_chars;  /* 如果n = 10,s2 = "hello world",则两轮循环后,还有"尾巴"没有copy完,在last_chars处继续处理 */
                }
                n = n – (s1 – s) – 1;  /* 还没有copy完n个字节,s2就到达末尾了,跳到zero_fill处继续为s1补零 */
                if (n == 0)
       return s;
                goto zero_fill;
        }

last_chars:     
        n &= 3;       /* n = n & 3 结果 n <= 3,n即为上面循环过后"尾巴字符"的数量 */
        if (n == 0)
                return s;
        do
        {
                c = *s2++;
                *++s1 = c;
                if (–n == 0)
                        return s;
        } while (c != '');

zero_fill:       
        do
                *++s1 = '';
        while (–n > 0);

        return s;
}

相比于strlen的实现,strncpy的实现更易理解。其字面上的逻辑就是每四个字节(n>>2)作为一组,每组逐个字节进行拷贝赋值,其内在目的则是减少循环次数,以获得性能的提升。要想知道为什么减少循环次数能提升性能的话,那就要深入到汇编层面去了,这里不再详述。另外还要一提的是GLIBC中的strncmp,strncat的实现也遵循着与上面同样的逻辑。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats