在linux系统中,可将多个目标文件打包成库文件,以便在编程时随时调用,而不必重新编写或定义,这种包称为库函数。库文件是一些预先编译好的函数的集合,那些函数都是按照可再使用的原则编写的。它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数(curses库)等。
1.基础知识:
linux下GCC在编译程序时要经历预处理,编译,汇编和连接四个阶段。
(1). 预处理阶段,主要处理#include和#define,它把#include包含进来的.h 文件插入到#include所在的位置,生成.i文件;
(2). 编译阶段,是最重要的阶段,在这个阶段GCC首先检查语法然后把文件转换成汇编程序,生成.s文件;上面这两步的输出文件都是文本文件,我们可以用诸如cat的文本处理等命令阅读这些输出文件;
(3). 汇编阶段,把*.s文件翻译成二进制机器指令文件*.o,需要反汇编工具如GDB的帮助才能读懂它;
(4). 连接阶段,gcc在这个阶段把所有的*.o文件连接成一个可执行文件,库文件的连接也在这步完成。
2. 标准库与非标准库
库函数可分为标准与非标准(自定义)库两大类。
(1)标准库文件是公用的,系统中的任何用户都可以利用这些库函数,一般保存在/lib或者/usr/lib目录里,并以头文件的方式提供包含调用。编译时要告诉C语言编译器(更确切地说是链接程序)应去查找哪些库文件,默认情况下,它只会查找标准库文件。
标准库在使用时,gcc等编译程序能够自动连接,所以在只需要包含其定义的头文件即可,如libc.a。libc.a为标准C函数库,它包含了诸如内存管理或者输入输出操作的基本函数。
(2)非标准库在连接时,必须加上-lname(name为去掉lib和尾部的.a或.so后的库名)参数;非标准库可以放在任意目录中,一般放于当前目录中,但当放置在非系统默认搜索路径中时,需要用-Ldir(dir为路径名称)指定搜索路径。
例如,数学函数并不是C标准库的组成部分,他们是由数学库/usr/lib/libm.a所定义的,因而在使用该库中的数学函数时,除了用#include<math.h>将头文件/usr/include/math.h加入到程序文件中,还要明确的用gcc的-lm选项来连接这个库(数学库libm.a放置在系统默认的库搜索路径/usr/lib中,因而不需要-Ldir参数)。
3. 静态库与共享库
函数库一般分为静态和共享(也称动态库)两种格式,库文件必须遵守一定的命名规则,库文件的名字永远以lib打头,随后是说明函数库情况的部分(比如用c表示这是一个 C语言库,而m表示这是一个数学运算库等)。文件名的最后部分以一个句点(.)开始,然后给出这个库文件的类型,.a为静态库文件,.so和.sa为共享型库文件。无论动态库还是静态库都需要用.o文件来生成。
一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库(动态库或静态库)搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中(如修改环境变量),也可以在程序连接时,通过-L参数来指定。
但由于在运行时,程序还需要连接动态库,因而,对于动态库,最好还是将库的位置添加到库的搜索路径之中或是把所用的库拷贝到系统默认的库搜索目录中。静态库则无此限制。
(1)静态库
静态库也叫做档案(archive),文件名按惯例都以.a结尾,比如C语言标准库/usr/lib/libc.a,X11库/usr/X11R6/lib/libX11.a等。它所包含的成员是若干.o文件,静态库中的各个成员(.o文件)没有特殊的存在格式,仅仅是一个.o文件的集合。在连接时,静态库的文件代码会被拷贝到可执行文件中,所以静态连接的可执行文件一般比较大一些。
在Linux(Unix)中使用工具“ar”对静态库进行维护管理,创建静态库用ar命令,在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
# ar -rc libmyhello.a hello.o
静态库的缺点是,如果我们在同一时间运行多个程序而它们又都使用着来自同一个函数库里的函数时,内存里就会有许多份同一函数的备份,在程序文件本身也有许多份同样的备份。这会消耗大量宝贵的内存和硬盘空间。
(2)共享库
也称动态库,许多UNIX系统支持共享库,它同时克服了静态库在内存和硬盘方面的无谓消耗。链接时,动态库的代码不会被加入可执行文件中,而是在程序被执行的时候加载。
共享库的默认存放位置和静态库是一样的,但有着不同的文件后缀。在一个典型的 Linux系统上,C语言标准库的共享版本是/usr/lib/libc.so N,其中的N是主版本号。当静态库和动态库同名时,gcc命令将优先使用动态库。
可用gcc来创建动态库,在系统提示符下键入以下命令得到动态库文件libmyhello.so。
# gcc -shared -fPCI -o libmyhello.so hello.o
4. 对比
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此在程序运行时还需要动态库存在。
假如程式是在编译时加载库文档的,就是使用了静态库。假如是在运行时加载目标代码,就成为动态库。换句话说,假如是使用静态库,则静态库代码在编译时就拷贝到了程式的代码段,程式的体积会膨胀。假如使用动态库,则程式中只保留库文档的名字和函数名,在运行时去查找库文档和函数体,程式的体积基本变化不大。
静态库的原则是“以空间换时间”,增加程式体积,减少运行时间;动态库则是“以时间换空间”,增加了运行时间,但减少了程式本身的体积。
5. 总结
标准静态库或共享库,路径和库名都不需显示指定;
非标准静态库,必须显示指定库名,不在默认路径中时需指定路径;
非标准共享库,编译时:必须指定库名,不在默认路径中需指定路径名,
运行时,不在默认路径中时需要将库位置添加到库搜索路径中或拷贝库到默认路径中。
6. gcc相关命令
使用要点:
⑴在gcc 的-I参数后加上库头文件的路径;
⑵在gcc 的-L参数后加上库文件所在目录;
⑶在gcc 的-l参数后加上库文件名,但是要去掉lib和.a扩展名,比如库文件名是libtest.a 那么参数就是-ltest。
2. linux中的内存函数
(1). getpagesize(获得内存分页大小)
#include <unistd.h>
size_t getpagesize(void);
返回分页的大小,单位为字节。此为系统分页大小,不一定和硬件分页大小相同。
(2). mmap(建立内存映射)
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
将某个文件内容映射到内存中, 对该内存区域的存取即是直接对该文件内容的读写
(3). munmap(解除内存映射)
3. 时间函数
(4). ftime(获得目前的时间和日期)
#include <sys/timeb.h>
int ftime(struct timeb*tp);
将目前时间日期由tp所指的结构返回。tp结构定义为:
struct timeb{
time_t time;//从1970年1月1日至今的秒数
unsigned short millitm;// 毫秒
short timezone;
short dstflag;
};
(5). gettimeofday(获得目前的时间)
#include <sys/time.h>
#include <unistd.h>
int gettimeofday(struct timeval*tv,struct timezone *tz);
settimeofday(设置目前时间)
4.
(6). ffs(在一整型数中查找第一个值为真的位)
#include <string.h>
int ffs(int i);
ffs会由低位至高位,判断参数i字节中的每一位,将最先出现位的值为1的位置返回。
5. 字符串函数
(7). #include <strings.h>
char *index(const char *s, int c);
char *rindex(const char *s, int c);
查找字符c在字符串s中出现的位置。
与strchr类似
(8). strcasecmp(忽略大小写比较字符串)
#include <strings.h>
int strcasecmp(const char *s1, const char *s2);
(9). strfry 随机重组字符串内容的字符串
#define _GNU_SOURCE
#include <string.h>
char *strfry(char *string);
由于strfry会改变参数string字符串内容,所以参数string必须是指向可以写入的内存地址。
int strncasecmp(const char *s1, const char *s2, size_t n);
6. 加密函数
(10). crypt 将密码或数据加密
#define _XOPEN_SOURCE
#include <unistd.h>
char *crypt(const char *key, const char *salt);
使用gcc编译时需要加入-lcrypt
(11). getpass 取得密码输入
#include <unistd.h>
char *getpass( const char *prompt);
7.数据结构函数
(12). #include <stdlib.h>
void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
8. 进程间通信函数
(13).mkfifio 建立具名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
(14). pipe 建立管道
#include <unistd.h>
int pipe(int fields[2]);
pipe()建立管道,并将文件描述词有参数fields数组返回。filedes[0]为管道读取端,filedes[1]是管道写入端
(15). popen 建立管道I/O
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
函数传入值:Command:指向的是一个以null结束符结尾的字符串,这个字符串包含一个shell命令,并被送到/bin/sh以-c参数执行,即由shell来执行type:“r”:文件指针链接到commond的标准输出,即该命令的结果产生输出 “w”:文件指针连接到command的标准输入,即该命令的结果产生输入
函数返回值:成功:文件流指针 出错:-1
(16). connect 建立socket连线
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);