2009年6月28日星期日

编码转换 iconv

一、利用iconv函数族进行编码转换
在LINUX上进行编码转换时,既可以利用iconv函数族编程实现,也可以利用iconv命令来实现,只不过后者是针对文件的,即将指定文件从一种编码转换为另一种编码。
iconv函数族的头文件是iconv.h,使用前需包含之。
#include <iconv.h>
iconv函数族有三个函数,原型如下:
(1) iconv_t iconv_open(const char *tocode, const char *fromcode);
此函数说明将要进行哪两种编码的转换,tocode是目标编码,fromcode是原编码,该函数返回一个转换句柄,供以下两个函数使用。
(2) size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char
**outbuf,size_t *outbytesleft);
此函数从inbuf中读取字符,转换后输出到outbuf中,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。
(3) int iconv_close(iconv_t cd);
此函数用于关闭转换句柄,释放资源。
例子1: 用C语言实现的转换示例程序
/* f.c : 代码转换示例C程序 */
#include <iconv.h>
#define OUTLEN 255
main()
{
char *in_utf8 = "姝e?ㄥ??瑁?";
char *in_gb2312 = "正在安装";
char out[OUTLEN];
//unicode码转为gb2312码
rc = u2g(in_utf8,strlen(in_utf8),out,OUTLEN);
printf("unicode-->gb2312 out=%sn",out);
//gb2312码转为unicode码
rc = g2u(in_gb2312,strlen(in_gb2312),out,OUTLEN);
printf("gb2312-->unicode out=%sn",out);
}
//代码转换:从一种编码转为另一种编码
int code_convert(char *from_charset,char *to_charset,char *inbuf,int
inlen,char *outbuf,int outlen)
{
iconv_t cd;
int rc;
char **pin = &inbuf;
char **pout = &outbuf;
cd = iconv_open(to_charset,from_charset);
if (cd==0) return -1;
memset(outbuf,0,outlen);
if (iconv(cd,pin,&inlen,pout,&outlen)==-1) return -1;
iconv_close(cd);
return 0;
}
//UNICODE码转为GB2312码
int u2g(char *inbuf,int inlen,char *outbuf,int outlen)
{
return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen);
}
//GB2312码转为UNICODE码
int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}
例子2: 用C++语言实现的转换示例程序
/* f.cpp : 代码转换示例C++程序 */
#include <iconv.h>
#include <iostream>
#define OUTLEN 255
using namespace std;
// 代码转换操作类
class CodeConverter {
private:
iconv_t cd;
public:
// 构造
CodeConverter(const char *from_charset,const char *to_charset) {
cd = iconv_open(to_charset,from_charset);
}
// 析构
~CodeConverter() {
iconv_close(cd);
}
// 转换输出
int convert(char *inbuf,int inlen,char *outbuf,int outlen) {
char **pin = &inbuf;
char **pout = &outbuf;
memset(outbuf,0,outlen);
return iconv(cd,pin,(size_t *)&inlen,pout,(size_t *)&outlen);
}
};
int main(int argc, char **argv)
{
char *in_utf8 = "姝e?ㄥ??瑁?";
char *in_gb2312 = "正在安装";
char out[OUTLEN];
// utf-8-->gb2312
CodeConverter cc = CodeConverter("utf-8","gb2312");
cc.convert(in_utf8,strlen(in_utf8),out,OUTLEN);
cout << "utf-8-->gb2312 in=" << in_utf8 << ",out=" << out << endl;
// gb2312-->utf-8
CodeConverter cc2 = CodeConverter("gb2312","utf-8");
cc2.convert(in_gb2312,strlen(in_gb2312),out,OUTLEN);
cout << "gb2312-->utf-8 in=" << in_gb2312 << ",out=" << out << endl;
}
二、利用iconv命令进行编码转换
在LINUX上进行编码转换时,既可以利用iconv函数族编程实现,也可以利用iconv命令来实现,只不过后者是针对文件的,即将指定文件从一种编码转换为另一种编码。
iconv命令用于转换指定文件的编码,默认输出到标准输出设备,亦可指定输出文件。
用法: iconv [选项...] [文件...]
有如下选项可用:
输入/输出格式规范:
-f, --from-code=名称 原始文本编码
-t, --to-code=名称 输出编码
信息:
-l, --list 列举所有已知的字符集
输出控制:
-c 从输出中忽略无效的字符
-o, --output=FILE 输出文件
-s, --silent 关闭警告
--verbose 打印进度信息
-?, --help 给出该系统求助列表
--usage 给出简要的用法信息
-V, --version 打印程序版本号
例子:
iconv -f utf-8 -t gb2312 aaa.txt >bbb.txt
这个命令读取aaa.txt文件,从utf-8编码转换为gb2312编码,其输出定向到bbb.txt文件。
小结: LINUX为我们提供了强大的编码转换工具,给我们带来了方便。
glibc带了一套转码函数iconv,使用方便,可识别的码很多,如果程序需要涉及到编码之间的转换,可考虑用它。
iconv命令的用法。
$ iconv --list # 显示可识别的编码名称
$ iconv -f GB2312 -t UTF-8 a.html > b.html #
转换GB2312编码的文件a.html为UTF-8编码,存入b.html
$ iconv -f GB2312 -t BIG5 a.html > b.html #
转换GB2312编码的文件a.html为BIG5编码,存入b.html
iconv编程涉及到以下glibc库的调用:
#include <iconv.h>
iconv_t iconv_open(const char *tocode, const char *fromcode);
int iconv_close(iconv_t cd);
size_t iconv(iconv_t cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft);
在使用iconv转码的时候,首先用iconv_open获取转码句柄,然后调用iconv转码,转完了后调用iconv_close关闭句柄。
iconv函数中:
参数cd是用iconv_open调用返回的转码句柄;
参数inbuf指向需要转码的缓冲区;
参数inbytesleft是inbuf所保存的需要转码的字节数;
参数outbuf存放转码结果;
参数outbytesleft存放outbuf空间的大小。
如果调用成功,iconv返回转换的字节数(不可逆转调用的字节数,可逆转调用的字节数不包括在内)。否则返回-1,并设置相应的errno。
iconv逐步扫描inbuf,每转换一个字符,就增加inbuf,减少inbytesleft,并将结果存入outbuf,结果字节数存入outbytesleft。遇到下列情况将停止扫描并返回:
1. 多字节序列无效,这时候errno为EILSEQ,*inbuf指向第一个无效的字符;
2. 有字节留在inbuf尚未转换,errno为EINVAL;
3. outbuf空间不够,errno为E2BIG;
4. 正常转换完备。
对于iconv函数,还有两种调用情况:
1.
inbuf或者*inbuf为NULL,outbuf和*outbuf不为NULL,iconv会设置转换状态为初始状态,并保存转换序列到*outbuf。如果outbuf空间不足,errno会设置为E2BIG,返回(size_t)(-1);
2.
inbuf或者*inbuf为NULL,outbuf或者*outbuf也为NULL,iconv设置转换状态为初始状态。
iconv命令的使用固然方便,可是如果转换过程中如果遇到问题则会停止转换,有时候我们希望跳过不能转换的字节序列继续转换。以下的一段程序能实现这种功能。
/**
* siconv.cpp - A simple way to demostrate the usage of iconv calling
*
* Report bugs to marchday2004@yahoo.com.cn
* July 15th, 2006
*/
#include <iconv.h>
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef DEBUG
#define TRACE(fmt, args...) fprintf(stderr, "%s:%s:%d:"fmt, \
__FILE__, __FUNCTION__, __LINE__, ##args)
#else
#define TRACE(fmt, args...)

