Ubuntu 11.10 (Oneiric) 上如何编译带 utrace 补丁的内核

首先准备 linux 内核编译环境:

  1. sudo apt-get install fakeroot build-essential crash kexec-tools makedumpfile kernel-wedge kernel-package
  2. sudo apt-get build-dep linux
  3. sudo apt-get install git-core libncurses5 libncurses5-dev libelf-dev asciidoc binutils-dev

检出带有 utrace 补丁的官方内核代码,并生成对应 Ubuntu 当前版本内核(3.0)的补丁:

  1. git clone https://github.com/utrace/linux.git utrace-linux-git
  2. cd utrace-linux-git/
  3. git checkout -b utrace-3.0 origin/utrace-3.0
  4. git diff v3.0 > /tmp/utrace.patch

获得 Ubuntu 定制内核代码,并打上 utrace 补丁:

  1. sudo apt-get install linux-source
  2. tar xjf /usr/src/linux-source-3.0.0.tar.bz2
  3. cd linux-source-3.0.0/
  4. patch -p1 < /tmp/utrace.patch

输出为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
patching file Documentation/DocBook/Makefile
patching file Documentation/DocBook/utrace.tmpl
patching file arch/x86/kernel/ptrace.c
patching file fs/proc/array.c
patching file include/linux/ptrace.h
patching file include/linux/sched.h
Hunk #1 succeeded at 187 (offset 3 lines).
Hunk #2 succeeded at 206 (offset 3 lines).
Hunk #3 succeeded at 1415 with fuzz 2 (offset 7 lines).
patching file include/linux/signal.h
patching file include/linux/tracehook.h
patching file include/linux/utrace.h
patching file init/Kconfig
Hunk #1 succeeded at 388 (offset 16 lines).
patching file kernel/Makefile
patching file kernel/fork.c
Hunk #1 FAILED at 168.
Hunk #2 succeeded at 1098 (offset 3 lines).
1 out of 2 hunks FAILED -- saving rejects to file kernel/fork.c.rej
patching file kernel/ptrace.c
patching file kernel/sched.c
patching file kernel/signal.c
Hunk #4 succeeded at 1993 (offset -2 lines).
Hunk #5 succeeded at 2017 (offset -2 lines).
Hunk #6 succeeded at 2124 (offset -2 lines).
Hunk #7 succeeded at 2132 (offset -2 lines).
patching file kernel/utrace.c

注意 kernel/fork.c 的补丁失败了,看看 kernel/fork.c.rej

1
2
3
4
5
6
7
8
9
10
--- kernel/fork.c
+++ kernel/fork.c
@@ -168,6 +168,7 @@
free_thread_info(tsk->stack);
rt_mutex_debug_task_free(tsk);
ftrace_graph_exit_task(tsk);
+ tracehook_free_task(tsk);
free_task_struct(tsk);
}
EXPORT_SYMBOL(free_task);

修改 kernel/fork.c ,在 free_task 函数的 ftrace_graph_exit_task(tsk); 之后手工加上这行 tracehook_free_task 调用即可。

编译新的内核,使用当前系统内核的配置参数作为基准,开启 utrace 补丁提供的 CONFIG_UTRACE 功能即可:

1
2
cp /boot/config-$(uname -r) .config
make oldconfig

出现如下提示时回答 y: Infrastructure for tracing and debugging user processes (UTRACE) [N/y/?] (NEW)

1
2
make-kpkg clean
export CONCURRENCY_LEVEL=9

这里指定编译内核时的并发任务数,设置为核数+1即可

1
fakeroot make-kpkg --initrd --append-to-version=-utrace binary-arch

编译完成后,在 linux-source-3.0.0/ 的上级目录会生成 linux-headers/image/debug-symbol 的 deb 安装包,直接用 sudo dpkg -i *.deb 安装即可。重启后选择新内核进入即可用 SystemTap 进行用户态程序跟踪。

参考资料

Alienware 惊魂记

Alienware M17x R3 已经到手很久了,本来配置的 AMD Radeon HD 6990M 是准备拿来玩 GPGPU 的。但由于 Alienware 坚持使用 Muxed Graphics Switch(人工切换集/独显比较可靠),而 Linux 下的新版本 AMD Catalyst 驱动只支持 Muxless Graphics Switch,使得这块卡在 Linux 下一直得不到很好的应用。

