主要内容:main 函数、进程的终止、命令行参数的分析、环境变量、C 程序的存储空间布局、库、函数跳转
int main(int argc,char**argv);//C/C++程序总是从main函数开始执行
//当内核执行C程序时,使用exec函数,再调用main之前先调用一个特殊的启动例程,可执行文件将此例程指定为程序的起始地址
//启动例程从内核获取命令行参数和环境变量值
1、从main返回
int main(int argc,char**argv){
return 0;
}
2、调用exit
void exit(int status);
0377为exit实际的返回值
status的实参与上& 0 1111 1111
status -128~127
exit能带回的范围为
3、调用_exit 或_Exit , _exit与_Exit是系统调用exit是库函数
4、最后一个线程从其启动例程返回
5、从最后一个线程调用 pthread_exit
6、实际的 main 函数形式可能是这样调用的
(main(argc,argv)) exit
exit 与_exit 的区别
1、调用abort
2、接收到一个信号并终止
3、最后一个线程对取消请求做出响应
在 exit 函数调用时,总会执行标准 I/O 库的清理关闭操作、对于所有打开流调用 fclose 函数,会造成所有缓冲中的数据都会被冲洗
一个进程可以登记多至 32 个函数,将有 exit 自动调用,称为这些函数为终止处理程序,终止处理程序每等级一次,就会被调用一次
#include <stdlib.h>
int atexit(void (*function)(void));//成功时返回0否则返回非0
//exit钩子函数 demo
#include <iostream>
#include <stdlib.h>
using namespace std;
void hook1(void)
{
<< "hook1" << endl;
cout }
void hook2(void)
{
<< "hook2" << endl;
cout }
void hook3(void)
{
<< "hook3" << endl;
cout }
int main(int argc, char **argv)
{
(hook1);
atexit(hook2);
atexit(hook3);
atexitreturn 0;
}
// output: hook3 hook2 hook1
相关函数
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
#include <getopt.h>
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
基本用法
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
for (int i = 0; i < argc; i++)
{
<< argv[i] << endl;
cout }
return 0;
}
//./ main - a 12 - l 4343
/*
./main
-a
12
-l
4343
*/
//或者
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
for (int i = 0; argv[i] != nullptr; i++) // POSIX与TSOC要求argv末尾必须有个null
{
<< argv[i] << endl;
cout }
return 0;
}
getopt 只支持短参数,如-a -b
optstring 格式
-a即可,不加参数
单个字符a 表示选项a没有参数 格式:: 表示选项b有且必须加参数 格式:-b 100或-b100,但-b=100错
单字符加冒号b2冒号c:: 表示选项c可以有,也可以无 格式:-c200,其它格式错误 单字符加
其他参数及其变量
1、变量optarg,这是库中定义好的一个变量,保存了参数后面的值,比如有参数-b 100,当getopt函数返回b的时候,此时optarg存储的就是100
2、getopt函数的返回值:char类型的参数所对应的int值,比如-b 100,getopt函数返回的就是字符b对应的int值
3、另外几个与optarg类似的几个变量
() 时的下一个 argv指针的索引。
optind —— 再次调用 getopt
optopt —— 最后一个未知选项。()打印出错信息,则只要将全域变量opterr设为0即可。 opterr —— 如果不希望getopt
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
#include <iostream>
#include <unistd.h>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
while (1)
{
int c = getopt(argc, argv, "A:BCDE:F");
if (c >= 'A' && c <= 'F')
{
<< static_cast<char>(c) << endl;
cout if (optarg != nullptr)
<< string(optarg) << endl;
cout }
else
{
break;
}
}
(0);
exit}
/*
gaowanlu@DESKTOP-QDLGRDB$ ./main -A 232 -E 43 -BE 43 -C 43
A
232
E
43
B
E
43
C
*/
getopt_long 为 getopt 的加强版,getopt_long 支持长选项,如“–ipaddr”
#include <getopt.h>
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
getopt_long
函数的第四个参数(longopts)与第三个参数(optstring)功能一致,optstring
用于支持短选项,longopts 则用于支持长选项。
longopts 是一个结构体数组,每个结构体表示一个支持的选项
struct option {
const char *name; /* 参数名称 */
int has_arg; /* 是否带有参数*/
int *flag; /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
int val; /* 指定函数匹配到*name表示的选项后,getopt_long函数的返回值,或flag非空时指定*flag的值 */
};
使用样例
#include <iostream>
#include <getopt.h>
using namespace std;
static struct option long_options[] = {
{"ip", required_argument, 0, 1},
{"other", no_argument, 0, 2}, // 当匹配到--append时getopt_long返回2
{"port", required_argument, 0, 3},
{nullptr, 0, nullptr, 0}};
int main(int argc, char **argv)
{
const char *optstr = "a:n:";
int opt = 0;
int opt_index = 0;
while ((opt = getopt_long_only(argc, argv, optstr, long_options, &opt_index)) != -1)
{
switch (opt)
{
// ip
case 1:
<< "ip=" << optarg << endl;
cout break;
// other
case 2:
<< "other" << endl;
cout break;
// port
case 3:
<< "port=" << optarg << endl;
cout break;
// a
case 'a':
<< "a=" << optarg << endl;
cout break;
// n
case 'n':
<< "n=" << optarg << endl;
cout break;
default:
break;
}
}
return 0;
}
/*
gaowanlu@DESKTOP-QDLGRDB:/$ ./main --ip 0.0.0.0 --port 88 --other 43 -a 32 -n 100
ip=0.0.0.0
port=88
other
a=32
n=100
*/
每个程序都接收到一张环境表,里面为系统环境变量
环境变量本质就是 KEY=VALUE,查看环境变量
gaowanlu@DESKTOP-QDLGRDB:/$ export
declare -x EMSDK="/SD/emsdk"
declare -x EMSDK_NODE="/SD/emsdk/node/14.18.2_64bit/bin/node"
declare -x EM_CONFIG="/SD/emsdk/.emscripten"
declare -x HOME="/home/gaowanlu"
declare -x HOSTTYPE="x86_64"
declare -x LANG="C.UTF-8"
declare -x LOGNAME="gaowanlu"
declare -x MOTD_SHOWN="update-motd"
declare -x NAME="DESKTOP-QDLGRDB"
declare -x OLDPWD
declare -x PATH="/SD/emsdk:/SD/emsdk/upstream/emscripten:/SD/emsdk/node/14.18.2_64bit/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/m
nt/c/Program Files/Eclipse Adoptium/jdk-11.0.17.8-hotspot/bin:/mnt/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.2/bin:/mnt/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA
/v10.2/libnvvp:/mnt/c/Program Files/Common Files/Oracle/Java/javapath:/mnt/f/mvviwer/MV Viewer/Runtime/x64/:/mnt/f/mvviwer/MV Viewer/Runtime/Win32/:/mnt/c/Program Files (x86)/Common
Files/Intel/Shared Libraries/redist/intel64_win/compiler:/mnt/c/Program Files (x86)/Common Files/Intel/Shared Libraries/redist/intel64/compiler:/mnt/c/Windows/system32:/mnt/c/Windo
ws:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/Program Files/Intel/WiFi/bin/:/mnt/c/Program Files/Common Fil
es/Intel/WirelessCommon/:/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/mnt/c/Program Files/Microsoft SQL Server/130/Tools/Binn/:/mnt/d/Program Files (x86)/IncrediBuild
:/mnt/c/Program Files/python:/mnt/c/Program Files/python/Scripts:/mnt/d/BtSoft/panel/script:/mnt/d/jishu/opencv/opencv/build/x64/vc15:/mnt/d/jishu/Git/cmd:/mnt/c/WINDOWS/system32:/m
nt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Program Files/PuTTY/:/mnt/c/Program Files/CMake/bin
:/mnt/d/jishu/apache-maven-3.8.1/bin:/mnt/c/Program Files/NVIDIA Corporation/NVIDIA NvDLISR:/mnt/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.2/lib:/mnt/c/Program Files/NVI
DIA GPU Computing Toolkit/CUDA/v10.2/include:/mnt/c/Program Files/NVIDIA Corporation/Nsight Compute 2019.5.0/:/mnt/c/ProgramData/chocolatey/bin:/mnt/c/Program Files/dotnet/:/mnt/d/e
rlang/erl-24.3.3/bin:/mnt/c/Program Files/Docker/Docker/resources/bin:/mnt/c/ProgramData/DockerDesktop/version-bin:/mnt/c/Users/gaowanlu/AppData/Local/Android/Sdk/platform-tools:/mn
t/c/Users/gaowanlu/AppData/Local/Android/Sdk/emulator:/mnt/c/Users/gaowanlu/AppData/Local/Android/Sdk/tools:/mnt/c/Users/gaowanlu/AppData/Local/Android/Sdk/tools/bin:/mnt/c/Program
Files/nodejs/:/mnt/c/Users/gaowanlu/.cargo/bin:/mnt/d/anaconda:/mnt/d/anaconda/Library/mingw-w64/bin:/mnt/d/anaconda/Library/usr/bin:/mnt/d/anaconda/Library/bin:/mnt/d/anaconda/Scri
pts:/mnt/c/Program Files/MySQL/MySQL Shell 8.0/bin/:/mnt/c/Users/gaowanlu/AppData/Local/Programs/Python/Python38-32/Scripts/:/mnt/c/Users/gaowanlu/AppData/Local/Programs/Python/Pyth
on38-32/:/mnt/d/jishu/Tools/WinNT:/mnt/d/jishu/MSDev98/Bin:/mnt/d/jishu/Tools:/mnt/d/Microsoft Visual Studio/VC98/bin:/mnt/c/Users/gaowanlu/AppData/Local/Microsoft/WindowsApps:/mnt/
d/jishu/vc/Microsoft VS Code/bin:/mnt/d/jishu/idea/IntelliJ IDEA Community Edition 2020.2.3/bin:/mnt/d/jishu/clion/CLion 2020.3.3/bin:/mnt/d/jishu/DataGrip 2021.1/bin:/mnt/d/jishu/I
ntelliJ IDEA Educational Edition 2021.1.1/bin:/mnt/d/jishu/IntelliJ IDEA 2021.1.2/bin:/mnt/c/MinGW/bin:/mnt/c/Users/gaowanlu/AppData/Roaming/npm:/snap/bin"
declare -x PWD="/mnt/c/Users/gaowanlu/Desktop/MyProject/note/testcode"
declare -x SHELL="/bin/bash"
declare -x SHLVL="1"
declare -x TERM="xterm-256color"
declare -x USER="gaowanlu"
declare -x WSLENV="WT_SESSION::WT_PROFILE_ID"
declare -x WSL_DISTRO_NAME="Ubuntu-20.04"
declare -x WT_PROFILE_ID="{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"
declare -x WT_SESSION="4357a040-291d-4667-87ef-403a24d2ef32"
declare -x XDG_DATA_DIRS="/usr/local/share:/usr/share:/var/lib/snapd/desktop"
程序读取环境变量
#include <iostream>
using namespace std;
//全局变量envviron包含了该指针数组的地址
extern char **environ;
int main(int argc, char **argv)
{
char **iter = &environ[0];
while (*iter)
{
<< *iter << endl;
cout ++iter;
}
return 0;
}
/*
PWD=/mnt/c/Users/gaowanlu/Desktop/MyProject/note/testcode
LOGNAME=gaowanlu
HOME=/home/gaowanlu
*/
指定 key 尝试获取 value
#include <stdlib.h>
char *getenv(const char *name);
char *secure_getenv(const char *name);
demo
#include <iostream>
#include <stdlib.h>
using namespace std;
int main(int argc, char **argv)
{
const char *v1 = getenv("NONE");
if (v1 == nullptr)
{
<< "nullptr" << endl;
cout } // nullptr
const char *v2 = getenv("LANG");
<< v2 << endl; // C.UTF-8
cout return 0;
}
#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
demo
#include <iostream>
#include <stdlib.h>
using namespace std;
int main(int argc, char **argv)
{
("NONE"); // NULL
getenvif (setenv("NONE", "ABCDEFG", true) == 0)
{
<< "添加成功" << endl;
cout << "NONE=" << getenv("NONE") << endl;
cout } // 如果NONE存在则覆盖
("NONE"); // 删除NONE
unsetenv<< (getenv("NONE") == nullptr) << endl;
cout return 0;
}
/*
添加成功
NONE=ABCDEFG
1
*/
改变或者添加环境变量
#include <stdlib.h>
int putenv(char *string);
demo
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
int main(int argc, char **argv)
{
char s[125];
const char *v = "WANLU=123";
(s, v, strlen(v) + 1);
strncpy(s);
putenv<< getenv("WANLU") << endl; // 123
cout return 0;
}
正文为指令、初始化数据段如 C 全局变量、未初始化数据段为只定义没有进行初始化的、栈、堆从两头向中间去,堆顶和栈顶之间未用的虚地址空间很大
@drecbb4udzdboiei-0626900:/mes/colnago/bin# size ./colnago
root
text data bss dec hex filename679851 10000 1312 691163 a8bdb ./colnago
dec为三者总长度十进制表示,hex为十六进制表示
静态函数库:是在程序执行前就加入到目标程序中去了,.a
文件,从链接层解决,不用重读编译
动态函数库同共享函数库:二者是一个东西,在 linux 上叫共享对象库,
文件后缀是.so ,windows 上叫动态加载函数库,
文件后缀是.dll),从执行时解决
后面应该还会详细学习
-static main.cpp //阻止gcc使用共享库
gcc .cpp //gcc默认使用共享库 gcc main
共享库:程序第一次执行或调用某个库函数时,用动态链接方法将程序与共享库函数相链接,减少了每个可执行文件的长度,增加了时间开销,因为要加载共享库,提供了编译程序时提供动态链接库,或者用代码在运行时加载.so 文件,调用内部函数。
主要为dlerror、dlopen、dlclose、dlsym函数的使用
#include <dlfcn.h>
char *dlerror(void);//获取错误描述信息
void *dlopen(const char *filename, int flags);
int dlclose(void *handle);
//加载动态链接库,或者关闭已打开的
void *dlsym(void *handle, const char *symbol);
//用于获取,动态链接库提供的函数,symbol为函数名等,返回函数指针
//函数指针需要自己强转为响应函数类型
extern "C"{
void hello();
}
extern "C" void world();
//提供一个hello,dlsym(handle,"hello")进行获取
//为什么要用extern "C" 因为C++有函数名重载机制
//不能使用函数名作为symbol 而extern "C"的作用就是,让其函数作为ID
//简单地说就是这样,深入学习,还要继续探讨
C++尽量还是用 OOP 吧
#include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
void *reallocarray(void *ptr, size_t nmemb, size_t size);
setjmp 与 longjmp,是一种非的 goto,可以在栈上跳过若干调用帧
#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);//env 跳转点 val带回去的内容
RETURN VALUE() and sigsetjmp() return 0 when called directly; on the "fake" return that occurs after longjmp() or sig‐longjmp(), the nonzero value specified in val is returned.
setjmp() or siglongjmp() functions do not return. The longjmp
demo
#include <iostream>
#include <setjmp.h>
using namespace std;
;
jmp_buf env
void d()
{
}
void c()
{
<< "call d()" << endl;
cout ();
d<< "d() end" << endl;
cout (env, 1);
longjmp}
void b()
{
<< "call c()" << endl;
cout ();
c<< "c() end" << endl;
cout }
void a()
{
int n;
<< "call b()" << endl;
cout = setjmp(env);
n if (n == 0)
{
();
b<< "b() end" << endl;
cout }
else
{
<< "jumped here" << endl;
cout }
}
int main(int argc, char **argv)
{
<< "call a()" << endl;
cout ();
a<< "a() end" << endl;
cout return 0;
}
// 不跳帧 跳帧后
// call a() call a()
// call b() call b()
// call c() call c()
// call d() call d()
// d() end d() end
// c() end jumped here
// b() end a() end
// a() end
在 jump 后,栈帧对于自动变量、寄存器变量、易失变量是否能得到原来的恢复呢?这是不确定的,对于加 volatile 属性的变量不使其回滚。声明为全局变量或者静态变量的值在 longjmp 时保持不变
#include <iostream>
#include <setjmp.h>
using namespace std;
;
jmp_buf env
void c()
{
(env, 1);
longjmp<< "c" << endl;
cout }
void b()
{
<< "call c()" << endl;
cout ();
c<< "c() end" << endl;
cout }
void a()
{
volatile int val1 = 123;
int val2 = 123;
int n;
<< "call b()" << endl;
cout = setjmp(env);
n if (n == 0)
{
();
b<< "b() end" << endl;
cout }
else
{
<< "jumped here " << n << endl;
cout << "val1 = " << val1 << " val2 = " << val2 << endl;
cout }
}
int main(int argc, char **argv)
{
<< "call a()" << endl;
cout ();
a<< "a() end" << endl;
cout return 0;
}
// call a()
// call b()
// call c()
// jumped here 1
// val1 = 123 val2 = 123
// a() end
每个进程都有一组资源限制,可以使用 getrlimit 与 setrlimit 查询和修改
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,
struct rlimit *old_limit);
struct rlimit {
rlim_t rlim_cur; /* Soft limit,软限制不能超过硬限制 */
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */
};
普通用户可以修改软限制,可提高可降低但不能高过硬限制,对硬限制只能降低
管理员可以修改软限制和硬限制,软限制不能超过硬限制
:
resource
RLIMIT_AS
RLIMIT_CORE
RLIMIT_CPU
RLIMIT_DATA
RLIMIT_FSIZE(early Linux 2.4 only)
RLIMIT_LOCKS
RLIMIT_MEMLOCK(since Linux 2.6.8)
RLIMIT_MSGQUEUE (since Linux 2.6.12, but see BUGS below)
RLIMIT_NICE
RLIMIT_NOFILE
RLIMIT_NPROC
RLIMIT_RSS(since Linux 2.6.12, but see BUGS)
RLIMIT_RTPRIO (since Linux 2.6.25)
RLIMIT_RTTIME (since Linux 2.6.8)
RLIMIT_SIGPENDING RLIMIT_STACK
@DESKTOP-QDLGRDB:/$ ulimit -a
gaowanlu(blocks, -c) 0
core file size (kbytes, -d) unlimited
data seg size (-e) 0
scheduling priority (blocks, -f) unlimited
file size signals (-i) 7823
pending (kbytes, -l) 64
max locked memory (kbytes, -m) unlimited
max memory size (-n) 1024
open files (512 bytes, -p) 8
pipe size (bytes, -q) 819200
POSIX message queues -time priority (-r) 0
real(kbytes, -s) 8192
stack size (seconds, -t) unlimited
cpu time (-u) 7823
max user processes virtual memory (kbytes, -v) unlimited
(-x) unlimited file locks
//demo
#include <iostream>
#include <sys/time.h>
#include <sys/resource.h>
using namespace std;
struct rlimit lim;
int main(int argc, char **argv)
{
(RLIMIT_MSGQUEUE, &lim);
getrlimit<< lim.rlim_cur << " " << lim.rlim_max << endl;
cout // 819200 819200
(RLIMIT_MSGQUEUE, &lim);
setrlimitreturn 0;
}