printf()格式化输出详解

printf的格式控制的完整格式:
% - 0 m.n l或h 格式字符
下面对组成格式说明的各项加以说明:
①%:表示格式说明的起始符号,不可缺少。
②-:有-表示左对齐输出,如省略表示右对齐输出。
③0:有0表示指定空位填0,如省略表示指定空位不填。
④m.n:m指域宽,即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。对数值型的来说,未指定n时,隐含的精度为n=6位。
⑤l或h:l对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型。
---------------------------------------
格式字符
格式字符用以指定输出项的数据类型和输出格式。
①d格式:用来输出十进制整数。有以下几种用法:
%d:按整型数据的实际长度输出。
%md:m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。
%ld:输出长整型数据。
②o格式:以无符号八进制形式输出整数。对长整型可以用"%lo"格式输出。同样也可以指定字段宽度用"%mo"格式输出。
例:
main()
{ int a = -1;
printf("%d, %o", a, a);
}
运行结果:-1,177777
程序解析:-1在内存单元中(以补码形式存放)为(1111111111111111)2,转换为八进制数为(177777)8。
③x格式:以无符号十六进制形式输出整数。对长整型可以用"%lx"格式输出。同样也可以指定字段宽度用"%mx"格式输出。
④u格式:以无符号十进制形式输出整数。对长整型可以用"%lu"格式输出。同样也可以指定字段宽度用"%mu"格式输出。
⑤c格式:输出一个字符。
⑥s格式:用来输出一个串。有几中用法
%s:例如:printf("%s", "CHINA")输出"CHINA"字符串(不包括双引号)。
%ms:输出的字符串占m列,如字符串本身长度大于m,则突破获m的限制,将字符串全部输出。若串长小于m,则左补空格。
%-ms:如果串长小于m,则在m列范围内,字符串向左靠,右补空格。
%m.ns:输出占m列,但只取字符串中左端n个字符。这n个字符输出在m列的右侧,左补空格,注意:如果n未指定,默认为0。
%-m.ns:其中m、n含义同上,n个字符输出在m列范围的左侧,右补空格。如果n>m,则自动取n值,即保证n个字符正常输出,注意:如果n未指定,默认为0。
如果是sprintf(desc, "%m.ns", sour); 如果desc空间够的话,会在%m.ns 串
的结尾自动补null字符,不同于strncpy。
例如 :sprintf(desc, "%.3s", "123456");
desc如果空间>=4字节的话,第4个字节将是null字符。
⑦f格式:用来输出实数(包括单、双精度),以小数形式输出。有以下几种用法:
%f:不指定宽度,整数部分全部输出并输出6位小数。
%m.nf:输出共占m列,其中有n位小数,如数值宽度小于m左端补空格。
%-m.nf:输出共占n列,其中有n位小数,如数值宽度小于m右端补空格。
⑧e格式:以指数形式输出实数。可用以下形式:
%e:数字部分(又称尾数)输出6位小数,指数部分占5位或4位。
%m.ne和%-m.ne:m、n和"-"字符含义与前相同。此处n指数据的数字部分的小数位数,m表示整个输出数据所占的宽度。
⑨g格式:自动选f格式或e格式中较短的一种输出,且不输出无意义的零。
---------------------------------------
关于printf函数的进一步说明:
如果想输出字符"%",则应该在"格式控制"字符串中用连续两个%表示,如:
printf("%f%%", 1.0/3);
输出0.333333%。
---------------------------------------
对于单精度数,使用%f格式符输出时,仅前7位是有效数字,小数6位.
对于双精度数,使用%lf格式符输出时,前16位是有效数字,小数6位.
----------------------------------------------------------------------------------------------------------------------
printf()函数是格式输出函数,请求printf()打印变量的指令取决与变量的类型.例如,在打印整数是使用%d符号,在打印字符是
用%c
符号.这些符号被称为转换说明.因为它们指定了如何不数据转换成可显示的形式.下列列出的是ANSI C标准peintf()提供的各种转换说明.
转换说明及作为结果的打印输出
%a 浮点数、十六进制数字和p-记数法(C99)
%A    浮点数、十六进制数字和p-记法(C99)
%c    一个字符 
%d    有符号十进制整数 
%e    浮点数、e-记数法
%E    浮点数、E-记数法
%f    浮点数、十进制记数法  
%g    根据数值不同自动选择%f或%e.
%G    根据数值不同自动选择%f或%e.
%i 有符号十进制数(与%d相同)
%o    无符号八进制整数
%p    指针    
%s    字符串
%u    无符号十进制整数
%x    使用十六进制数字0f的无符号十六进制整数 
%X    使用十六进制数字0f的无符号十六进制整数
%%    打印一个百分号
//还有一个特殊的格式%*.*
,这两个星号的值分别由第二个和第三个参数的值指定
printf("%.*s \n", 8, "abcdefgggggg");
printf("%*.*f \n", 3,3, 1.25456f);
使用printf ()函数
 printf()的基本形式: printf("格式控制字符串",变量列表);