如果能禁用集成显卡,应该就可以正常使用 Catalyst 驱动,杯具的是官方 BIOS 从 A04 版之后就去掉了显卡切换选项,Linux 下也没发现有软件方式解决该问题。最近在这里找到人家破解的 M17x R3 A08 BIOS 解开了显卡切换选项,于是刷上试试。在新 BIOS 的第 2 个 Advanced/Video Configuration/Internal Graphics Device 组中,把 Internal Graphics Device 对应选项从原来的 Auto 改为 Disable 禁用集显,然后 Catalyst 果然好用了,只是屏幕亮度在禁用集显之后略有下降,不影响使用。

但是……我手很欠的想试试别的选项,结果把 Video Configuration/Primary Display 选项从原来的 SG 改成了 Auto,又把 Internal Graphics Device/Internal Graphics Device 从 Disable 改成了 Enable,重启以后就杯具鸟:主显示器黑屏,外接显示器无信号,BIOS 不进入 POST 过程,键盘无反应,电源灯间隔几秒闪一下,同时伴有很小的 beep 声。Alienware 就这样成了板砖……

恢复尝试1:CMOS放电

  • 拔掉电池和电源适配器
  • 长按电源键30s
  • 插上电池或电源适配器开机
  • 失败…… 😦

恢复尝试2:联系 DELL 售后

售后人员表示刷非官方 BIOS 导致的故障不保修,我擦…… 😦

