UNIX Day02

1 环境变量
2 静态库的构建和使用
3 共享库的构建和使用
4 动态加载
1 环境变量
1.1 问题
每个进程都拥有一张独立的环境变量表,其中保存着专属于该进程的所有环境变量。环境变量表是一个以空指针结尾的字符指针数组,其中每个指针指向一个格式为“变量名=变量值”的字符串,该指针数组的起始地址保存在全局变量environ中。

1.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:环境变量表

代码如下所示:

#include <stdio.h>
int main (int argc, char* argv[], char* envp[])
{
Extern char** environ;
printf ("%p, %p\n", environ, envp);
char** pp;
for (pp = envp; pp && *pp; ++pp)
printf ("%s\n", *pp);
return 0;
}
上述代码中,以下代码:

int main (int argc, char* argv[], char* envp[])
main函数的第三个参数就是环境变量表的起始地址。

上述代码中,以下代码:

Extern char** environ;

通过全局环境变量表指针environ可以访问所有环境变量。

上述代码中,以下代码:

printf ("%p, %p\n", environ, envp);

通过运行结果可以看出main函数的第三个参数envp与全局环境变量表指针environ是同一个值。

上述代码中,以下代码:

char** pp;
for (pp = envp; pp && *pp; ++pp)
    printf ("%s\n", *pp);

遍历环境变量表。

步骤二:环境变量函数

代码如下所示:

#include <stdio.h>
#include <stdlib.h>
int main()
{
printf(“PATH=%s\n”,getenv(“PATH”));
putenv(“MYNAME=zhangfei”);
printf(“MYNAME=%s\n”,getenv(“MYNAME”));
putenv(“MYNAME=zhaoyun”);
printf(“MYNAME=%s\n”,getenv(“MYNAME”));
setenv(“MYHOME”, “Zibo”, 0);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
setenv(“MYHOME”, “Yantai”, 0);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
setenv (“MYHOME”, “Dezhou”, 1);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
unsetenv(“MYNAME”);
printf(“MYNAME=%s\n”,getenv(“MYNAME”));
return 0;
}
上述代码中,以下代码:

printf("PATH=%s\n",getenv("PATH"));

获取环境变量PATH的值。

上述代码中,以下代码:

putenv("MYNAME=zhangfei");
printf("MYNAME=%s\n",getenv("MYNAME"));

设置环境变量MYNAME的值为zhangfei。

上述代码中,以下代码:

putenv("MYNAME=zhaoyun");
printf("MYNAME=%s\n",getenv("MYNAME"));

如果环境变量MYNAME的值存在,则修改为zhaoyun。

上述代码中,以下代码:

setenv("MYHOME", "Zibo", 0);
printf("MYHOME=%s\n",getenv("MYHOME"));

另一个设置环境变量函数。

上述代码中,以下代码:

setenv("MYHOME", "Yantai", 0);
printf("MYHOME=%s\n",getenv("MYHOME"));

setenv (“MYHOME”, “Dezhou”, 1);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
第三个参数为非0时,将第一个参数指定的环境变量的值修改为第二个参数的值,否则该环境变量的值保持不变。

这两个设置环境变量函数的区别是putenv的指针型参数被直接放到环境变量表中,而setenv的指针型参数则是将其目标字符串复制到环境变量表中,如下代码:

char envar[256] = “MYNAME=zhangfei”;
putenv (envar);
strcpy (envar, “MYNAME=zhaoyun”); // 修改了环境变量
char name[128] = “MYHOME”, value[128] = “Zibo”;setenv (name, value, 0);
strcpy (value, “Dezhou”); // 对环境变量毫无影响
上述代码中,以下代码:

unsetenv("MYNAME");
printf("MYNAME=%s\n",getenv("MYNAME"));

根据环境变量的名称删除环境变量。

1.3 完整代码
本案例中的完整代码如下所示:

