PILE读书笔记_进程环境
进程是操作系统运行程序的一个实例, 也是操作系统分配资源的单位。 在Linux环境中, 每个进程都有独立的进程空间, 以便对不同的进程进行隔离, 使之不会互相影响。
atexit函数
#include <stdlib.h>
int atexit(void (*function)(void));
atexit用于注册进程正常退出时的回调函数。 若注册了多个回调函数, 最后的调用顺序与注册顺序相反;类似于栈。
使用atexit注册的退出函数是在进程正常退出时, 才会被调用。 这里的正常退出是指, 使用exit退出或使用main中最后的return语句退出。 若是因为收到信号而导致程序退出,atexit注册的退出函数则不会被调用。
putenv函数
#include <stdlib.h>
int putenv(char *string);
putenv用于增加或修改当前的环境变量。 string的格式为“名字=值”。 如果当前环境变量没有该名称的环境变量, 则增加这个新的环境变量; 如果已经存在, 则使用新值。如果非要用putenv来设置环境变量, 就必须要保证参数是一个长期存在的内容。 因此, 只能选择全局变量、 常量或动态内存等。
比如:
#include <stdlib.h>
#include <stdio.h> static void set_env_string(void)
{
char test_env[] = "test_env=test";
if ( != putenv(test_env)) {
printf("fail to putenv\n");
}
printf("1. The test_evn string is %s\n", getenv("test_env"));
} static void show_env_string(void)
{
printf("2. The test_env string is %s\n", getenv("test_env"));
} int main()
{
set_env_string();
show_env_string();
return ;
}
运行结果:

改为全局变量之后就正确了:
#include <stdlib.h>
#include <stdio.h> char test_env[] = "test_env=test"; static void set_env_string(void)
{
if ( != putenv(test_env)) {
printf("fail to putenv\n");
}
printf("1. The test_evn string is %s\n", getenv("test_env"));
} static void show_env_string(void)
{
printf("2. The test_env string is %s\n", getenv("test_env"));
} int main()
{
set_env_string();
show_env_string();
return ;
}
运行结果:

setenv函数
#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);
为了杜绝上面putenv的缺点,可以用setenv函数
参数说明:
(1)name: 要加入的环境变量名称。
(2)value: 该环境变量的值。
(3)overwrite: 用于指示是否覆盖已存在的重名环境变量。
#include <stdlib.h>
#include <stdio.h>
static void set_env_string(void)
{
setenv("test_env", "test", );
printf("1. The test_evn string is %s\n", getenv("test_env"));
} static void show_env_string(void)
{
printf("2. The test_env string is %s\n", getenv("test_env"));
} int main()
{
set_env_string();
show_env_string();
return ;
}
运行结果:

动态库和静态库
静态库在链接阶段, 会被直接链接进最终的二进制文件中, 因此最终生成的二进制文件体积会比较大,但是可以不再依赖于库文件。 而动态库并不是被链接到文件中的, 只是保存了依赖关系, 因此最终生成的二进制文件体积较小, 但是在运行阶段需要加载动态库。

静态库
在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:
l 静态库对函数库的链接是放在编译时期完成的。
l 程序在运行时与函数库再无瓜葛,移植方便。
l 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
下面编写一些简单的四则运算C++类,将其编译成静态库给他人用,头文件如下所示:
|
StaticMath.h头文件 |
|
#pragma once class StaticMath { public: StaticMath(void); ~StaticMath(void);
static double add(double a, double b);//加法 static double sub(double a, double b);//减法 static double mul(double a, double b);//乘法 static double div(double a, double b);//除法
void print(); }; |
Linux下使用ar工具、Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:

通过上面的流程可以知道,Linux创建静态库过程如下:
l 首先,将代码文件编译成目标文件.o(StaticMath.o)
|
g++ -c StaticMath.cpp |
注意带参数-c,否则直接编译为可执行文件
l 然后,通过ar工具将目标文件打包成.a静态库文件
|
ar -crv libstaticmath.a StaticMath.o |
生成静态库libstaticmath.a。