恢复尝试3:BIOS 灾难恢复(需要另一台可用电脑)

  • 打开 Alienware 背板,拆掉独立显卡(具体过程参考这里:http://dell.benyouhui.it168.com/thread-1711488-1-1.html)
  • 用 7-zip 打开官方 A08 BIOS 可执行文件,提取其中的 PAR00MEC.fd 文件(用破解 BIOS 的同名文件也可以,我就是继续刷破解 BIOS 😉),改名为 PAR00X64.fd (灾难恢复特殊文件,注意大小写,仅适用于 M17 系列,用附件中的 phoenixtool 打开 PAR00MEC.fd 即可获知该特殊文件名)
  • PAR00X64.fd 复制到仅有一个小于 2GB 的 FAT32 分区的 U 盘根目录下,再将 U 盘插到 Alienware 的 eSATA 口上(你没看错……实际是个 eSATA/USB 通用口)
  • Alienware 拔掉电池和电源,按住小键盘区的 END 键不放,插上电源,自动开机后松开即可(注意:如果之前不拆掉独立显卡,还是会维持原来的板砖状态的,无法继续)
  • 等几秒后,Alienware 会自动读取 U 盘上的灾难恢复文件并刷新 BIOS,同时伴有 10 几声较大的 beep 声
  • beep 声结束后会自动重启 2 次,不要断电!
  • 最后就看到正常的开机画面了! 😃

破解的 A08 BIOS、phoenixtools 工具和拆机照见附件,也许对你有帮助哈。

拆下来的 6990M 显卡及散热管

顺便拆开键盘看看

键盘下面有还没用上的 2 个内存槽

近看未用的内存槽,全插上 8GB 内存条就 16GB 内存咯

留个默认 BIOS 设置照,改 Primary Display 选项要小心啊

要禁用集显从这里进

这里的 Primary Display 选项别乱改哈

这里的 Integrated Graphics Device 从 Auto 改成 Disable 就能禁用集显了

64 位 Ubuntu 11.10 下安装 PPStream

PPStream 的 Linux 版本只出了 i386 架构的安装包,但由于 64 位 Ubuntu 11.10 的包划分有所变化,直接安装 PPStream 官方 deb 包会提示依赖问题。在我的机器上通过以下步骤可以成功安装 PPStream:

  • 安装 32 位支持库:
1
sudo apt-get install ia32-libs
  • 下载 PPStream deb 包:
1
wget http://download.ppstream.com/linux/PPStream.deb
  • 去除无效的依赖关系再安装:
1
2
3
4
5
6
7
8
9
# 提取deb包文件
dpkg-deb -x PPStream.deb ppstream
# 提取deb包元信息
dpkg-deb -e PPStream.deb ppstream/DEBIAN
# 修改 ppstream/DEBIAN/control,删除Depends:...这行并保存
# 重新打包
dpkg-deb -b ppstream PPStream-hacked.deb
# 安装新包
sudo dpkg -i PPStream-hacked.deb
  • 此时 PPStream 自带的 mplayer 因为缺乏 32 位的 libgif4 库而无法启动(表现为启动 PPStream 后视频无法播放,不停地跳到下一个视频),但 32 位版本的 libgif4 和 64 位版本文件位置相同不能直接安装,这应该是 apt 源中的打包问题,需要我们自己来修正:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 下载32位libgif4包
apt-get download libgif4:i386
# 提取deb包文件
dpkg-deb -x libgif4*.deb libgif4
# 提取deb包元信息
dpkg-deb -e libgif4*.deb libgif4/DEBIAN
# 修改安装位置
mv libgif4/usr/lib{,32}
mv libgif4/usr/share/doc/libgif4{,-i386}
# 修改libgif4/DEBIAN/md5sums,将其中的usr/lib改为usr/lib32,usr/share/doc/libgif4改为usr/share/doc/libgif4-i386,保存
# 重新打包
dpkg-deb -b libgif4 libgif4-hacked.deb
# 安装新包
sudo dpkg -i libgif4-hacked.deb
  • 现在应该可以启动 PPStream 了,若无声音可调整选项中的音频设备为 alsa,若无图像可调整选项中的视频设备为 xv (可根据自己系统环境找一个性能更好的选项)

Enjoy it!

注意:上述对 32 位 libgif4 包的处理仅对 Ubuntu 11.10 及之前的版本有效,11.10 之后的版本动态库目录发生了变化,且该包的问题可能已经被修正,笔者没有新环境进行验证,请自行寻找相关解决方案!

更新:Linux Mint 14 (Nadia) / Ubuntu 12.10 (Quantal) 无需进行上面 libgif4 包的修正,直接 sudo apt-get install libgif4:i386 libjpeg62:i386 安装相应包即可正常启动 PPStream

[zz] Optimizing TCP Socket Across Data Centers

原文链接:http://sna-projects.com/blog/2011/01/optimizing-tcp-socket-across-data-centers/

一点评注: 广域网中高延迟带宽积链路的基本优化方式就是增加 TCP 连接的确认窗口大小,确认窗口大小在 Linux 下直接对应于 socket send/recv buffer size 设置,大于 64 KB 的窗口大小是 TCP 扩展选项,需要在 3-way handshake 时进行双方协商,因此对 socket send/recv buffer size 的设置必须在 listen / connect 之前进行,否则在 TCP 连接建立以后就无法使用大于 64 KB 的窗口了。看 man 7 tcp 就能发现相关说明。

另外应用程序能自行设置的最大 send/recv buffer size 同时收到 kernel 配置项 net.core.rmem_max / net.core.wmem_maxnet.ipv4.tcp_rmem[2] / net.ipv4.tcp_wmem[2] 的约束,设置很大的 buffer size 前先看看需不需要提升这些限制。

Intel Non-Commercial Software Download

http://software.intel.com/en-us/articles/non-commercial-software-download/

更新下载内容

Product Suites

  • Intel® Parallel Studio XE 2013 for Linux
    • Includes Intel® C++ Composer XE 2013, Intel® Fortran Composer XE 2013, Intel® VTune™ Amplifier XE 2013, Intel® Inspector XE 2013
  • Intel® C++ Studio XE 2013 for Linux
    • Includes Intel® C++ Composer XE 2013, Intel® VTune™ Amplifier XE 2013, Intel® Inspector XE 2013

Compilers and Libraries

  • Intel® Fortran Composer XE 2013 for Linux
    • Includes Intel® Fortran Compiler, Intel® Math Kernel Library 11.0
  • Intel® C++ Composer XE 2013 for Linux
    • Includes Intel® C++ Compiler, Intel® Integrated Performance Primitives 7.1, Intel® Math Kernel Library 11.0, Intel Cilk™ Plus, the Intel® Threading Building Blocks (Intel® TBB)

Performance Libraries

  • Intel® Math Kernel Library (Intel® MKL) 11.0 for Linux
  • Intel® Integrated Performance Primitives (Intel® IPP) 7.1 for Linux

Performance Profilers

  • Intel® VTune™ Amplifier XE 2013 for Linux

Thread and Memory Checkers

  • Intel® Inspector XE 2013 for Linux

Other Free Resources from Intel

  • Download free code samples (Intel® IPP 7.1 Only); Jump-start your software development with Intel® IPP

Baidu 首页手写输入法交互数据格式分析

手写识别接口以 HTTP POST 方式访问 http://hw.baidu.comContent-Typeapplication/x-www-form-urlencoded ,POST body 中包含如下 2 个参数:

  • type - 总是为 1;
  • wd - 为手写笔划矢量数据,格式如下:
    • 每条矢量笔划数据都是形如 x1,y1,x2,y2,... 的坐标列表,其中 x 和 y 坐标的有效值范围在 4~209 之间(屏幕坐标系,左上角为原点,向右向下坐标递增),以字符 "a" 作为分隔符将 10 进制坐标数值序列化为字符串。例如一个笔划经过的坐标若为 (15,15)-(15,30) ,则该笔划序列化后的字符串为 "15a15a15a30" ;而另一条笔划经过的坐标若为 (15,30)-(30,30) ,则序列化后的字符串为"15a30a30a30";
    • 多条矢量笔划数据字符串之间以 "aa" 分隔后作为最终数据串,例如上述 2 条笔划组成的最终数据串为 "15a15a15a30aa15a30a30a30"

对应上述笔划数据的完整手写识别请求为:

1
2
3
4
5
POST http://hw.baidu.com HTTP/1.1
Content-type: application/x-www-form-urlencoded
Content-length: 34

wd=15a15a15a30aa15a30a30a30&type=1

响应体为 JSON 对象,包含如下 2 个字段:

  • t - 总是为 1;
  • s - 为候选字列表字符串,其中包含 10 个候选字,以 Unicode 转义序列表示

对应上述手写识别请求的响应为:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Date: Wed, 18 Aug 2010 14:39:09 GMT
Server: hw.baidu.com
Content-Length: 74
Content-Type: text/html;charset=gb2312
Cache-Control: private
Expires: Wed, 18 Aug 2010 15:39:09 GMT
Connection: Keep-Alive

{"s":"\u004c\u4e5a\u0063\u0043\u5315\u4e04\u4e0a\u006c\u535c\u4e00","t":1}

经过简单的解码即可知候选字列表为 "L乚cC匕丄上l卜一"。

对此接口进行一些简单的包装即可实现各种在线手写识别应用,不过有没有可能用这个做 OCR?有兴趣的同学可以尝试尝试 😃

Erlang 中 DNS 解析顺序的问题

Erlang 的 DNS 解析方法有包括 file(读取 /etc/hosts 文件)、dns(Erlang 自己的 DNS 客户端)、native(调用外部程序 inet_gethost 用 libc 的 gethostbyname 函数解析域名) 在内的好几种方式,可以在 kernel inetrc 文件中以 {lookup, [...]} 形式指定多种 DNS 解析方法的应用顺序。在 inet:gethostbyname_tm/4 函数中可以发现任意一个域名的解析顺序是:

  1. 尝试按 lookup 属性中指定的顺序应用 file、dns、native(或 yp、nis、nisplus、wins 等)中的方法解析域名,并忽略不认识的 DNS 解析方法;
  2. 若所有指定的 DNS 解析方法都试过以后也没能得到 IP 地址,就检查给定域名是不是已经是一个 IPv4/v6 地址了,若是则直接返回,否则报错。

由于 dns 方式比 native 方式效率高、并发能力好,所以配置 lookup 为 [file, dns] 是比较常见的。这一解析顺序通常情况下工作的都很好,但若 DNS 服务器会将所有无法正常解析的域名解析为一个统一的 IP 地址(例如大部分公司网络环境),而你尝试去解析一个已经是 IPv4/v6 地址的字符串,就会产生严重的问题。Why?因为在第 1 步中,dns 方法会将已经是 IP 地址的字符串当作域名发送给 DNS 服务器,由于前述的 DNS 服务器特性,解析结果就会变成另外一个驴头不对马嘴的 IP 地址,造成严重的混乱。

这一问题影响面很广,基础库中的 gen_tcp、gen_udp、http 等都会受波及。目前的临时解决方法有这么几种:

  1. 若可能被当作域名解析的 IP 地址是有限的几个,最简单的办法是在 /etc/hosts 文件中为这些域名设置本地域名别名,这样在 file 解析阶段就能将其正确处理了;
  2. 或者在 lookup 属性中用 native 替代 dns,由于 native 使用 libc 的 gethostbyname 函数解析域名,其会先识别给定域名是否已经是 IP 地址,故可以避免发生此问题。弊端是 native 调用外部进程解析域名,并发度和性能上都有不小的损失;
  3. 或者自行用 inet_parse:ipv4_address/1, ipv6_address/1 预先检查待解析的域名字符串是否已经是 IP 地址,若是则以标识 IP 地址的 4 元组(IPv4)或 16 元组(IPv6)作为主机地址参数。但此法对 http 模块无效,因为即便 URL 中给出的主机地址是 IP,该模块内部还是会进行一次域名解析。

该问题我已经发到了 erlang-bugs@erlang.org 并得到了 Raimo Niskanen 的积极回应,有望在下个版本中得到改进:

Raimo Niskanen 写道: This leaves us some other alternatives:

  1. To extend the 'file' or the 'dns' lookup methods with IP string detection.
  2. To do only do IP string detection when the 'native' lookup method is not used, but to do it first instead of last as today, within the inet:gethostbyname lookup method wrapper itself.
  3. To introduce a new lookup method e.g 'ipstring' that the user can insert in the lookup chain wherever suitable.

I do not like 1) since it would make either of those lookup methods impure, harder to test and harder to explain.

