• Unix环境高级编程-学习-02-进程环境之进程终止、命令行参数、环境表、C程序的存储空间布局


    目录

    一、环境信息

    二、声明

    三、进程终止

    1、情况分类

    2、退出函数

    3、退出实验

    (1)main声明int和调用return值

    (2)main声明int和不调用return

    (3)main声明不int和不调用return

    4、atexit

    5、atexit实验

    四、命令行、环境表

    1、命令行

    2、环境表

    3、实验

    五、C程序的存储空间布局

    1、图示

    2、名词解释

    3、实验


    一、环境信息

    名称
    CPUIntel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
    操作系统CentOS Linux release 7.9.2009 (Core)
    内存3G
    逻辑核数2

    二、声明

    本文部分内容参考了《Unix环境高级编程》第三版,这本书写的很好,推荐大家进行阅读。

    三、进程终止

    1、情况分类

    程序终止一共分为八种:

    编号情况正常与否
    1main返回正常终止
    2调用exit函数
    3调用_exit或_Exit函数
    4最后一个线程从其启动例程返回(后续博客讲述)
    5从最后一个线程调用pthread_exit(后续博客讲述)
    6调用abort(后续博客讲述)异常终止
    7接到一个信号(后续博客讲述)
    8最后一个线程对取消请求做出响应(后续博客讲述)

    2、退出函数

    退出函数有三种:

    函数声明描述
    void exit(int __status)对于所有打开流调用fclose函数,之后再进入内核。
    void _exit(int __status)直接进入内核。
    void _Exit(int __status)直接进入内核。

    上面三个函数都包含参数__status(终止状态),如果调用时没带参数,或者用renturn;返回,或者main函数没有声明返回值类型为整型,那么进程的终止状态时未定义的,如果声明了整型返回值,但没有调用return或上面的三个函数,那么会隐式返回终止状态0。

    main函数中exit(0)和return(0)等价。

    3、退出实验

    (1)main声明int和调用return值

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. printf("Uid : %d, Gid : %d\n",getuid(),getgid());
    7. return 6;
    8. }
    1. [gbase@czg2 Src]$ ../Exec/MyGetUserGroupId
    2. Uid : 1001, Gid : 1001
    3. [gbase@czg2 Src]$ echo $?
    4. 6

    (2)main声明int和不调用return

    1. #include
    2. #include
    3. #include
    4. int main()
    5. {
    6. printf("Uid : %d, Gid : %d\n",getuid(),getgid());
    7. }
    1. [gbase@czg2 Src]$ ../Exec/MyGetUserGroupId
    2. Uid : 1001, Gid : 1001
    3. [gbase@czg2 Src]$ echo $?
    4. 0

    (3)main声明不int和不调用return

    1. #include
    2. #include
    3. #include
    4. main()
    5. {
    6. printf("Uid : %d, Gid : %d\n",getuid(),getgid());
    7. }
    1. [gbase@czg2 Src]$ gcc -Wall -Wextra -O3 MyGetUserGroupId.c -o /opt/Developer/ComputerLanguageStudy/C/Unix/Exec/MyGetUserGroupId
    2. MyGetUserGroupId.c:5:1: 警告:返回类型默认为‘int’ [-Wreturn-type]
    3. main()
    4. ^
    5. MyGetUserGroupId.c: 在函数‘main’中:
    6. MyGetUserGroupId.c:8:1: 警告:在有返回值的函数中,控制流程到达函数尾 [-Wreturn-type]
    7. }
    8. ^
    9. [gbase@czg2 Src]$ ../Exec/MyGetUserGroupId
    10. Uid : 1001, Gid : 1001
    11. [gbase@czg2 Src]$ echo $?
    12. 23

    不同操作系统编译该程序,可能得到不同的终止状态,这取决于main函数返回时寄存器和栈的内容。

    1. [gbase@czg2 Src]$ gcc -Wall -Wextra -O3 -std=gnu11 MyGetUserGroupId.c -o /opt/Developer/ComputerLanguageStudy/C/Unix/Exec/MyGetUserGroupId
    2. MyGetUserGroupId.c:5:1: 警告:返回类型默认为‘int’ [默认启用]
    3. main()
    4. ^
    5. [gbase@czg2 Src]$ ../Exec/MyGetUserGroupId
    6. Uid : 1001, Gid : 1001
    7. [gbase@czg2 Src]$ echo $?
    8. 0

    -std=gnu11启用C++11标准和GNU扩展特性,可以发现终止状态变化了。

    4、atexit

    atexit的作用是注册终止处理程序。就是在程序执行exit后,程序里面不会立马结束会先执行终止处理程序,再关闭文件流使用fclose函数,最后调用_exit或_Exit函数。

    这个函数个人还觉得非常有用的,虽然还没有想到怎么使用。

    5、atexit实验

    1. #include
    2. #include
    3. void MyExit1(void)
    4. {
    5. printf("MyExit1\n");
    6. }
    7. void MyExit2(void)
    8. {
    9. printf("MyExit2\n");
    10. }
    11. int main()
    12. {
    13. if (atexit(MyExit1) != 0)
    14. {
    15. printf("Can't Register MyExit1.\n");
    16. }
    17. if (atexit(MyExit1) != 0)
    18. {
    19. printf("Can't Register MyExit1.\n");
    20. }
    21. if (atexit(MyExit2) != 0)
    22. {
    23. printf("Can't Register MyExit2.\n");
    24. }
    25. printf("main Finish.\n");
    26. return 0;
    27. }
    1. [gbase@czg2 Src]$ ../Exec/MyAtexit
    2. main Finish.
    3. MyExit2
    4. MyExit1
    5. MyExit1

    注意一点我们注册的顺序和调用的顺序是反的,应该是一个函数栈的方式实现。

    四、命令行、环境表

    为什么一起讲呢因为我把实现放一块了。

    1、命令行

    我们常常看到操作系统命令如ls可以后面带很多的参数,来实现不同的功能,通过main的传入参数int argc, char* argv[],我们也可以实现相同的功能。argc表示参数个数。argv表示参数值集合。

    2、环境表

    每个程序都会接收到一张环境表。也就是操作系统环境变量例如:LD_LIBRAYR_PATH,PATH之类等。

    只需要在main函数的传入参数加上一个定义char* envp[]即可。

    envp和argv不同没有给出参数个数argc,但envp数组的最后一个元素字符串是NULL,我们可以此为结束标志。

    3、实验

    1. #include
    2. #include
    3. int main(int argc, char* argv[],char* envp[])
    4. {
    5. int i;
    6. for ( i = 0; i < argc; i++)
    7. {
    8. printf("argv[%d] : %s\n",i,argv[i]);
    9. }
    10. i = 0;
    11. while (envp[i] != NULL)
    12. {
    13. printf("envp[%d] : %s\n",i,envp[i]);
    14. i++;
    15. }
    16. return 1;
    17. }
    1. [gbase@czg2 Src]$ ../Exec/MyEchoArg Parameter1 Parameter2
    2. argv[0] : ../Exec/MyEchoArg
    3. argv[1] : Parameter1
    4. argv[2] : Parameter2
    5. envp[0] : XDG_SESSION_ID=2
    6. envp[1] : HOSTNAME=czg2
    7. envp[2] : GCLUSTER_SID=gcluster
    8. envp[3] : SHELL=/bin/bash
    9. envp[4] : TERM=xterm-256color
    10. envp[5] : HISTSIZE=1000
    11. envp[6] : GBASE_BASE=/opt/gnode
    12. envp[7] : SSH_GBASE_PASSWD=6762617365
    13. envp[8] : QTDIR=/usr/lib64/qt-3.3
    14. envp[9] : OLDPWD=/opt/Developer/ComputerLanguageStudy/C/Unix
    15. envp[10] : QTINC=/usr/lib64/qt-3.3/include
    16. envp[11] : QT_GRAPHICSSYSTEM_CHECKED=1
    17. envp[12] : USER=gbase
    18. envp[13] : LD_LIBRARY_PATH=:/opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/Make/Libs/:/opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/Gbase8a/libs/Gbase8a/x86_64_linux/:/lib64:/opt/gcluster/server/lib/gbase/:/opt/gnode/server/lib/gbase/:/opt/gnode/server/lib/gbase/plugin/gbfti/lib:/opt/gnode/server/lib/gbase/plugin/gbfti:/opt/gcluster/server/lib/gbase/plugin:/opt/gcluster/server/lib/gbase/plugin/gbfti:/opt/gcluster/server/lib/gbase/plugin/gbfti/lib:/lib64:/opt/gcluster/server/lib/gbase/:/opt/gnode/server/lib/gbase/:/opt/gnode/server/lib/gbase/plugin/gbfti/lib:/opt/gnode/server/lib/gbase/plugin/gbfti:/opt/gcluster/server/lib/gbase/plugin:/opt/gcluster/server/lib/gbase/plugin/gbfti:/opt/gcluster/server/lib/gbase/plugin/gbfti/lib
    19. envp[14] : LS_COLORS=rs=0:di=38;5;27:ln=38;5;51:mh=44;38;5;15:pi=40;38;5;11:so=38;5;13:do=38;5;5:bd=48;5;232;38;5;11:cd=48;5;232;38;5;3:or=48;5;232;38;5;9:mi=05;48;5;232;38;5;15:su=48;5;196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;10;38;5;21:st=48;5;21;38;5;15:ex=38;5;34:*.tar=38;5;9:*.tgz=38;5;9:*.arc=38;5;9:*.arj=38;5;9:*.taz=38;5;9:*.lha=38;5;9:*.lz4=38;5;9:*.lzh=38;5;9:*.lzma=38;5;9:*.tlz=38;5;9:*.txz=38;5;9:*.tzo=38;5;9:*.t7z=38;5;9:*.zip=38;5;9:*.z=38;5;9:*.Z=38;5;9:*.dz=38;5;9:*.gz=38;5;9:*.lrz=38;5;9:*.lz=38;5;9:*.lzo=38;5;9:*.xz=38;5;9:*.bz2=38;5;9:*.bz=38;5;9:*.tbz=38;5;9:*.tbz2=38;5;9:*.tz=38;5;9:*.deb=38;5;9:*.rpm=38;5;9:*.jar=38;5;9:*.war=38;5;9:*.ear=38;5;9:*.sar=38;5;9:*.rar=38;5;9:*.alz=38;5;9:*.ace=38;5;9:*.zoo=38;5;9:*.cpio=38;5;9:*.7z=38;5;9:*.rz=38;5;9:*.cab=38;5;9:*.jpg=38;5;13:*.jpeg=38;5;13:*.gif=38;5;13:*.bmp=38;5;13:*.pbm=38;5;13:*.pgm=38;5;13:*.ppm=38;5;13:*.tga=38;5;13:*.xbm=38;5;13:*.xpm=38;5;13:*.tif=38;5;13:*.tiff=38;5;13:*.png=38;5;13:*.svg=38;5;13:*.svgz=38;5;13:*.mng=38;5;13:*.pcx=38;5;13:*.mov=38;5;13:*.mpg=38;5;13:*.mpeg=38;5;13:*.m2v=38;5;13:*.mkv=38;5;13:*.webm=38;5;13:*.ogm=38;5;13:*.mp4=38;5;13:*.m4v=38;5;13:*.mp4v=38;5;13:*.vob=38;5;13:*.qt=38;5;13:*.nuv=38;5;13:*.wmv=38;5;13:*.asf=38;5;13:*.rm=38;5;13:*.rmvb=38;5;13:*.flc=38;5;13:*.avi=38;5;13:*.fli=38;5;13:*.flv=38;5;13:*.gl=38;5;13:*.dl=38;5;13:*.xcf=38;5;13:*.xwd=38;5;13:*.yuv=38;5;13:*.cgm=38;5;13:*.emf=38;5;13:*.axv=38;5;13:*.anx=38;5;13:*.ogv=38;5;13:*.ogx=38;5;13:*.aac=38;5;45:*.au=38;5;45:*.flac=38;5;45:*.mid=38;5;45:*.midi=38;5;45:*.mka=38;5;45:*.mp3=38;5;45:*.mpc=38;5;45:*.ogg=38;5;45:*.ra=38;5;45:*.wav=38;5;45:*.axa=38;5;45:*.oga=38;5;45:*.spx=38;5;45:*.xspf=38;5;45:
    20. envp[15] : GCLUSTER_GROUP=gbase
    21. envp[16] : GCLUSTER_BASE=/opt/gcluster
    22. envp[17] : TERMINFO_DIRS=/opt/gcluster/server/share/terminfo:/opt/gnode/server/share/terminfo:/usr/share/terminfo
    23. envp[18] : MAIL=/var/spool/mail/gbase
    24. envp[19] : PATH=/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/gcluster/server/bin:/opt/gnode/server/bin:/home/gbase/.local/bin:/home/gbase/bin:/opt/gcluster/server/bin:/opt/gnode/server/bin
    25. envp[20] : PWD=/opt/Developer/ComputerLanguageStudy/C/Unix/Src
    26. envp[21] : GCLUSTER_HOME=/opt/gcluster/server
    27. envp[22] : LANG=zh_CN.UTF-8
    28. envp[23] : HISTCONTROL=ignoredups
    29. envp[24] : SHLVL=1
    30. envp[25] : HOME=/home/gbase
    31. envp[26] : PYTHONPATH=:/usr/lib64/python_gcware:/usr/lib64/python_gcware
    32. envp[27] : GCLUSTER_USER=gbase
    33. envp[28] : LOGNAME=gbase
    34. envp[29] : QTLIB=/usr/lib64/qt-3.3/lib
    35. envp[30] : GBASE_SID=gbase
    36. envp[31] : XDG_DATA_DIRS=/home/gbase/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
    37. envp[32] : LESSOPEN=||/usr/bin/lesspipe.sh %s
    38. envp[33] : HAPPY_SUNSHINE_HOME=/home/gbase/HappySunshineTool
    39. envp[34] : GBASE_HOME=/opt/gnode/server
    40. envp[35] : TCMALLOC_AGGRESSIVE_DECOMMIT=1
    41. envp[36] : _=../Exec/MyEchoArg

    如果大家不想要所有的环境变量可以使用getenv,去获取指定的环境变量。

    1. Status MyGetOsEnv(const char * OsEnvName, MyStrType RetVal)
    2. {
    3. JudgeAllNullPointer(OsEnvName);
    4. JudgeAllNullPointer(RetVal);
    5. RetVal = getenv(OsEnvName);
    6. if (RetVal == NULL)
    7. {
    8. LogFormat(Error,"Get Os Env : Fail, OsEnvName : %s, RetVal : NULL.\n",OsEnvName);
    9. return FailFlag;
    10. }
    11. LogFormat(Debug,"Get Os Env : OK, OsEnvName : %s, RetVal : %s.\n",OsEnvName,RetVal);
    12. return SuccessFlag;
    13. }

    这里给大家简单封装了一下,供参考。

    五、C程序的存储空间布局

    1、图示

    7942b9cc60e74b91b4ed8cd5f0ad9930.png

    纯手工画图,想来想去还是需要来一张。

    2、名词解释

    名称描述
    正文段

    1、存放CPU执行的机器指令部分。

    2、正文段是可共享的,所以即使频繁的执行程序,在存储器中也只需要一个副本。

    3、正文段是只读的,防止被恶意修改。

    初始化数据段

    1、也称为数据段,包含了程序中需明确赋初值的变量。

    2、我通过实验理解就是初始化的全局变量。

    未初始化数据段

    1、也称为bss段。

    2、我通过实验理解就是未初始化的全局变量。

    3、在程序启动后,内核会将此段中的数据初始化为0或空指针。

    1、自动变量已经每次函数调用时所需保存的信息都存放在此段中。
    2、例如:调用函数时的返回地址和调用者的环境信息、函数内定义的变量等。

    3、栈从高地址向低地址方向增长。

    1、动态分配的内存,由malloc、calloc申请的内存等。

    2、堆从低地址向高地址方向增长。

    注意:只有正文和初始化段存放在磁盘的程序文件中。

    3、实验

    1. #include
    2. #include
    3. char GlobalNoInitArray[100];
    4. int GlobalIinitVal = 0;
    5. int main(int argc, char* argv[])
    6. {
    7. int SysStackInit = 1;
    8. char SysStackNoInit;
    9. long* Heap = (long*)malloc(sizeof(long));
    10. printf("argv : %p\n",argv);
    11. printf("SysStackInit : %p\n",&SysStackInit);
    12. printf("SysStackNoInit : %p\n",&SysStackNoInit);
    13. printf("Heap : %p\n",Heap);
    14. printf("GlobalNoInitArray : %p\n",GlobalNoInitArray);
    15. printf("GlobalIinitVal : %p\n",&GlobalIinitVal);
    16. free(Heap);
    17. Heap = NULL;
    18. return 1;
    19. }
    1. [gbase@czg2 Src]$ ../Exec/MyStorageSpaceDistribution
    2. argv : 0x7fff4c7ae6c8
    3. SysStackInit : 0x7fff4c7ae5c0
    4. SysStackNoInit : 0x7fff4c7ae5bf
    5. Heap : 0x980010
    6. GlobalNoInitArray : 0x601080
    7. GlobalIinitVal : 0x601064

    从中可以看出确实是按照图中进行排序的。再次点赞一下这本书,不错的,值得大家读。

    1. [gbase@czg2 Src]$ size ../Exec/MyStorageSpaceDistribution
    2. text data bss dec hex filename
    3. 1624 564 136 2324 914 ../Exec/MyStorageSpaceDistribution

    从左到右分别是正文段、数据段、bss段、十进制总长度、十六进制总长度。(单位:字节)

  • 相关阅读:
    求2个字符串的最短编辑距离 java 实现
    定了,JDK 19 功能集冻结,JDK官方终于支持支持协程了
    【ES6】学习笔记:字符串扩展
    基于opencv+tensorflow+神经网络的智能银行卡卡号识别系统——深度学习算法应用(含python、模型源码)+数据集(一)
    python读写hive
    C++ 宏定义中的##
    快速部署stable diffusion@Ubuntu
    Day 59 django ROM 多表查询
    Git/Gitee零基础入门教程
    Ubuntu MySQL
  • 原文地址:https://blog.csdn.net/qq_45111959/article/details/134281085