大一点的项目会编写makefile文件(CMake等等工程管理工具)来生成静态库,输入多个命令太麻烦了。
编写使用上面创建的静态库的测试代码:
|
测试代码: |
|
#include "StaticMath.h" #include <iostream> using namespace std;
int main(int argc, char* argv[]) { double a = 10; double b = 2;
cout << "a + b = " << StaticMath::add(a, b) << endl; cout << "a - b = " << StaticMath::sub(a, b) << endl; cout << "a * b = " << StaticMath::mul(a, b) << endl; cout << "a / b = " << StaticMath::div(a, b) << endl;
StaticMath sm; sm.print();
system("pause"); return 0; } |
Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。
# g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath

l -L:表示要连接的库所在目录
l -l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a或.so来确定库的名称。
动态库
为什么需要动态库,其实也是静态库的特点导致。
l 空间浪费是静态库的一个问题。

l 另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

动态库特点总结:
l 动态库把对一些库函数的链接载入推迟到程序运行的时期。
l 可以实现进程之间的资源共享。(因此动态库也称为共享库)
l 将一些程序升级变得简单。
l 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
编写四则运算动态库代码:
|
DynamicMath.h头文件 |
|
#pragma once class DynamicMath { public: DynamicMath(void); ~DynamicMath(void);
static double add(double a, double b);//¼Ó·¨ static double sub(double a, double b);//¼õ·¨ static double mul(double a, double b);//³Ë·¨ static double div(double a, double b);//³ý·¨ void print(); }; |
l 首先,生成目标文件,此时要加编译器选项-fpic
|
g++ -fPIC -c DynamicMath.cpp |
-fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。
l 然后,生成动态库,此时要加链接器选项-shared
|
g++ -shared -o libdynmath.so DynamicMath.o |
-shared指定生成动态链接库。

其实上面两个步骤可以合并为一个命令:
|
g++ -fPIC -shared -o libdynmath.so DynamicMath.cpp |
编写使用动态库的测试代码:
|
测试代码: |
|
#include "../DynamicLibrary/DynamicMath.h"
#include <iostream> using namespace std;
int main(int argc, char* argv[]) { double a = 10; double b = 2;
cout << "a + b = " << DynamicMath::add(a, b) << endl; cout << "a - b = " << DynamicMath::sub(a, b) << endl; cout << "a * b = " << DynamicMath::mul(a, b) << endl; cout << "a / b = " << DynamicMath::div(a, b) << endl;
DynamicMath dyn; dyn.print(); return 0; } |
引用动态库编译成可执行文件(跟静态库方式一样):
|
g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath |
然后运行:./a.out,发现竟然报错了!!!

可能大家会猜测,是因为动态库跟测试程序不是一个目录,那我们验证下是否如此:

发现还是报错!!!那么,在执行的时候是如何定位共享库文件的呢?
1) 当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统动态载入器(dynamic linker/loader)。
2) 对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib 目录找到库文件后将其载入内存。
如何让系统能够找到它:
l 如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。
l 如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:
n 编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
n 运行ldconfig ,该命令会重建/etc/ld.so.cache文件
我们将创建的动态库复制到/usr/lib下面,然后运行测试程序。