I prefer 2) because it would behave kind of natural.

However, 3) is the purest but would require manual configuration in more cases.

But 2) wins... At least until someone convinces me otherwise.

Linux 中针对 SysV IPC 的内核参数调整

SysV IPC 包括 Semaphore、Shared Memory 和 Message Queue 这 3 类进程间通信手段,虽然 POSIX.1-2001 实时接口标准规定了另一套提供相同手段但更一致化的接口(POSIX IPC),但 SysV IPC 仍然有相当数量的用户。

通过调整一些内核参数,可以更改 SysV IPC 对数据的固有限制,相关参数对应的控制文件可在 /proc/sys/kernel/ 目录下找到,也可以通过 sysctl 更改,现罗列如下:

控制文件路径 内核参数(通过 sysctl 更改时使用) 含义
/proc/sys/kernel/shmmax kernel.shmmax 共享内存中最大内存块尺寸(byte),默认为 33554432,即 32MB
/proc/sys/kernel/shmall kernel.shmall 整个系统中共享内存的最大尺寸,以页(4KB)为单位,默认为 2097152,即 2M 个页,共计 8GB
/proc/sys/kernel/shmmni kernel.shmmni 整个系统中共享内存块的最大数量,默认为 4096
/proc/sys/kernel/msgmax kernel.msgmax 消息队列中单条消息的最大尺寸(byte),默认为 8192
/proc/sys/kernel/msgmnb kernel.msgmnb 默认的每个消息队列的最大尺寸(byte),默认为 16384
/proc/sys/kernel/msgmni kernel.msgmni 整个系统中消息队列的最大数量,默认为 16
/proc/sys/kernel/sem kernel.sem 信号量的 4 个控制参数,从前向后分别是:单个信号量集合中容纳的最大信号量数量(默认为 250)、整个系统中信号量的最大数量(默认为 32000)、每个 semop 调用中允许的最大操作数量(默认为 32)、整个系统中信号量集合的最大数量(默认为 128)