#include<cstdio>
int main()
{
//for int
int i=30122121;
long i2=309095024l;
short i3=30;
unsigned i4=2123453;
printf("%d,%o,%x,%X,%ld,%hd,%u\n",i,i,i,i,i2,i3,i4);//如果是:%l,%h,则输不出结果
printf("%d,%ld\n",i,i2);//试验不出%ld和%d之间的差别,因为long是4bytes
printf("%hd,%hd\n\n\n",i,i3);//试验了%hd和%d之间的差别,因为short是2bytes
//for string and char
char ch1='d';
unsigned char ch2=160;
char *str="Hello everyone!";
printf("%c,%u,%s\n\n\n",ch1,ch2,str);//unsigned
char超过128的没有字符对应
//for float and double,unsigned and signed can not be used with double
and float
float fl=2.566545445F;//or 2.566545445f
double dl=265.5651445;
long double dl2=2.5654441454;
//%g没有e格式,默认6位包括小数点前面的数,
//%f没有e格式,默认6位仅只小数点后面包含6位
//%e采用e格式,默认6位为转化后的小数点后面的6位
printf("%f,%e,%g,%.7f\n",fl,dl,dl,dl);
printf("%f,%E,%G,%f\n",fl,dl,dl,dl);//%F is wrong
printf("%.8f,%.10e\n",fl,dl);
printf("%.8e,%.10f\n\n\n",fl,dl);
//for point
int *iP=&i;
char *iP1=new char;
void *iP2;//dangerous!
printf("%p,%p,%p\n\n\n",iP,iP1,iP2);
//其他知识:负号,表示左对齐(默认是右对齐);%6.3,6表示宽度,3表示精度
char *s="Hello world!";
printf(":%s: \n:%10s: \n:%.10s: \n:%-10s: \n:%.15s: \n:%-15s:
\n:%15.10s: \n:%-15.10s:\n\n\n",
s,s,s,s,s,s,s,s);
double ddd=563.908556444;
printf(":%g: \n:%10g: \n:%.10g: \n:%-10g: \n:%.15g: \n:%-15g:
\n:%15.10g: \n:%-15.10g:\n\n\n",
ddd,ddd,ddd,ddd,ddd,ddd,ddd,ddd);
//还有一个特殊的格式%*.*
,这两个星号的值分别由第二个和第三个参数的值指定
printf("%.*s \n", 8, "abcdefgggggg");
printf("%*.*f \n", 3,3, 1.25456f);
return 0;
}

静态库和共享库库的定位搜索路径(编译时和运行时)

