背景
我们在开发过程中经常需要进行程序移植。由于不同硬件环境、操作系统和各类依赖库版本的差异,会出现各种兼容性问题。
对于glibc来讲,除了C语言标准之外,还包含了posix、systemV等特性。为此,glibc针对不同的标准有特定的宏定义,用于指定当前代码说遵循的标准和版本。这些宏定义被称为特性测试宏(feature test macro)。
1. 标准概述
1.1 C语言标准
主要包含C89(1989年最初的ANSI C版本)、C99(1999)、C11(2011)和C17(2017)。
1.2 BSD
BSD(伯克利软件发布版,Berkeley Software Distribution)最初是加州大学伯克利分校1979年发布的unix版本,包括了C shell,vi等功能. 由此演化成BSD的标准.
1.3 System V
AT&T1983年发布了unix系统System III,并于1989发布了System V.通过对其它厂商的使用授权使其逐渐成为很多unix功能实现的基础.
1.4 POSIX
POSIX先是称为IEEE标准,之后又被ISO采纳,因此POSIX.1为ISO/IEC 9945-1:1990. 之后陆续升级了命令与工作标准(POSIX.2)、测试方法标准(POSIX.3)、实时 API(POSIX.4)等.
1.5 F151-1和FIPS 151
FIPS 是Federal Information Processing Standard(联邦信息处理标准)于1989年发布。由于美国政府是计算机系统的“大买家”,大多数计算机厂商都会确保其UNIX系统符合FIPS 151-1版本的POSIX.1规范。 FIPS 151-2标准已在2000年被废弃。
1.6 X/Open和XPG3
X/Open公司是由多家国际计算机厂商所组成的联盟(The Open Group) ,基于POSIX的开放系统标准。包含XPG3(1989), XPG4(1992). 其接口成为XSI(X/Open System Interface).
之后经过AT&T公司等一些列转让, X/Open又将XPG4版本2“重新包装”为SUS(Single UNIX Specification)(即SUSv1)或称之为UNIX95。其内容包括:XPG4版本2,X/Open Curses规范第4号版本2,以及X/Opena联网服务(XNS)规范第4号。
SUS版本2(SUSv2,http://www.unix.org/version2/online.html)于1997年发布.
1.7 大一统 SUSv3
2001年, IEEE , The Open Group和ISO委员会共同成立奥斯汀公共标准工作组(Austin CSRG), 批准了POSIX 1003.1-2001,成为ISO/IEC 9945:2002,该标准取代了之前的POSIX版本,被称为 SUSv3. 包含了基本定义, 系统接口, shell和其他工具等内容, 成为POSIX的超集.
SUSv3规范可在线获得,网址是http://www.unix.org/version3/online.html。通过SUSv3认证的UNIX实现可被称为UNIX 03。
至此, ANSI C, POSIX, XPG等标准实现了统一. 并在2008年发布了 POSIX.1-2008/SUSv4,或者称为(2008,Austin CSRG)
2. 特性测试宏
由上述, 每个标准的内容和接口都有差异, 有的是新添的,也有的在新标准中已经被废弃了. 因此在移植过程中, 我们需要指定我们当前代码使用的具体标准. 指定方式就是在包含头文件之前,定义相关的特性测试宏. 比如:
#define _XOPEN_SOURCE
或者添加编译选项:
gcc -D_POSIX_SOURCE hello.c
glibc具体支持的标准如下:
2.1 _POSIX_SOURCE
一经定义(不管任何值),头文件会遵从POSIX.1-1990和ISO C(1990)标准的定义。
该宏已被_POSIX_C_SOURCE取代。
2.2 _POSIX_C_SOURCE
若定义为1,效果与_POSIX_SOURCE相同。
若将其值定义为大于等于199309,遵从POSIX.1b(实时)。
大于等于199506,遵从对POSIX.1c(线程)。
等于200112,遵从对POSIX.1-2001(排除了XSI扩展)。(2.3.3版本之前,glibc头文件对_POSIX_C_SOURCE 200112不做解释。)
若将其值定义为200809,遵从POSIX.1-2008规范。(2.10版本之前,glibc头文件对_POSIX_C_SOURCE 200809不做解释。)
2.3 _BSD_SOURCE
一经定义(任何值),开启对BSD定义的支持。同时, 只要定义了该宏,便定义了_POSIX_C_SOURCE值为199506。
极少数的情况下,当标准之间发生冲突时,显式设置该宏会导致系统向BSD定义倾斜。
2.4 _SVID_SOURCE
一经定义(任何值),头文件遵从System V接口规范(SVID)的定义。
2.5 _GNU_SOURCE
一经定义(任何值),头文件除了指定前面所有标准的定义外,还会开启对各种GNU扩展定义的支持:
在调用GNU C编译器时, 不带任何特殊选项,即默认定义了_POSIX_SOURCE、_POSIX_C_SOURCE=200809(glibc版本为2.5-2.9时,其值为200112;glibc版本低于2.4时,其值为199506)、_BSD_SOURCE以及_SVID_SOURCE。
在对个别宏进行了定义,或以其标准模式之一去调用编译器时(比如, cc –ansi 或cc – std=c99),只会按需提供定义。不过,有一个例外:若未对_POSIX_C_SOURCE另行定义,也没有以标准模式之一去调用编译器,则_POSIX_C_SOURCE的值会被定义为200809(glibc版本为2.4-2.9时,其值为200112;glibc版本低于2.4时,其值为199506)。
2.6 _POSIX_C_SOURCE、_XOPEN_SOURCE以及POSIX.1/SUS
在POSIX.1-2001/SUSv3中,仅对_POSIX_C_SOURCE和_XOPEN_SOURCE特性测试宏进行了明确定义,应用程序要符合该标准,应分别将上述两宏的值定义为200112 和 600。这就是我们最常看到的定义了. 即:
-D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600
SUSv3 明文规定将_XOPEN_SOURCE 设置为 600 时,就包含了将POSIX_C_SOURCE设置为200112时的所有特性。因此,为符合SUSv3(即XSI规范),应用程序只需要定义_XOPEN_SOURC, _POSIX_C_SOURCE可以省略.
将_POSIX_C_SOURCE值定义为200112,即表示应用程序符合POSIX.1-2001基本规范(即符合除XSI扩展规范以外的POSIX规范)。
将_XOPEN_SOURCE值定义为600,即表示应用程序符合SUSv3规范(即符合XSI规范基本规范加XSI扩展规范)。
如果需要声明 POSIX.1-2008/SUSv4,只是需要将上述两个特性测试宏的值分别定义为200809和700。即:
-D_POSIX_C_SOURCE=200809 -D_XOPEN_SOURCE=700
同样, _XOPEN_SOURCE=700包含了_POSIX_C_SOURCE=200809, 所以应用程序只需要定义_XOPEN_SOURC, _POSIX_C_SOURCE可以省略.
3. 代码移植报错implicit declaration of function处理
我们明明声明了对应的头文件,但是编译时就是要报错,说里面的某一些api没有声明. 以usleep为例, 分三步处理.
error:warning: implicit declaration of function ‘usleep’ ;disyou mean 'sleep'?[-Wimplicit-function-declaration]
3.1查看API手册
通过man xxx,来查看该函数对应的版本声明:
man usleep
...
usleep():
Since glibc 2.12:
(_XOPEN_SOURCE >= 500) && ! (_POSIX_C_SOURCE >= 200809L)
|| /* Glibc since 2.19: */ _DEFAULT_SOURCE
|| /* Glibc <= 2.19: */ _BSD_SOURCE
Before glibc 2.12:
_BSD_SOURCE || _XOPEN_SOURCE >= 500
可以看到, 对于glibc 各个版本_XOPEN_SOURCE,_POSIX_C_SOURCE , _BSD_SOURCE等对应需要的标准声明.
3.2 glibc 版本确定
可以看到在上述宏使用时, 和glibc的版本有较大关系. 因此确定具体定义之前需要先知道当前环境的glibc版本.
直接运行libc的库就可以看到:
$ /lib/x86_64-linux-gnu/libc.so.6
...
version 2.27
或者通过ldd查看:
$ ldd --version
ldd (Ubuntu EGLIBC 2.27-3ubuntu1.4) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
...
或者
getconf GNU_LIBC_VERSION
glibc 2.19
3.3 添加声明
头文件之前声明:
#define __DEFAULT_SOURCE
或者 编译选项声明:
gcc -D__DEFAULT_SOURCE ...
或者在其它构建工具声明,比如auto tool 中configure:
add_cflag -D__DEFAULT_SOURCE
总结
多man, 多搜索, 我们将收获更多知识 .