Linux可移植性,如何才能实现软件预期的灵活性.不同硬件和软件平台用户都可以不做任何修改或者只做极少的修改就可以使用软件.
常见的可移植性需求有:
a.平台之间的可移植,例如unix和linux以及mac
b.linux不同发行版之间
使用可移植函数库并充分使用各种自动配置工具编写的linux可以满足以上要求.
1.抽象层
在linux内核之上穿件标准化函数库,例如glibc.内核只负责处理繁琐的硬件细节.系统工具可以绕开函数库直接使用内核的接口.
2.linux标准化
a.LSB (linux标准化规范 Linux Standards Base)
主要基于POSIX以及单一UNIX规范.
它的目标是:在符合LSB特定版本要求的指定主机架构上,在不同的发行版和同一发行版的不同版本之间提供正真的二进制兼容.
LSB是一个二进制ABI标准.它使用rpm包作为可移植软件包格式概念基础,并对LSB兼容包的命名和内部内容就行了约束.
3.linux软件包开发注意事项
当编写自己的应用程序时,需要在设计中考虑将依赖于哪些外部软件和函数库,哪些软件将由自己交付,如何支持不满足应用程序要求的发行版.
4.可移植的源代码
以上都是linux可移植的泛泛之谈.代码可移植才最重要.
在linux编写的软件不仅仅要运行在相同平台发行版之间可以二进制兼容,还应该在大多数提供正确函数库和必备条件的系统中成功编译.编译过程必须小心的确定不同软件环境之间的差异.
为了辅助不同编译环境之间的源代码级的可移植性,人们创建了gnu的自动化工具autotools.autoconf,autoheader,libtool,automake等许多脚本和工具一起工作构成gnu编译系统.这个工具自动运行一系列的测试以获得对用户所处硬件和软件环境的理解,然后再确定是否可能在这一特定的软件环境中编译软件.当准备号合适的源代码集之后,就可以使用你可能早已熟悉的configure,make,make install,来编译软件了.
a.gnu自动化工具
我们这里组织一个案例hello.c,库libhello,以及configure判定大端小端
hello.c:
#include "hello.h"
int main()
{
print_message("hello world!\n");
#ifdef WORDS_BIGENDIAN
printf("this is a big endian\n");
#endif
return 0;
}
help.c:
include
void print_message(char *msg)
{
printf("the message is %s \n",msg);
}
hello.h:
void print_message(char *msg)
执行以下命令:
gcc -shared -fPIC -o libhello.so help.c
gcc -lhello -L. hello.c
export LD_LIBRARY_PATH=`pwd`
./hello
1)gnu autoconf
configure用于建立包含一个特定系统动态信息的头文件,这些动态信息用于源代码测试.例如,一个编译周期通常会创建一个头文件config.h.它包含很多#define声明对应已经经过测试的特性.如果gnu configyre确定本地的环境适合编译软件,那么这些定义将有助于增加软件的灵活性.因为他们允许必要的时候进行条件代码编译.
以下为一个configure.in的例子:
##这一句是autoconf需要的最低版本
AC_PREREQ([2.68])
##设置软件包的名字,版本号,以及作者邮箱
AC_INIT([hello], [1.0], [[email protected]])
##以下是设置包含进configure中的特征和测试需要.
#设置主机编译还是为另一个目标系统编译
AC_CANONICAL_SYSTEM
#测试目录下是否存在hello.c,用于确保用户在正确的目录下.
AC_CONFIG_SRCDIR([hello.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.测试是否存在c编译器
AC_PROG_CC
#测试标准头文件的可用性
AC_HEADER_STDC
#测试大端小端
AC_C_BIGENDIAN
#测试其他的头文件
AC_CHECK_HEADERS
#指定gnu autoconf运行之后输出的文件
AC_OUTPUT(makefile)
最后执行autoconf
configure脚本将从configure.in文件中自动生成基于可移植的shell代码.
2)gnu automake
automake用于建立makefile文件.makefile.am为输入文件.通常在根目录下有一个,各个子目录下也有一个.顶层的只是告诉automake进入src目录:
根目录:
SUBDIRS=src
子目录:
#生成的目标
bin_PROGRAMS=hello
#源文件
hello_SOURCES=hello.c
#告诉automake需要一个额外的库libhello,并提供了函数库本身的编译依赖关系.
hello_LDADD=libhello.la
lib_LTLIBRARIES=libhello.la
libhello_la_SOURCES=help.c
makefile.am只需要告诉automake,源文件以及依赖关系.
3)gnu libtool
被用来处理静态和共享库.
下面是手动处理过程:
1)autoscan 扫描整个目录
autoscan 生成configure.scan,改名为configure.ac或者configure.in
2)aclocal
该命令扫描configure.in 生成aclocal.m4,这个文件中包含有automake需要的定义.
3)autconf
读取configure.in 生成configure
4)autoheader
准备config.h.in ,在configure的时候生成config.h
5)libtool
执行libtoolize,准备函数库
5.国际化
1.字符集的使用
在过去的20年中,人们指定了一套针对字符编码和表示方式的标准.软件厂商厌倦了事先他们自己的字符编码系统来处理自己产品的多语言.他们共同合作产生了一个通用的解决反感,该反感可以普遍用户处理未来软件不断增长的对语言本地化的需求.这一工作的主要成狗之一就是unicode标准.
使用unicode字符为linux编写软件比较简单,但是你要牢记linux最初是建立在8位的us ascii字符编码上的.因此很多系统工具还不能处理unicode.但是这并不是一个大问题,我们可以在linux上是使用utf-8编码来存储这些字符.
utf-8解决了在linux中使用unicode可能出现的问题.unicode多字节编码会包含一些特殊的单字节字符,如\0.他们在文本或者路径中会引起很大的问题.例如null字符在linux中c函数库中有特别的含义,它是字符串的结束标志,如果标准c库要处理的unicode字符串使用这个字符,就会产生问题.而utf-8以向后兼容的方式,(ASCII是utf-8的有效字符)使用多个单字节进行存储.
utf-8根据字符本身的编码改变其宽度.哪些适用于7为的ascii字符集的字符将继续使用一个字节,其他字符则需要一个转译字符来表明存在一个多字节编码的utf-8字符.utf-8字符最大长度为6个字节.
为图形化桌面编写软件的时候涉及使用已有的对unicode有很好支持的函数库.
2.宽字符
unicode是一种用来表示字符集的编码系统.通过使用它,不同的程序可以处理不同形状和大小的字符串.从内部看,一个程序可以使用它所喜欢的任何数据类型来存储数据.但在大多数情况下程序员通过使用比技术要求略大的内存来减轻他们的负担,因为这样可以有利于处理字符串和字符.
现代linux系统支持宽字符概念.一个宽字符是一个多字节的数据类型.用于在内部表示如一个unicode字符串中的每一个字符.在大多数系统中一个宽字符占4个字节.或在大多数32位机上有一个内存字宽.使用宽字符需要包含系统头文件wchar.h.
例子:
#include
#include
#include
#include
int main()
{
char *sc="short characters";
wchar_t *wc=L"wider characters";
printf("%ls are more universally userful than %s,",wc,sc);
printf("but they do use more space (%d as opposed to %d bytes).\n",wcslen(wc)*sizeof(wchar_t),strlen(sc));
}
输出:wider characters are more universally userful than short characters,but they do use more space (64 as opposed to 16 bytes).
注意:宽字符的声明前面需要加上"L".
3.使用用户的语言
限制软件在全球推广的阻力来自于使用母语.你需要为程序和用户之间提供一个翻译.函数库gettext可以很好的完成,并且可以通过将翻译外包给那些有语言天赋的人.
库函数gettext可以简单的利用一个普通的ASCII文本字符串并使用它作为某个软件对应翻译表格的索引.每个语言翻译被存储在.po文件中,可以和软件一起分发.一些程序可能包含数十个保存在/usr/share/locale/中的可用的中文翻译
.例子:
主程序:
#include
#include
#include
#include
//以下为gettext要求的宏,定义以方便xgettext
#define _(String) gettext (String)
#define gettext_noop(String) String
#define N_(String) gettext_noop (String)
int main(int argc,char **argv)
{
setlocale(LC_ALL,"");
bindtextdomain("gettext_example",".");//注意这里绑定的是当前目录
textdomain("gettext_example");//注意此处是确认使用哪个翻译文件
printf("%s\n",gettext("hello,world!"));//注意输出的放在gettext下
return 0;
}
gettext_example 文件:
msgid "hello,world!"
msgstr "sunjianxi"
操作步骤:
生成po翻译文件:xgettext -ao gettext_example.po gettext_example.c
将po文件翻译为二进制文件: msgfmt -o en_US/LC_MESSAGES/gettext_example.mo gettext_example.po
注意这里:
export LANG=en_US
翻译文件需要放在en_US/LC_MESSAGES下面