转自:http://www.cnblogs.com/Swartz/articles/3939382.html

作者:Lokki 出处:http://www.cnblogs.com/Swartz/ 欢迎转载,也请保留这段声明。谢谢!

讨论贴


最近在CSDN上看到一个帖子在讨论 进程间共享的Posix mutex的锁定状态能否被子进程继承?,其中4楼的帖子给出了一个测试局部mutex能否被继承的例子:

 1 #include <pthread.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5
6 int main(void)
7 {
8 pid_t pid;
9 pthread_mutex_t mut;
10
11 pthread_mutex_init(&mut, NULL);
12
13 printf("lock\n");
14 pthread_mutex_lock(&mut);
15
16 printf("fork\n");
17 pid = fork();
18 if( pid == 0 ) // 子进程尝试锁定
19 {
20 printf("child: lock\n");
21 pthread_mutex_lock(&mut);
22 printf("child: over\n");
23
24 exit(0);
25 }
26
27 pthread_mutex_destroy(&mut);
28 return(0);
29 }

在之后的楼层讨论,大家发现在编译以上代码时候加 -lpthread 和不加 -lpthread 得出了截然不同的结果。这是为什么呢?

1 gcc -o test1 main.c -lpthread
2 gcc -o test2 main.c

本文主要讨论动态链接编译的方式。

为什么加不加 -lpthread 都可以编译通过且成功执行


由于程序正确地添加了头文件 pthread.h,所以在编译的链接阶段之前,加不加 -lpthread 生成的目标文件是没有区别的,区别在于编译阶段的链接过程。程序里面的pthread_mutex_init pthread_mutex_lock 等函数(事实上除了main函数之外的函数都是)是未定义的符号。编译器需要在链接期间的正确地解析未定义符号的地址。那么对于test2中的pthread_mutex_lock函数,连接器能正确解析到吗?我们先来看一下test1和test2都链接了哪些动态库(注意链接库的顺序)。

很显然,test2链接的动态库里面没有pthread。我们看一下libc.so里面的符号是不是有pthread_mutex_lock函数。

原来在libc.so库里面有这些函数,这样test2就可以正确编译执行了。

为什么程序运行的结果不一样?


在我们执行两个程序之后得到以下结果:

显然,test1的结果表明了局部mutex的状态可以被子进程继承,test2的结果却恰恰相反。这是因为test1调用的pthread_mutex_*函数是来自于pthread库,test2调用的函数来自libc.so库。libc.so库里面的pthread_mutex_*函数什么都没有做,只是返回了0。这样就会出现上述结果。

为什么libc要定义一些多线程的函数?


我们再次查看libc.so定义的pthread有关的函数时候,可以找到很多熟悉的函数(没有pthread_create,为什么?)。

libc之所以这样做是因为一些库需要做到线程安全,但是自身却不使用线程。试想,一个库的函数需要使用mutex,当该函数调用时候需要lock和unlock。这样在程序为单线程时候不必链接pthread库这个函数仍然可以正常调用,事实上lock和unlock的动作不会有任何效果。在多线程时候,程序链接了pthread库,这样函数就可以正常地lock和unlock,实现线程安全的特性。

例如,根据posix标准,你每次调用fputc(ch, stream)都会有mutex的lock和unlock。

如何保证调用到有效的pthread函数?


1. 对于动态链接的程序

通过符号介入。符号介入是指:在动态链接时候,如果一个符号在多个库里面有定义,那么连接器将使用它第一次找到的版本。看第一个图,我们可以看到libpthread的链接顺序要比libc靠前,默认情况下libc都排在编译器链接顺序的最后一位。这样的话,如果程序没有链接pthread库,那么程序会调用libc的pthread函数。如果程序链接了pthread库,由于pthread的链接顺序总是排在libc之前,程序会调用pthread的函数。

2. 对于静态链接的程序

之前我们讨论的是动态链接的程序,其中涉及到了动态链接符号的解析,由于静态链接程序没有这个步骤,所以静态链接程序不能通过这个来实现。我们可以看一下libc.a里面的pthread函数:

很明显,pthread的相关函数都被定义为了弱符号,而这些函数在libpthread.a里面都被定义为强符号。这样在程序链接pthread时候会调用pthread的强符号,没有链接pthread时候会调用libc定义的函数。

3. 符号版本的作用

情况比较少,暂时忽略

后记


通过对现象的深入剖析,看到了libc库的强大。事实上,libc除了pthread相关函数还实现了其他的一些函数,这些函数称为stubs。他们不会实现实际的功能,只是一个占位符(place holder),当有真正的函数可用时候,他们就会让出自己的位置。

GUN对stub的描述:

A stub function is a function which cannot be implemented on a particular machine or operating system. Stub functions always return an error, and set errno to ENOSYS (Function not implemented). See Error Reporting. If you define a stub function, you must place the statement stub_warning(function), where function is the name of your function, after its definition. This causes the function to be listed in the installed <gnu/stubs.h>, and makes GNU ld warn when the function is used.