以上参数及含义都可以从 linux kernel 源码树的 ipc/ 子目录下 SysV IPC 对应的实现文件中找到,其相关宏定义在 include/linux/ 子目录下的相关头文件中。

GCC 中 -pthread 和 -lpthread 的区别

用 GCC 编译使用了 POSIX thread 的程序时通常需要加额外的选项,以便使用 thread-safe 的库及头文件,一些老的书里说直接增加链接选项 -lpthread 就可以了,像这样:

1
2
$ gcc -c x.c
$ gcc x.o -ox -lpthread

而 GCC 手册里则指出应该在编译和链接时都增加 -pthread 选项,像这样:

1
2
$ gcc -pthread -c x.c
$ gcc x.o -ox -pthread

那么 -pthread 相比于 -lpthread 链接选项究竟多做了什么工作呢?我们可以在 verbose 模式下执行一下对应的 GCC 命令行看出来。下面是老式的直接加 -lpthread 链接选项的输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ gcc -v -c x.c
...
/usr/lib/gcc/i486-linux-gnu/4.2.4/cc1 -quiet -v x.c -quiet -dumpbase x.c
-mtune=generic -auxbase x -version -fstack-protector -fstack-protector -o /tmp/cch4ASTF.s
...
as --traditional-format -V -Qy -o x.o /tmp/cch4ASTF.s
...
$ gcc -v x.o -ox -lpthread
...
/usr/lib/gcc/i486-linux-gnu/4.2.4/collect2 --eh-frame-hdr -m elf_i386 --hash-style=both
-dynamic-linker /lib/ld-linux.so.2 -ox
/usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib/crt1.o
/usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib/crti.o
/usr/lib/gcc/i486-linux-gnu/4.2.4/crtbegin.o
-L/opt/intel/Compiler/11.1/046/tbb/ia32/cc4.1.0_libc2.4_kernel2.6.16.21/lib/../lib
-L/usr/lib/gcc/i486-linux-gnu/4.2.4
-L/usr/lib/gcc/i486-linux-gnu/4.2.4
-L/usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib
-L/lib/../lib
-L/usr/lib/../lib
-L/opt/intel/Compiler/11.1/046/lib/ia32
-L/opt/intel/Compiler/11.1/046/tbb/ia32/cc4.1.0_libc2.4_kernel2.6.16.21/lib
-L/usr/lib/gcc/i486-linux-gnu/4.2.4/../../..
x.o -lpthread -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc
--as-needed -lgcc_s --no-as-needed
/usr/lib/gcc/i486-linux-gnu/4.2.4/crtend.o /usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib/crtn.o