库文件在连接(静态库和共享
库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般
Linux 系统把 /lib 和 /usr/lib
两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到
库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:
在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。
在 /etc/ld.so.conf 文件中添加库的搜索路径。
将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择
添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:
/usr/X11R6/lib
/usr/local/lib
/opt/lib
需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是
因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件
/etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache
是一个非文本的数据文件,不能直接编辑,它是根据 /etc/ld.so.conf
中设置的搜索路径由 /sbin/ldconfig
命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig 命令要以 root
权限执行)。
因此,为了保证程序执行时对库的定位,在 /etc/ld.so.conf
中进行了库搜索路径的设置之后,还必须要运行 /sbin/ldconfig 命令更新
/etc/ld.so.cache 文件之后才可以。ldconfig
,简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件缓存到/etc/ld.so.cache
以供使用。因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下
/sbin/ldconfig使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用
的,结果编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂computer蠢猪一个。
在程序连接时,对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过
-L 参数显式指定。因为用 -L
设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。
前面已经说明过了,库搜索路径的设置有两种方式:
在环境变量 LD_LIBRARY_PATH 中设置
在 /etc/ld.so.conf 文件中设置。
其中,第二种设置方式需要 root 权限,以改变 /etc/ld.so.conf 文件并执行
/sbin/ldconfig 命令。而且,当系统重新启动后,所有的基于 GTK2
的程序在运行时都将使用新安装的 GTK+ 库。不幸的是,由于 GTK+
版本的改变,这有时会给应用程序带来兼容性的问题,造成某些程序运行不正常。为了避免出现上面的这些情况,在
GTK+
及其依赖库的安装过程中对于库的搜索路径的设置将采用第一种方式进行。这种设置方式不需要
root 权限,设置也简单:
$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH
可以用下面的命令查看 LD_LIBRAY_PATH 的设置内容:
$ echo $LD_LIBRARY_PATH
至此,库的两种设置就完成了。
交叉编译时候如何配置连接库的搜索路径
交叉编译的时候不能使用本地(i686机器,即PC机器,研发机器)机器上的库,但是在做编译链接的时候默认的是使用本地库,即/usr/lib,/lib两个目录。因此,在交叉编译的时候,要采取一些方法使得在编译链接的时候找到需要的库。
首先,要知道:编译的时候只需要头文档,真正实际的库文档在链接的时候用到。
(这是我的理解,假如有不对的地方,敬请网上各位大侠指教)
然后,讲讲如何在交叉编译链接的时候找到需要的库。
(1)、交叉编译时候直接使用-L和-I参数指定搜索非标准的库文档和头文档的路径。例如:
arm-linux-gcc test.c -L/usr/local/arm/2.95.3/arm-linux/lib
-I/usr/local/arm/2.95.3/arm-linux/include
(2)、使用ld.so.conf文档,将用到的库所在文档目录添加到此文档中,然后使用ldconfig命令刷新缓存。
(3)、使用如下命令:
$ export
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/arm/2.95.3/arm-linux-lib
参见《ld.so.conf 文档和PKG_CONFIG_PATH变量》这篇文章。
通过环境变量LD_LIBRARY_PATH指定动态库搜索路径(!)。
通过设定环境变量LD_LIBRARY_PATH也可以指定动态库搜索路径。当通过该环境变量指定多个动态库搜索路径时,路径之间用冒号":"分隔。
不过LD_LIBRARY_PATH的设定作用是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。(LD_LIBRARY_PATH
的缺陷和使用准则,可以参考《Why LD_LIBRARY_PATH is bad》
)。通常情况下推荐还是使用gcc的-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路
径查找库,避免了使用LD_LIBRARY_PATH环境变量查找。
(4)、交叉编译时使用软件的configure参数。例如我编译minigui-1.3.3,使用如下配置:
#!/bin/bash
rm -f config.cache config.status
./configure --build=i686-linux --host=arm-linux --target=arm-linux \
CFLAGS=-I/usr/local/arm/2.95.3/arm-linux/include \
LDFLAGS=-L/usr/local/arm/2.95.3/arm-linux/lib \
--prefix=/usr/local/arm/2.95.3/arm-linux \
--enable-lite \
--disable-galqvfb \
--disable-qvfbial \
--disable-vbfsupport \
--disable-ttfsupport \
--disable-type1support \
--disable-imegb2312py \
--enable-extfullgif \
--enable-extskin \
--disable-videoqvfb \
--disable-videoecoslcd
这里我配置了CFLAGS和LDFLAGS参数,这样一来,我就不用去修改每个Makefile里-L和-I参数了,也不用再去配置LD_LIBRARY_PATH或改写ld.so.conf文档了。
Linux下动态库使用小结
1. 静态库和动态库的基本概念
静态库,是在可执行程序连接时就已经加入到执行码中,在物理上成为执行程序的一部分;使用静态库编译的程序运行时无需该库文件支持,哪里都可以用,但是生
成的可执行文件较大。动态库,是在可执行程序启动时加载到执行程序中,可以被多个可执行程序共享使用。使用动态库编译生成的程序相对较小,但运行时需要库
文件支持,如果机器里没有这些库文件就不能运行。
2. 如何使用动态库
如何程序在连接时使用了共享库,就必须在运行的时候能够找到共享库的位置。linux的可执行程序在执行的时候默认是先搜索/lib和/usr/lib这
两个目录,然后按照/etc/ld.so.conf里面的配置搜索绝对路径。同时,Linux也提供了环境变量LD_LIBRARY_PATH供用户选择
使用,用户可以通过设定它来查找除默认路径之外的其他路径,如查找/work/lib路径,你可以在/etc/rc.d/rc.local或其他系统启动
后即可执行到的脚本添加如下语句:LD_LIBRARY_PATH
=/work/lib:$(LD_LIBRARY_PATH)。并且LD_LIBRARY_PATH路径优先于系统默认路径之前查找(详细参考《使用
LD_LIBRARY_PATH》)。
不过LD_LIBRARY_PATH的设定作用是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。(LD_LIBRARY_PATH
的缺陷和使用准则,可以参考《Why LD_LIBRARY_PATH is bad》
)。通常情况下推荐还是使用gcc的-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路
径查找库,避免了使用LD_LIBRARY_PATH环境变量查找。
3.库的链接时路径和运行时路径
现代连接器在处理动态库时将链接时路径(Link-time
path)和运行时路径(Run-time
path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。比如我们做嵌入式
移植时#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3
(work/lib/zlib下是交叉编译好的zlib库),将target编译好后我们只要把zlib库拷贝到开发板的系统默认路径下即可。或者通过-
rpath(或-R )、LD_LIBRARY_PATH指定查找路径。
小问题:
1.编译时的-L选项是否影响LD_LIBRARY_PATH的值?
举一个实例:
当前文件夹结构如下:
test.c tools/
tool下有tool.c tool.h my_err.h 以及由此生成的libtool.so
tool下编译生成库文件
gcc -Wall -g -shared -o tool.so tool.c
在当前文件夹引用:
gcc -Wall -g –o test.c -Ltools -ltool
编译不报错,但是运行加载的时候就出现cannot open shared object file。
如果将该库文件拷贝到/usr/lib下就没有错误,正常运行。
说明编译时的-L选项并不影响环境变量LD_LIBRARY_PATH,-L只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径,系统还是会到默认路径下查找该程序所需要的库。
有关动态链接库需要注意的相关事项
1.有关动态链接库需要注意的相关事项。
如果碰到有GTK 的程序中 有 <gtk\gtk.h>
那么需要加入动态链接库的参数
$ gcc test_a.c test_b.c iptbox.c -fPIC -shared -o iptbox.so `pkg-config
--cflags --libs gtk+-2.0
生成出来的.so 动态链接库 需要放在usr/lib 目录下
cp libipt.so /usr/lib
2、在运行AP时出现错误,可以使用下列方式来查找问题:
strace ./test
3、查找文件夹下路径:
find /usr/lib -name libipt.so
4、解压缩命令:
tar -zxvf 文件名
5、解RPM包的方式:
#rpm -ivh scim-1.4.7.4-1benX.src.rpm
#cd /usr/src/dorado/SPECS
#rpmbuild -bb scim.spec
6、目录权限打开:
chmod 777 root(目录名称)
7、linux 下安装软件三步骤:
a. ./configure
b. make
c. make install
8、更新库引用:
ldconfig
9、查找是否有安装该AP.
# whereis scim-ppenglish

多线程编程之重点--使用DSP/BIOS时选择线程类型的参考方法

了解以下这些,对在DSP/BIOS上设计多线程应用程序就是轻而易举的事件。
DSP/BIOS支持多种类型线程,每种类型线程且有不同执行及抢先点,下图表(点击可见大图)列出了这些线程的一些特点。

线程选择的一些原则
对线程选择基于对下面这个问题的回答:在应用程序中,这个线程是否具有实时性(线程必须在严格的时间段内结束,以及不允许被抢先)?该线程的执行时间相对其它部分来说是否少很多?
1. 严格的实时性
如果线程的执行需要严格的实时性,而线程执行需要的时间又很少时,可以使用硬件中断或时钟函数来完成。CLK时钟函数也是在硬件中断中执行的。
DSP芯片一般都有一个或多个片上定时器外设,并能产生周期性的等间隔硬件中断。DSP/BIOS内核通常会使用其中的一个定时器作为自己的系统时钟。在大多数TMS320系列的DSP中,DSP/BIOS内核的CLK模块都可以提供接近一个机器周期的时钟。CLK函数将由片上的定时器中断触发,并在该中断的中断服务子程序(ISR)中调用执行。
硬件中断线程由外部的同步事件(或AD转换器的中断信号)触发而执行。HWI函数或中断服务子程序(ISR)将在中断发生后执行。由于硬件中断具有最高优先级,所以HWI函数具有严格的实时性。在DSP/BIOS的应用程序中,HWI中断函数能够处理频率为200KHZ的中断(具体瑾因工作频率不同或ISR的复杂度而可能有差异)。所以当处理时间间隔在2-5US之间时,可以使用硬件中断线程。还有一个原因,就是硬件中断线程有极小的中断潜伏期。所谓中断潜伏期,指的是从中断触发到中断服务子程序的第一条指令开始执行之间的时间。当然,中断潜伏期越长,表示中断的响应越不及时,这一点对处理的实时性来讲相当重要。硬件中断线程只可以用汇编语言或C语言和汇编语言的混合体,但通常我推荐使用汇编语言。
在硬件中断处理函数中,可以将SWI软件中断对象,或TSK任务对象放到执行队列中,但它们必须等到所有的硬件中断线程结束后才能允许。所以,应该尽量减小HWI硬件中断函数,以便中断服务序尽快结束。另外,DSP在响应硬件中断后会自动屏蔽所有的中断,因此可以通过设置调用HWI_enter的参数开放一些中断,允许这些中断抢先执行。在系统存在多个硬件中断线程,而其中一个的中断服务程序稍长时,这种方法非常有效。
当硬件中断函数调用某些PIP管道模块的API函数时,如PIP_alloc,PIP_free,PIP_get,PIP_put等,读写通知函数也将在中断响应中调用执行。
2.部分实时性
有时会遇到这种情况,只有一部分线程的执行要求有严格的实时性,其余部分执行时间较长而且对处理时间没有太多的要求。显然,这时仅仅使用硬件中断线程是不理想的。
一般情况下,要求严格实时性的处理都是由硬件中断触发,所以我们显然会使用硬件中断服务程序来响应这个中断,以便满足对实时性的要求。更为能用的做法是,可以增加SWI软件中断或TSK任务线程来完成一些非实时性的处理任务。这样可以减少中断的潜伏期,提高响应实时性请求的能力。
所以,将代码分为多个线程,只要求有严格实时性的任务放到HWI硬件中断中完成,而其余处理都放到优先级较低的SWI或TSK中完成。这些SWI或TSK线程有如下两个特点:
* 能完成实时处理任务,但允许处理时间相对较长;
* 允许被其他线程抢先。
那么这些处理函数究竟该选择使用SWI软件中断线程还是TSK任务线程呢?请先看下面的几种情况:
* 处理函数需要等待某些资源(例如数据、上位机的信号等),以便继续运行。
* 处理函数与其他线程之间有复杂的联系或数据共享要求。
* 希望处理函数有自己的堆栈空间而不是系统共用的堆栈空间。
* 处理函数中用到LCK,MBX或SEM这几个内核模块。
* 希望在线程创建、删除、退出、就绪、切换时调用钩子函数。
当上面的情况有任何一种存在时,建议使用TSK任务线程;相反,当没有上面的一种情况出现时,建议选用SWI软件中断。任务线程与软件中断线程有以下几点区别:
* 任务在执行时可以被挂起(suspended,可以理解为暂停),直到必需的条件(如数据准备好、信号同步等)得到满足,才可以继续运行。在任务被挂起时,即任务处于暂停或阻塞(blocked)状态,其他任务或线程得以执行,而软件中断做不到这点。
* DSP/BIOS内核提供了一组用于任务之间通信和同步的数据结构,包括旗语、邮箱和锁。这些数据结构无法用于软件中断之间的通信和同步。
* 每个任务都有自己的堆栈区,而软件中断使用共享的系统堆栈。
* 当任务被创建、删除、退出或切换时,都可以调用特殊函数(钩子函数)。这些钩子函数可以用于保存任务的环境面不仅仅是CPU的寄存器。
* 任务线程的优先级比软件中断线程低,而比后台IDL线程高。任务线程内部又被划分为16个优先等级。用户任务的优先级可以是从1-15级,最低的0级内核保留给LOOP循环使用。
软件中断线程往往伴随着硬件中断的发生。每当有硬件中断发生时,会触发HWI硬件中断服务函数,而软件中断的触发是靠SWI软件中断API函数调用实现的。例如,在硬件中断服务函数中调用SWI_post函数,可以触发一个SWI软件中断。
软件中断线程适合于处理一些发生速率较低的任务,或对实时性要求不苛刻的任务。SWI软件中断可以帮助硬件中断函数将一些非严格实时性的处理放到低优先级的线程中,以减少硬件中断的响应时间。这点非常重要,因为在硬件中断响应过程中往往处于关中断状态。
与硬件中断线程一样,软件中断线程总会一直执行下去,直到结束。当程序将软件中断放到CPU的执行队列中排队等待时,软件中断函数需要的所有数据应该是准备好了的。SWI软件中断对象中的邮箱提供了一种判断资源准备情况的方法。在其他线程运行时,一个软件中断线程不能够等待资源变为有效。
软件中断的优先级比任务线程高,但比硬件中断低。软件中断线程比任务线程使用更少的存储器,因为所有的软件中断线程都使用系统的堆栈空间。任务线程提供了额外的能力,如暂停(阻塞)和切换,而软件中断线程不支持这些。
3.周期性的服务
虽然有时线程的执行有实时性限制,但是处理任务却有足够长的时间执行而且能够允许其他线程抢先,即被其他线程中断去处理更紧急的任务。这种情况可以参照上面处理部分实时性线程的原则进行处理。但其中有一个较特殊的应用,就是需要周期性地或在固定的时间间隔内完成处理任务,而一般情况下,时间间隔比处理任务所需的时间要长得多。对于这种要求,我建议使用DSP/BIOS内核提供的PRD周期性函数来完成。
周期函数以系统时钟为基础,默认情况下,系统时钟由片上定时器驱动,也可以配置为其他事件来驱动,如IO事件等。周期性函数属于SWI软件中断中的PRD_swi对象,通常该对象的优先级较低,可以通过DSP/BIOS的配置工具提高它的优先级。
所有的PRD周期函数具有同样的SWI软件中断优先级,因此一个周期函数是不能抢先其他周期函数的。当存在一些处理时间较长的其他SWI或TSK线程时,为了保证所有PRD周期函数正常执行,除了尽量缩短周期函数本身的处理时间外,还需要提高整个PRD周期函数的优先级,以保证周期函数被允许在系统时钟触发时能够抢先那些低优先级的线程。
如果多个周期函数被同一个系统时钟触发,它们将按照创建时的顺序执行。另外,若周期函数需要被挂起或暂停时,应该使用TSK线程来完成同样的服务。
4.不需要实时性
有时线程只需要在后台进行一些无关紧要的处理,比如收集统计数据、与自己交换检测数据等。这时,我们建议使用IDL线程。
IDL等待循环是DSP/BIOS内核中最低优先级的线程。当应用程序的主函数返回后,DSP/BIOS内核调用该应用程序所用到的DSP/BIOS模块的初始化启动代码。在这些启动代码结束后,便进入这个IDL等待循环。该循环是一个无限循环,它不停地调用IDL后台对象中的所有函数。每个函数在前一个函数结束后依次运行。这些IDL函数不断被运行,直到被更高优先级的线程抢先。由于IDL函数的优先级最低,运行的时间得不到保证,所以IDL函数不适合于有实时性要求的任何处理任务。它通常用在一些不需要中断(当然也不能有中断)的非实时设备查询中,或用于监视系统状态,或其他一些后台活动。
DSP目标系统与主机的DSP/BIOS分析工具之间的通信是在后台IDL循环中完成的。这可以保证DSP/BIOS的分析工具不会干扰应用程序的运行。如果目标板上的DSP太忙,以致无法运行后台IDL线程时,DSP/BIOS分析工具会停止接收目标板信息。所以我们经常会看到,当目标板DSP的处理任务太繁重或因错误落入一个死循环时,CCS的所有操作都会变得迟缓起来。

只放一只羊!

"阿尔迪"超市的所有者,是德国80多岁的阿尔布莱希特兄弟,它是全世界公认的零售
业航母,目前的身价已达400亿欧元。在美国,阿尔迪的年销售额现已达到48亿美元,它计划
今后每年开40家分店,到2010年把总量扩展到1000家。同时,阿尔迪还在北美收购了那些被
沃尔玛挤兑而破产的零售商。
  尽管年销售额达2000亿欧元的美国"沃尔玛"公司,销售额是阿尔迪的6倍,但阿尔迪每
年经销的单件商品的总价值却超过4000万欧元,竟是沃尔玛的30倍。因阿尔迪只销售不超过
1000种商品,比沃尔玛少15万种。
  德国人给阿尔迪起了一个通俗的名字——"穷人店"。二战后,阿尔布莱希特兄弟从盟
军战俘营回到家乡,开办了一家食品零售店。因为当时人们只想用最低的价格购买生活必需
品,所以兄弟俩把价格定得很低。他们说:"我们的业务发展基础只有一个:最低的价格!
"
  阿尔迪能从一个三流小店发展成为世界上最成功的零售商,人们曾有无数个猜想,认为
它一定有非常机密的营销方案,而且也会把它严严实实地隐藏起来。但出乎人们意料,它的
制胜法宝只有两个字:"简单"!就是"简单"这两个字,令许多企业始终无法模仿。
  它简单到什么程度,从以下几个方面就可以看出:
  不做广告
  阿尔迪所有的商场,只有每周一期八开的《阿尔迪信息报》放在超市入口,对下周的新
上柜货品作个介绍,由顾客随意自取浏览,便于顾客按照自己的意愿选择喜欢的商品。除此
之外,绝不做其他任何形式的商品宣传。
  商品单调
  商场里只放着简单的600~700种商品。货品装在纸箱里堆在光秃秃的货架板上,价目表悬
在头顶,而不是贴在商品包装上;瓶装芦笋和沙丁鱼罐头挤破了纸箱,手纸只有两种牌子,
腌菜只有一种,每种商品只提供一种选择,即同类商品之中最好的品牌,每一种商品都只有
一种规格的包装。商品都是能够迅速带出店铺的,包括罐头食品、纸袋包装食品、快餐食品
、一些新鲜果蔬和冰冻食品等。除少量日用品、食品设有货架、冷柜外,其他商品均由店员
打开纸箱包装,任顾客自取。
  员工人数少
  每一家阿尔迪超市的雇员都少于10名,他们都身兼数职。商场里没有现代化超市设备,
没有使用条形码扫描仪和读卡机等现代化设备,坚持使用最简单的收款机,而且只收现金。
没有人员为顾客提供装袋的服务,店员对商品价格倒背如流,他们心算和录入速度令人惊叹
,整个顾客交款的过程非常快捷。
 不收尾数钱
  在阿尔迪,商品价格的尾数都是0或5。他们开始时经过测试,发现营业员找零钱的时间
会影响销售,如果将找零钱的时间去掉,可以减少营业员数量,又可以多卖出为数不少的商
品。于是阿尔迪决定,尾数0.05~0.09马克的商品,按0.05马克收款;而尾数0~0.04马克的
商品,按0收款。这样做,既提高了商店员工的工作效率,又降价吸引了更多的顾客。
  
  不举债经营
  阿尔迪从不举债经营,扩张都是用已产生的利润来进行,因此风险很低。曾是阿尔迪的
管理人员迪特?布兰德斯说:"阿尔迪的一个原则是:不仓促展开业务,而是先打好牢固的基
础。基础一旦有了,它会行动得很快。"
  员工执行力强
  阿尔迪的用人原则是能力+高薪,非常注重员工队伍的精干和年轻化。与其他零售企业相
比,阿尔迪的薪水要高出10%~20%。工作出色的员工有很多升迁机会,升任超市经理的即
配给公车,升任部门主任的年薪可达到20万马克。因此,阿尔迪吸引了大批年轻的优秀人才
加盟。
  有人这样形象地总结了阿尔迪成功的秘诀:只放一只羊!因为,无数个想放一群羊的人
,到了最后,却一只羊也没剩下,原因就是他们被无尽的贪欲挤垮,而最后失掉了一群羊。
所以,不管你做什么工作,在做事前,还是先衡量一下自己的能力,看看你到底适合放几只
羊?

货币政策奠定中长期底部——金学伟

在硬通货时代,每隔一段时间就会发生一次经济衰退和萧条。那是真正的衰退,不是今天我们通常所说的经济增长率下降,而是国民生产总值实实在在的减少。这种由国民生产总值周期性的正增长和负增长交替的经济波动,被称为古典式波动。与此相反,近几十年来的经济波动主要体现为增长率的高低起伏,由此产生的波动称为增长型波动。
产生古典式波动的原因很多。其中最主要的原因之一出在货币供应量上。货币作为商品价值尺度、财富储存手段、商品流通媒介与支付手段,它的总量必需和经济总量相匹配:有多大的经济总量就需要有多大的货币总量,否则整个经济运行就会失序,正常的循环就会中断。而在硬通货时代,货币就是金、银、铜,它的增长受到矿山开采、冶炼等生产环节的制约,这些环节的年均增长率决定了货币的年均增长率。当经济的发展过于迅猛,或货币生产的环节出现梗阻时,整个社会就会产生钱不够的问题,正常的商品流通与循环也就无法进行下去。最终的结果只能是经济总量降下来,以适应货币总量。
我国北宋年间曾发生过两次经济危机,其原因都在货币短缺。北宋时曾短暂出现过的"交子"——世界上最早的纸币形式,就是应这一需要产生的。明朝末年张献忠窜入四川,建立大西国。这个农民天真地以为有了银子就有一切,把整个大西国的银子搜刮一空,使四川经济陷入万劫不复的深渊,最后连张献忠本人想举办一次"国宴",也空有一大堆银子,却买不到所需的肉。清朝的康熙、雍正年间也发生过一次钱不够的问题,不过当时缺的主要是铜钱,国家拿不出那么多铜来铸造铜钱,以满足商品流通的需要。为此朝廷争论了好多年,直到雍正上台,这位务实皇帝拍板:不怕人家说大清朝铜钱不纯,不要担心因此损害大清朝面子,以60%的铜加40%的锡铸成铜钱,才解决了这个问题。
在纸币时代,上述问题已不存在,只要需要,货币可随时印刷。但由此产生的新的问题就是货币发行量的过剩,以及由此带来的长期通货膨胀。当今世界,一年的通胀率也许就超过了以往100年。
通胀发生于现在,它的根源却在过去。以我国为例,从1986年到2006年的20年间,我国GDP总量的年均复合增长率为16.26%,而货币供应量(M2)的年均复合增长率为22.84%。如果说,经济总量增长1%,货币总量也增长1%属于正常所需,那么,这一数字表明在过去20年里,我国货币供应量平均每年要超过正常所需6.58个百分点。如以近10年来看,则从1996年到2006年,GDP的年均增长率为11.39%,而货币总量的年均增长率为16.72%,平均每年超过正常所需5.33个百分点。当今的通货膨胀实际上就是长期积累的货币超量发行结果。2002年,笔者曾发表文章,针对当时众多经济学家要求政府加大货币发行量,以解决所谓退货紧缩问题,根据货币总量和经济总量以及流通中现金的运行态势预测:从2003年起,我国就将进入新一轮通胀,在此之前,任何加大货币发行量的建议,都会加剧日后治理通胀的难度。当前的通胀,实际上就是过去10多年间超量发行货币的总爆发或集中体现。这样的通胀,任何一个国家和政府都无法从根本上消除它,因为时间不能倒退,我们无法回到过去再重新来过。只有当社会经济总量、资产总量与货币总量在新的物价基础上达到新的平衡,通胀才能基本消除。在此之前,唯一的办法就是通过一定手段,延长平衡的时间,以放慢通胀速度,也就是以延长通胀时间的方法,来防止因平衡速度过快而导致恶性通胀的出现。如果我们不能清楚地认识到这一点,如果把控制通胀提到太高的高度,如果为反通胀而过度收缩银根,以致货币发行量不能和现有的经济总量相匹配,满足不了经济运转的需要,那么很可能会出现通胀没有下去,而GDP总量却下去了,最终反而导致通胀率的居高不下,甚至猛升。
笔者以为,近两年我国央行的货币政策已出现了相关苗头:2007年,我国名义GDP总量增长了17.76%,而当年货币供应量只增长了16.9%;今年上半年,名义GDP的同比增长率为22.33%,货币供应量的同比增长率只有17.4%。货币供应量的增长率已经连续1年半低于经济总量的增长率。这样的货币政策已无法维持下去。
 如果说在硬通货时代,经济总量的周期性下降是不可避免的;那么,在纸币时代,周期性的通货膨胀同样也是不可避免的,能够避免的只有恶性通胀以及经济总量的衰退。如果说在硬通货时代,通胀比衰退更可怕,因为通胀会加剧衰退;那么在纸币时代,衰退比通胀更可怕,因为衰退可加剧通胀。这已被越来越多的人所认识。
( 作者为上海智晟投资管理有限公司首席经济顾问)

办公族五个坏习惯让身体长石头

肾结石一跃成为全民最为关注的疾病之一。但是要预防结石病,仅仅关注奶粉或者牛奶的食用安全远远不够,而需要被关怀的也绝不仅止于婴幼儿群体。相关资料显示,近年来结石的发病率逐年升高,发病群体涵盖所有年龄层,并有年轻化的趋势,其中办公室白领占据相当大的比例。
  结石为何盯上办公族?有关专家指出,这与办公族工作繁忙、工作节奏紧张等客观因素有关,更与其不良的生活习惯紧密相连,其中五个坏习惯,是致病的罪魁祸首。
  不吃早餐
  不吃早餐的办公族不在少数,有的是因为早上赶着上班,来不及吃,有的是为了减肥,不愿意吃。这一看似寻常的生活习惯却是导致结石病的重要原因。据健康863网的保健专家介绍,不吃早餐诱发结石病的几率相当大。这是因为人体在经过夜晚的长时间睡眠后,需要在早晨摄入食物来补充能量,胆作为消化系统的一部分,会在早上分泌胆汁,做好消化食物的准备。人如果不吃早餐,胆就没有食物可消化,胆汁就会长时间停留在胆囊,长此以往,胆汁会淤积在胆囊或肠道,胆固醇自胆汁中析出就易形成结石。
  防石计:不要给自己任何借口不吃早餐。吃早餐耽搁不了太多时间,也不会让你长胖。即使是简单的喝点牛奶、吃片面包,也能起到预防作用。
  不爱喝水
  不少办公族因为工作节奏快,喝水的时间相应较少,还有为数不少的人即使有时间喝水,量也不够大,而这正是诱发结石病的另一重要原因。专家表示,喝水少会减少人体尿液,并会使尿液过度浓缩,尿中盐类的浓度增高,沉淀增多,就容易形成肾结石或尿路结石。
  防石计:多喝水可以增加排尿量,稀释尿液,降低尿内晶体浓度,冲洗尿路,有利于预防结石形成及促使结石排出,所以即便不口渴,个人每天也应喝水2000毫升以上,并最好选择清水。
  不喜欢运动
  运动量少也会导致结石的产生。而办公族的工作性质注定了坐的时间非常长,运动的机会少,下班后很多人也不爱运动。专家指出,人如果很少运动,一方面不利于钙质吸收,会增加尿液中的钙盐成分,产生肾结石或尿路结石;另一方面人体腹壁会松弛,引致内脏下垂,压迫胆管,使胆汁排泄不畅而淤积,从而形成胆结石。

  防石计:加强运动,不要"坐等"结石的形成。在办公室工作两个小时左右,就应站起来做些简单的放松运动,业余也应保证一定的运动量,每天的运动时间应在30分钟以上。
  吃得太油
  办公族因为工作繁忙,应酬较多等原因,在外就餐的机会较多,吃大鱼大肉的频率较高,并容易暴饮暴食,尤其是男性,往往吃得过于油腻,而这正是结石病的重要诱因。因为高脂肪、高蛋白食物会增高胆汁中的胆固醇含量,形成结石;动物内脏、肉类代谢也会产生尿酸,导致结石。
  防石计:管住嘴,限制食物中胆固醇的含量,少吃或忌吃富含胆固醇的食物,如肥肉、动物内脏、鱼籽、蟹黄、蛋黄等。多吃新鲜瓜果和蔬菜以及大蒜、洋葱、香菇、黑木耳等具有降低胆固醇作用的保护性食物。
  吃得太素
  吃得太油腻容易得结石,吃得太素同样容易长石头。办公室MM大多热衷减肥,不少人信奉素食主义,以蔬菜为主食。专家提醒,虽然蔬菜富含维生素、矿物质和膳食纤维,但是,菠菜、芹菜、番茄、竹笋等蔬菜中同时还富含草酸,过量食用可能从尿中析出而形成结石,其与豆制品或者钙片中的钙结合后,也容易形成结石。
  防石计:含草酸盐较多的蔬菜在吃之前应先焯一下,然后再过一遍水,以减少草酸摄入量;可多吃碱性蔬菜和苹果,综合酸碱度;宜适量摄入高脂肪、高蛋白食物,均衡饮食结构,减小结石风险。