参考


  1. http://stackoverflow.com/questions/6266183/does-linking-an-lpthread-changes-application-behaviour-linux-glibc
  2. http://stackoverflow.com/questions/21092601/is-pthread-in-glibc-so-implemented-by-weak-symbol-to-provide-pthread-stub-functi
  3. http://stackoverflow.com/questions/11161462/why-glibc-and-pthread-library-both-defined-same-apis/11210463#11210463
  4. http://stackoverflow.com/questions/21092601/is-pthread-in-glibc-so-implemented-by-weak-symbol-to-provide-pthread-stub-functi
  5. http://www.gnu.org/software/libc/manual/html_node/Porting.html

编译程序加不加 -lpthread 的区别【转】的更多相关文章

  1. shell脚本加不加export的区别

    加了export: jackyyu@ubuntu:~$ cat 1.sh #!/bin/dash test=test echo ${test} echo ${TERM} TERM=dumb expor ...

  2. JavaScript函数后面加不加括号的区别

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  3. JAVA中,一个类中,方法加不加static的区别,

    通俗理解: 1.若是对象的特有行为,(也就是某个实例方法特有的行为),不加static 2. 若是对象集合共有的集合,则加static static类型方法只可以访问静态变量和方法 实例方法可以访问实 ...

  4. C++中创建对象的时候加括号和不加括号的区别

    c++创建对象的语法有----- 1 在栈上创建 MyClass a; 2 在堆上创建加括号 MyClass *a= new MyClass(); 3 不加括号 MyClass *a = new My ...

  5. C++中创建对象的时候加括号和不加括号的区别(转)

    c++创建对象的语法有----- 1 在栈上创建 MyClass a; 2 在堆上创建加括号 MyClass *a= new MyClass(); 3 不加括号 MyClass *a = new My ...

  6. scanf加不加\n?

    近两天用vs2013敲代码碰到的问题 关于scanf小括号中加不加\n的区别 例程序如下所示: 第一个程序: int main(){ ; printf("你会去敲代码吗?(选择1 or 0) ...

  7. Java中主类中定义方法加static和不加static的区别

     Java中主类中定义方法加static和不加static的区别(前者可以省略类名直接在主方法调用(类名.方法),后者必须先实例化后用实例调用) 知识点:1.Getter and Setter 的应用 ...

  8. js中加“var”和不加“var”的区别

    JavaScript 拥有动态类型.这意味着相同的变量可用作不同的类型: var x // x 为 undefined var x = 6; // x 为数字 var x = "Bill&q ...

  9. 【转】new对象时,类名后加括号和不加括号的区别

    请看测试代码: #include <iostream> using namespace std; // 空类 class empty { }; // 一个默认构造函数,一个自定义构造函数 ...

随机推荐

  1. docker-compose 使用

    Docker提供一个容器编排工具------>Docker Compose,它允许用户在一个模板(YAML格式)中定义一组相关联的应用容器,这组容器会根据配置模板中的"--link&q ...

  2. 在生产环境下实现每天自动备份mysql数据库

    1.描述 我相信很多朋友在工作都都会有这种需求,老板或领导让你每天都要备份mysql数据库,你该如何实现呢,是每天到一定的时间在服务器上敲一遍mysql的备份命令,还是想写个脚本,定时定点的自动备份呢 ...

  3. web前端使用localstorage、sessionstorage、cookie增删获方法

    今天主要的学习内容是cookie与本地储存的知识, 在HTML5中,本地存储是一个window的属性,包括localStorage和sessionStorage,从名字应该可以很清楚的辨认二者的区别, ...

  4. 用express框架实现反向代理

    目前很多公司开发都是前后台分离开发,于是我用node起了一个服务,用node中的express框架实现了反向代理.(通俗易懂的讲就是我在我的电脑访问不到后台同事的电脑接口,这样做以后就可以在我本地访问 ...

  5. SSH无密码登录及远程拷贝命令SCP的使用

    SSH无密码登录 1.生成密钥对(公钥和私钥) $ cd /home/cen/.ssh $ ssh-keygen -t rsa #生成密钥,使用rsa方式进行加密,四个回车 $ ssh-copy-id ...

  6. OpenCV学习笔记(七) 图像金字塔 阈值 边界

    转自: OpenCV 教程 使用 图像金字塔 进行缩放 图像金字塔是视觉运用中广泛采用的一项技术.一个图像金字塔是一系列图像的集合 - 所有图像来源于同一张原始图像 - 通过梯次向下采样获得,直到达到 ...

  7. RF,GBDT,XGBoost,lightGBM的对比

    转载地址:https://blog.csdn.net/u014248127/article/details/79015803 RF,GBDT,XGBoost,lightGBM都属于集成学习(Ensem ...

  8. LINQ体验(9)——LINQ to SQL语句之Insert/Update/Delete操作

    我们继续讲解LINQ to SQL语句,这篇我们来讨论Insert/Update/Delete操作.这个在我们的程序中最为常用了.我们直接看例子. Insert/Update/Delete操作 插入( ...

  9. 码农与UI沟通的日常

    事情是这样的,这是一个兴趣群组的效果图. 我看了一眼没有帖子时的提示,觉得这样的提示 不走心 不能展现出我们团队对于人生及世界的深度理解和高尚的品格. 于是,我选择了表达内心的真实感受. 我觉得这完美 ...

  10. 利用python列表实现堆栈和队列

    堆栈: 堆栈是一个后进先出的数据结构,其工作方式就像生活中常见到的直梯,先进去的人肯定是最后出. 我们可以设置一个类,用列表来存放栈中的元素的信息,利用列表的append()和pop()方法可以实现栈 ...