本文中静态库和动态库部分参考自:
http://www.cnblogs.com/skynet/p/3372855.html
PILE读书笔记_进程环境的更多相关文章
- PILE读书笔记_标准I/O
在学习和分析标准I/O库的同时, 可以重点与Linux的I/O系统调用进行比较. stdin. stdout和stderr都是FILE类型的文件指针, 是由C库静态定义的, 直接与文件描述符0. 1和 ...
- PILE读书笔记_文件I/O
open函数 int open(const char *pathname, int flags, mode_t mode); 参数说明: (1)pathname: 表示要打开的文件路径 (2)flag ...
- PILE读书笔记_基础知识
程序的构成 Linux下二进制可执行程序的格式一般为ELF格式. 我们可以用readelf命令来读取二进制的信息. ELF文件的主要内容就是由各个section及symbol表组成的. 下面来分别介绍 ...
- APUE读书笔记:进程控制
重点函数:fork,exit,_exit 一.fork 函数原型: #include <unistd.> pid_t fork(void) 函数说明:fork函数将创建一个子进程,该函数调 ...
- erl_0019《硝烟中的erlang》 读书笔记005 “进程信息"
对一个运行中的Erlang系统来说,进程绝对是重要的组成部分.正因为进程是所有运行实体的基础,因此会想去了解它们的更多信息.幸运的是,VM提供了大量的可用信息,其中有些可以安全使用,有些在生产环境中使 ...
- APUE 学习笔记(五) 进程环境
1.main函数 C程序总是从main函数开始执行,当内核执行C程序时,在调用main函数之前先调用exec函数从内核获取命令行参数和环境变量值 2.进程终止 正常终止: (1)在main函数内执 ...
- UNIX环境高级编程笔记之进程环境
本章讲的都是一些非常基础的知识,目的是为了下一章讲进程控制做铺垫,所以,本章就不做过多的总结了,直接看图吧.
- 『TensorFlow』读书笔记_进阶卷积神经网络_分类cifar10_上
完整项目见:Github 完整项目中最终使用了ResNet进行分类,而卷积版本较本篇中结构为了提升训练效果也略有改动 本节主要介绍进阶的卷积神经网络设计相关,数据读入以及增强在下一节再与介绍 网络相关 ...
- 『TensorFlow』读书笔记_进阶卷积神经网络_分类cifar10_下
数据读取部分实现 文中采用了tensorflow的从文件直接读取数据的方式,逻辑流程如下, 实现如下, # Author : Hellcat # Time : 2017/12/9 import os ...
随机推荐
- [LA 3942] Remember the Word
Link: LA 3942 传送门 Solution: 感觉自己字符串不太行啊,要加练一些蓝书上的水题了…… $Trie$+$dp$ 转移方程:$dp[i]=sum\{ dp[i+len(x)+1]\ ...
- 【找规律】URAL - 2069 - Hard Rock
题解及证明:http://www.cnblogs.com/StupidBoy/p/5241258.html #include<cstdio> #include<algorithm&g ...
- 【KM算法】HDU2255-奔小康赚大钱
KM算法的裸体.O(n^4)的模板,实际上在增广路径的时候依然有冗余,可以用bfs优化到O(n^3). #include <iostream> #include <cstdio> ...
- 找出分数最高的前两个学生 Exercise05_09
import java.util.Scanner; /** * @author 冰樱梦 * 时间:2018年下半年 * 题目:找出分数最高的前两个学生 * */ public class Exerci ...
- Exercise03_03
import java.util.Scanner; public class LinearEquation { public static void main(String[] args){ doub ...
- 每天一个linux命令12之top
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.下面详细介绍它的使用方法.top是一个动态显示过程,即可以通过用户按键来不断刷新 ...
- TOleDBMSSQLConnectionProperties驱动MSSQL数据库
TOleDBMSSQLConnectionProperties驱动MSSQL数据库 为了让MORMOT可以驱动所有版本的MSSQL,需要改用SQLOLEDB,因为所有的WINDOWS操作系统里面都提供 ...
- 【开源类库学习】MBProgressHUD(提示框)
新博客: http://www.liuchendi.com MBProgressHUD是一个开源类库,实现了各种样式的提示框, 下载地址:https://github.com/jdg/MBProgre ...
- libCurl 简单使用
curl easy 的使用步骤 curl_easy_init() curl_easy_setopt() curl_easy_perform() curl_easy_cleanup() ------- ...
- Swift,结构体与类
1.结构体(小的类就是用struct来写) struct arrow{ var x=0,y=0 } 2.类(常用) class a{ var a=10 var b=20 } var b=a() //实 ...