下面是在编译和链接时分别指定 -pthread 选项的输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ gcc -v -pthread -c x.c
...
/usr/lib/gcc/i486-linux-gnu/4.2.4/cc1 -quiet -v -D_REENTRANT
x.c -quiet -dumpbase x.c
-mtune=generic -auxbase x -version -fstack-protector -fstack-protector -o /tmp/cc205IQf.s
...
as --traditional-format -V -Qy -o x.o /tmp/cc205IQf.s
...
$ gcc -v x.o -ox -pthread
/usr/lib/gcc/i486-linux-gnu/4.2.4/collect2 --eh-frame-hdr -m elf_i386 --hash-style=both
-dynamic-linker /lib/ld-linux.so.2 -ox
/usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib/crt1.o
/usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib/crti.o
/usr/lib/gcc/i486-linux-gnu/4.2.4/crtbegin.o
-L/opt/intel/Compiler/11.1/046/tbb/ia32/cc4.1.0_libc2.4_kernel2.6.16.21/lib/../lib
-L/usr/lib/gcc/i486-linux-gnu/4.2.4
-L/usr/lib/gcc/i486-linux-gnu/4.2.4
-L/usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib
-L/lib/../lib
-L/usr/lib/../lib
-L/opt/intel/Compiler/11.1/046/lib/ia32
-L/opt/intel/Compiler/11.1/046/tbb/ia32/cc4.1.0_libc2.4_kernel2.6.16.21/lib
-L/usr/lib/gcc/i486-linux-gnu/4.2.4/../../..
x.o -lgcc --as-needed -lgcc_s --no-as-needed <strong>-lpthread</strong>
-lc -lgcc
--as-needed -lgcc_s --no-as-needed
/usr/lib/gcc/i486-linux-gnu/4.2.4/crtend.o /usr/lib/gcc/i486-linux-gnu/4.2.4/../../../../lib/crtn.o

可见编译选项中指定 -pthread 会附加一个宏定义 -D_REENTRANT ,该宏会导致 libc 头文件选择那些 thread-safe 的实现;链接选项中指定 -pthread 则同 -lpthread 一样,只表示链接 POSIX thread 库。由于 libc 用于适应 thread-safe 的宏定义可能变化,因此在编译和链接时都使用 -pthread 选项而不是传统的 -lpthread 能够保持向后兼容,并提高命令行的一致性。