Crane
Table_bottom

Search
Loading
Table_bottom

分类
Table_bottom

随机文章
Table_bottom

标签云
Table_bottom

最新评论
Table_bottom

链接
Table_bottom

功能
Table_bottom

Linux下用g++编译共享库的一个问题

最近在使用linux下的共享库so的时候遇到一个奇怪的问题,做个记录,方便备查。

一般来说,如果用gcc编译的时候加上-shared和-fPIC选项,可以把源文件编译成一个so文件,可以在其他源程序连接阶段把这个链接上去,从而可以调用so文件提供的函数接口,这样可以多文件共用一个so文件提供的函数,即节省内存空间,也便于更新,所有的接口只需要更新so文件就行。

其实除了上面的方法,还有一个方法,那就是在运行时由程序自己动态加载so文件,使用一系列系统调用如dlopen,dlsym,dlclose等来进行动态加载,获取函数地址从而进行函数调用,关闭加载的so文件等。

一般以第一种方法用得多,但是第二种方法更灵活,结合配置文件,更具一般意义上的服务扩展性。 但是就是在用第二种方法的时候出现了一点问题。

这里把问题抽象一下,假设有一个源文件是要编译为so文件的,假设这个源文件只提供一个简单的函数,add,取两个整数为参数,返回它们的和,源文件为add.c,代码如下:

#include <stdio.h> 
 
int add(int a, int b) 
{ 
    return a+b; 
} 

编译

gcc -shared -fPIC add.c -o libadd.so

 

然后写个脚手架程序来测试,test.c,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char *argv[])
{
    void * handle;
    int (*func)(int, int);
    char *error;

    handle = dlopen("libadd.so", RTLD_LAZY);
    if(!handle)
    {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    func = (int (*)(int,int))dlsym(handle, "add");
    if((error = dlerror()) != NULL)
    {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    func(3, 4);
    dlclose(handle);

    return 0;
}
 

编译

gcc test.c -o test -ldl

然后修改LD_LIBRARY_PATH变量,加上库所在的目录,这里是当前目录

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
 

好的,这样是没有问题的,这样的方法是标准的动态加载so库的方法,但是问题在什么地方呢,如果把gcc换成g++,问题就出现了 整个编译完成后,执行test,它会告诉你

./libadd.so: undefined symbol: add
 

找不到符号add 然后用nm看一下libadd.so的符号名

$ nm libadd.so |grep add

00000000000005ec T _Z3addii

它娘的,这是c++编译器的符号命名法,用c++filt看一下

$ c++filt _Z3addii

add(int, int)

看吗,这才是我们很显而易见的函数名 所以,解决方案出来,把库源文件的函数当C符号来处理,这样

extern "C" {
// the function code
...
}

然后编译运行,这就是好的,编译出来后用nm看一下

$ nm libadd.so |grep add

00000000000005dc T add

哈哈,这就好了,然后在dlsym中写符号名的时候就直接写add就行了。

这里例子虽然是用g++编译纯C文件,其实是真正的cpp文件也是可以的。

15身份证号码转18位的程序

以前在哪看到的,安全焦点吧!丢这做个备份

 

/*输入原来的15位身份证号码,产生新的18位身份证号码的程序*/

#include "stdio.h"
#include "string.h"
#include "conio.h"

/*
* gen New 18 ID Card from old 15 ID
*/
char genNewID( char ID[], char NewID[])
{
    int W[18] = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1};
    char A[11] = {'1','0','x','9','8','7','6','5','4','3','2'};
    int i,j,S;

    if(strlen(ID) != 15)
        return -1;

    memcpy( NewID, ID, 6 );
    NewID[6]='1';
    NewID[7]='9';
    NewID[8]=0;
    strcat( NewID, &ID[6] );
    S = 0;
    for(i=0;i<17;i++)
    {
        j = (NewID[i] - '0') * W[i];
        S = S + j;
    }

    S = S % 11;
    NewID[17] = A[S];
    NewID[18] = 0;

    return A[S];
}

int main(int argc, char* argv[])
{
    char ID[20], NewID[20], ret;

    puts("输入原来的15位身份证号码,产生新的18位身份证号码\n");
    do{
        printf("Input your old 15 ID Card: ");
        scanf( "%s", ID );
        if(stricmp(ID, "exit") == 0)break;
        ret = genNewID( ID, NewID );
        printf("Your New 18 ID Card:       %s \n", ret != -1 ? NewID : "Input Error!!");
    }while(1);

    getch();

    return 0;
} 

 

 

测试堆的最大申请数量

看到说linux下的虚拟地址空间分给进程本身的是3GB(高址的1GB是内核空间,也就是0xc000000以上地址),所以有这样一个程序来测试用malloc最多能申请多少内存?

#include<stdio.h>
#include
unsigned max=0;
int main(void)
{
  int i,count;
  unsigned size[]={1024*1024,1024,1};
  for(i=0;i<3;i++){
    for(count=1;;count++){
      void *p=malloc(max+count*size[i]);
      if(p){
        max+=count*size[i];
        free(p);
      }
      else{
        break;
      }
    }
  }
  printf("The max memory i can alloc is %u\n",max);
  return 0;
}

在我的linux机器上跑了后发现只有1.1G左右的空间,想一下应该是内存768M+swap400多M,所以到不了那么大吧!

 

 

 

 

求三个参数中最大两个的平方和

看到一题,实现一个函数,求三个函数中最大的两个参数的平方和。

很自然的,想到这样的方法:

int sum_square_largest(int x, int y, int z){

    if(y > x && z > x){
        return y * y + z * z;
    }

    if(x > y && z > y){
        return x * x + z * z;
    }

    if(x > z && y > z){
        return x * x + y * y;
    }
}
但是可以看到中间的判断都是相似的,由此可就得一更简洁的方法,使用递归。
int sum_square_largest(int x, int y, int z){

    if (x <= y && x <= z){
        return y * y + z * z;
    }

    return sum_square_largest(y, z, x);
}
由于这里是尾递归,gcc优化会识别出这个,然后自动调整堆栈,不会出现函数开销在堆栈上花费很多时间资源的情况。

 
 

 

 

 

 

分享代码的好地方

介绍一个分享以及在线运行代码的好地方,codepad,在线运行支持语言种类也很多,当然那个PlainText就免谈了(其实是分享用)。

Language:













界面很简洁,一看就知道怎么用,注意到下面那个Private选项了吗?这里其实可以分享代码,是个geek的好地方,有的人甚至写出了VIM上 用的插件,可以直接在vim里面写代码,然后发布到这里来分享。

程序员用的东西一向以简洁高效著称,这里的注册就很简单,只要你打个用户名和密码就成,密码也让你只打一遍,请保证正确,不过geek一般都自信不会打错。

想看最近都有哪些代码提交,点击那个Recent Pastes就可以看到一大串的列表了,还有提交时间。