#include <stdio.h>
#include <stdlib.h>
int main()
{
printf(“PATH=%s\n”,getenv(“PATH”));
putenv(“MYNAME=zhangfei”);
printf(“MYNAME=%s\n”,getenv(“MYNAME”));
putenv(“MYNAME=zhaoyun”);
printf(“MYNAME=%s\n”,getenv(“MYNAME”));
setenv(“MYHOME”, “Zibo”, 0);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
setenv(“MYHOME”, “Yantai”, 0);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
setenv(“MYHOME”, “Dezhou”, 1);
printf(“MYHOME=%s\n”,getenv(“MYHOME”));
unsetenv(“MYNAME”);
printf(“MYNAME=%s\n”,getenv(“MYNAME”));
return 0;
}
2 静态库的构建和使用
2.1 问题
静态库的本质就是将多个目标文件打包成一个文件。在使用时链接静态库就是将库中被调用的代码复制到调用模块中。优点是使用静态库的代码在运行时无需依赖库,且执行效率高,缺点是静态库占用空间大,库中代码一旦修改必须重新链接。

2.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:构建静态库

文件math.h代码如下所示:

#ifndef math_h
#define math_h
double add(double x, double y);
void show(double result);
#endif
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
double add(double x, double y)
{
return x + y;
}
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
void show(double result)
{
printf("%lf\n", result);
}
使用如下命令,将C源文件编译成目标文件:

gcc –c calc.c

gcc –c show.c

使用如下命令,将目标文件打包成静态库文件

ar –r libmath.a calc.o show.o

步骤二:使用静态库

文件main.c代码如下所示:

#include <stdio.h>
#include “math.h”
int main()
{
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return0;
}
使用如下命令,与静态库文件一同生成可执行文件:

gcc main.c libmath.a

也可以使用如下命令,先指定静态库文件的路径,再生成可执行文件:

export LIBRARY_PATH=静态库文件所在路径

gcc main.c –lmath //选项-l用于指定静态库名

还可以使用如下命令,生成可执行文件:

gcc main.c –lmath –L静态库文件所在路径

2.3 完整代码
本案例中的完整代码如下所示:

文件math.h代码如下所示:

#ifndef math_h
#define math_h
double add(double x, double y);
void show(double result);
#endif
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
double add(double x, double y)
{
return x + y;
}
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
void show(double result)
{
printf("%lf\n", result);
}
文件main.c代码如下所示:

#include <stdio.h>
#include “math.h”
int main()
{
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return0;
}
3 共享库的构建和使用
3.1 问题
共享库和静态库最大的不同就是,链接共享库并不需要将库中被调用的代码复制到调用模块中,相反被嵌入到调用模块中的仅仅是被调用代码在共享库中的相对地址。如果共享库中的代码同时为多个进程所用,共享库的实例在整个内存空间中仅需一份,这正是共享的意义所在,共享库的优点是占用空间小,即使修改了库中的代码,只要接口保持不变,无需重新链接,缺点是使用共享库的代码在运行时需要依赖库,执行效率略低。

3.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:共享库的构建

文件math.h代码如下所示:

#ifndef math_h
#define math_h
double add(double x, double y);
void show(double result);
#endif
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
double add(double x, double y)
{
return x + y;
}
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
void show(double result)
{
printf("%lf\n", result);
}
使用如下命令,将C源文件编译成目标文件:

gcc –c –fpic calc.c

gcc –c –fpic show.c //选项fpic是指生成位置无关代码,即调用代码通过相对地址标识被调用代码的位置,模块中的指令与该模块被加载到内存中的位置无关。

使用如下命令,将目标文件打包成共享库文件

gcc –shared calc.o show.o –o libmath.so

编译和链接也可以合并为一步完成

gcc -shared -fpic calc.c show.c -o libmath.so

步骤二:共享库的使用

文件main.c代码如下所示:

#include <stdio.h>
#include “math.h”
int main()
{
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return 0;
}
使用如下命令,与共享库文件一同生成可执行文件:

gcc main.c libmath.so

也可以使用如下命令,先指定共享库文件的路径,再生成可执行文件:

export LIBRARY_PATH=共享库文件所在路径

gcc main.c –lmath //选项-l用于指定共享库名

还可以使用如下命令,生成可执行文件:

gcc main.c –lmath –L共享库文件所在路径

运行时需要保证LD_LIBRARY_PATH环境变量中包含共享库所在的路径,可使用以下控制台命令设置

$ export LD_LIBRARY_PATH=共享库文件所在路径

3.3 完整代码
本案例中的完整代码如下所示:

文件math.h代码如下所示:

#ifndef math_h
#define math_h
double add(double x, double y);
void show(double result);
#endif
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
double add(double x, double y)
{
return x + y;
}
文件calc.c代码如下所示:

#include <stdio.h>
#include “math.h”
void show(double result)
{
printf("%lf\n", result);
}
文件main.c代码如下所示:

#include <stdio.h>
#include “math.h”
int main()
{
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return 0;
}
4 动态加载
4.1 问题
可以在程序中动态加载共享库。此时C的源程序需要调用一组特殊的函数,它们被声明于一个专门的头文件dlfcn.h,并在一个独立的库中予以实现。使用这组函数需要包含此头文件,并链接该库。

4.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:加载头文件和库

代码如下所示:

#include <stdio.h>
#include <dlfcn.h>
int main()
{
return 0;
}
上述代码中,以下代码:

#include <dlfcn.h>
需要动态加载共享库时,必须有此行代码。

步骤二:加载共享库

代码如下所示:

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main()
{
void* handle = dlopen(“libmath.so”, RTLD_LAZY);
if (! handle)
{
fprintf (stderr, “加载共享库失败!\n”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

void* handle = dlopen("libmath.so", RTLD_LAZY);

用于将共享库libmath.so载入内存并获得其访问句柄,保存在指针handle中,dlopen函数的第二个参数为RTLD_LAZY时表示延迟加载,使用共享库中的符号(如调用库中的函数)时才加载,为RTLD_NOW时表示立即加载。

步骤三:获取函数地址

代码如下所示:

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main()
{
void* handle = dlopen(“libmath.so”, RTLD_LAZY);
if (! handle)
{
fprintf (stderr, “加载共享库失败!\n”);
exit (EXIT_FAILURE);
}
double (add)(double, double) = (double()(double, double))dlsym(handle, “add”);
if (! add)
{
fprintf (stderr, “获取函数地址失败!\n”);
exit (EXIT_FAILURE);
}
void (show)(double) = (void()(double))dlsym(handle, “show”);
if (! show)
{
fprintf (stderr, “获取函数地址失败!\n”);
exit (EXIT_FAILURE);
}
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return0;
}
上述代码中,以下代码:

double (*add)(double, double) = (double(*)(double, double))dlsym(handle, "add");
if (! add)
{
    fprintf (stderr, "获取函数地址失败!\n");
    exit (EXIT_FAILURE);
}

从指针handle指定共享库中获取与给定函数名add对应的函数入口地址。

注意:所返回的函数指针是void类型,需要造型为实际函数指针类型(double()(double, double))才能调用。

步骤四:卸载共享库

代码如下所示:

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main()
{
void* handle = dlopen(“libmath.so”, RTLD_LAZY);
if (! handle)
{
fprintf (stderr, “加载共享库失败!\n”);
exit (EXIT_FAILURE);
}
double (add)(double, double) = (double()(double, double))dlsym(handle, “add”);
if (! add)
{
fprintf (stderr, “获取函数地址失败!\n”);
exit (EXIT_FAILURE);
}
void (show)(double) = (void()(double))dlsym(handle, “show”);
if (! show)
{
fprintf (stderr, “获取函数地址失败!\n”);
exit (EXIT_FAILURE);
}
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
if (dlclose (handle))
{
fprintf (stderr, “卸载共享库失败!\n”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

if (dlclose (handle))
{
fprintf (stderr, “卸载共享库失败!\n”);
exit (EXIT_FAILURE);
}
从内存中卸载共享库,所卸载的共享库未必真的会从内存中立即消失,因为其它程序可能还需要使用该库,只有所有使用该库的程序都显式或隐式地卸载了该库,该库所占用的内存空间才会真正得到释放。

注意:无论所卸载的共享库是否真正被释放,传递给dlclose函数的句柄参数都会在该函数成功返回后立即失效。

4.3 完整代码
本案例的完整代码如下所示:

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main()
{
void* handle = dlopen(“libmath.so”, RTLD_LAZY);
if (! handle)
{
fprintf (stderr, “加载共享库失败!\n”);
exit (EXIT_FAILURE);
}
double (add)(double, double) = (double()(double, double))dlsym(handle, “add”);
if (! add)
{
fprintf (stderr, “获取函数地址失败!\n”);
exit (EXIT_FAILURE);
}
void (show)(double) = (void()(double))dlsym(handle, “show”);
if (! show)
{
fprintf (stderr, “获取函数地址失败!\n”);
exit (EXIT_FAILURE);
}
printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
if (dlclose (handle))
{
fprintf (stderr, “卸载共享库失败!\n”);
exit (EXIT_FAILURE);
}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值