操作系统:进程环境

启动代码

c程序运行时,最开始运行的是启动代码,启动代码再去调用main函数,然后整个c程序都已运行。

总之,高级语言程序=启动代码+自己代码。

所以,c的启动代码其实才是整个c程序的开始代码,不过由于启动代码并不是我们自己写的,一般是由编译器提供的 。

启动代码做了什么?

  1. 对c程序的内存空间进行布局,得到c程序运行所需要的内存空间结构。

  2. 为库的调用预留接口。如果程序使用的是动态库的话,编译时,动态库代码并不会被直接编译到程序中
    只会留下相应的接口,程序运行起来后,才会去对接库代码,为了能够对接动态库,启动代码会留下动态库的对接接口。

启动代码一般是由汇编语言编写

在程序的内存空间结构还没有布局起来之前,高级语言程序还无法运行,此时只能使用汇编,当利用汇编编写的启动代码将高级语言的内存空间结构建立起来后,自然就可以运行c/c++等高级语言的程序了。

程序的终止

正常终止

进程主动调用终止函数/返回关键字所实现的结束,就是正常终止。

  • main调用return关键字结束

  • 程序任何位置调用exit函数结束

    #include <stdlib.h>
    void exit(int status);//status就是返回的状态值
    
  • 程序任何位置调用_exit函数结束
    _exit是一个系统函数(系统API),而exit是c库函数,exit就是调用_exit来实现的。

    #include <unistd.h>         
    void _exit(int status);
    
  1. 裸机时:
    只能调用return返回,因为没有os时,不支持exit_exit函数。
    裸机时,main的return到启动代码后,返回动作到启动代码就截止了。

  2. 有os时:
    returnexit_exit,使用哪种来返回都行。
    推荐使用returnexit,因为_exit是Linux os的系统调用,在windows就不支持了

atexit

#include <stdlib.h>
int atexit(void (*function)(void)) ;

这是一个c库函数,无论在任何位置正常终止程序,都会调用进程终止处理函数。

  • 功能:注册(登记)进程终止处理函数,参数就是被登记"进程终止函数”的地址。当进程无论什么时候正常终止时,会自动的去调用登记的进程终止处理函数,实现进程终止时的一些扫尾处理。
  • 返回值
    函数调用成功返回0,失败返回非零值,不会进行错误号设置。
  • 参数function:需要被注册的进程终止处理函数的地址。
  1. 进程终止处理函数的注册顺序和调用顺序刚好相反。
    调用atexit注册时,会将"进程终止处理函数"的函数地址压入进程栈中,当进程正常终止时,又会自动从栈中取出函数地址,并执行这个函数,实现进程的扫尾操作。
    栈的特点是先进后出,先压栈的后调用,所以调用顺序刚好和注册顺序相反。

  2. 在Linux下,调用atexit最多可以允许登记32个终止处理函数。

  3. 同一个函数如果被登记多次,自然也会被调用多次。

  4. 在两种情况下,登记的进程终止处理函数不会被调用

    • 异常终止,不会调用
    • 直接调用_exit来正常终止时,不会调用
      换句话说,只有使用return和exit来正常终止时,才会调用。

有关标准Io的库缓存的缓冲有三种,无缓冲、行缓冲、全缓冲。
标准输出(printf)的库缓存就是行缓冲的,在缓存中积压的数据,直到出现以下情况时,才会刷新输出,否则就一直挤压着。

  • 遇到\n时就刷新输出,\n表示这是一行,就好比句号表示一句话一样。

  • 库缓存中数据满了,也会自动刷新输出,这就好比盆里的水满了溢出一样。
    不过一般来说,数据不可能多到能够把缓存装满的。

  • 调用标准fflush函数,主动刷新数据

    #include <stdio.h>    
    int fflush(FILE *stream); 
    

    将标准IOstream的库缓存内的数据输出,如果stream参数为NULLfflush() flushes all open output streams.

  • 调用fclose关闭标准输出时,会自动调用fflush刷洗数据

    为什么调用exit正常终止时,会刷新标准io的缓存呢?

    因为exit会调用fclose关闭所有的标准IO,关闭时会自动调用fflush来刷新数据。

异常终止

进程不是因为returnexit_exit函数而终止的,而是被强行发送了一个信号,这个信号将进程给无条件终止了,这就是异常终止。

  • 自杀:自己调用abort函数,自己给自己发一个SIGABRT信号将自己杀死,杀人凶器是信号;

    #include <stdlib.h>               
    void abort(void);
    
  • 他杀:由别人发一个信号,将其杀死,杀人凶器也是信号;
    为什么按下ctrl+c,就可以将程序终止。
    其实向终端输入ctrl+c时,就是在向我的进程发送了某个信号,然后这个信号将我的程序给异常终止了。

main函数参数

int main(int argc,char **argv);
  • argc存放参数个数
  • argv指针数组,存放参数,第一个参数永远都是程序名。判断参数结尾:argv[i]==NULL
    将命令行参数传递给main函数形参的过程
    . / a . o u t   ∗ ∗   ∗ ∗ ⟶ 终端窗口进程构建指针数组 ⟶ o s 内核 ⟶ e x e c 启动代码 ⟶ m a i n 函数形参 ./a.out \ **\ **\longrightarrow 终端窗口进程构建指针数组\longrightarrow os内核\overset{exec}{\longrightarrow}启动代码\longrightarrow main函数形参 ./a.out  终端窗口进程构建指针数组os内核exec启动代码main函数形参

环境变量

  1. eviron全局变量
    environ与main函数的argv一样,指向的都是一个字符串指针数组。只不过,argv与命令行参数有关,environ与环境变量表有关

    extern char **environ;
    
  2. 第三个main函数参数

    int main(int argc,char **argv,char **environ);
    
  3. 调用API添加或修改环境变量

    #include <stdlib.h>    
    int putenv(char *string);  
    int setenv(const char *name, const char *value, int overwrite); 
    int unsetenv(const char *name); 
    char *getenv(const char *name);  
    

    函数功能

    • putenv:设置新的环境变量到环境表中。
      如果这环境变量之前就存在,那么这一次的数据会无条件覆盖之前的数据。如果不存在,就添加这个新的环境变量。
      • string:新的环境变量+值,比如"name=value"。
    • setenv函数:功能同上。
      • name:环境变量的名字。
      • value:环境变量值。
      • overwrite:如果发现name环境变量以前就已经存在,会根据overwrite的值来决定是否覆盖,0:不覆盖,非0:覆盖
    • getenv:获取环境变量
      • name:环境变量的名字。

    函数返回值

    • putenv函数:调用成功返回0,失败返回非0,errno被设置。
    • setenv函数:调用成功返回0,失败返回-1,errno被设置。
    • getenv函数:调用成功返回字符串指针,失败返回NULL指针

当有os支持时,基本所有的进程都是由父进程"生"出来的:原始进程—>进程—>进程一>终端进程—>a.out进程

所有进程的"环境变量表"都是从父进程复制得到的,最原始进程的"环境变量表"则是从"环境变量文件"中读到的。

环境变量文件—>原始进程进程环境表—>子进程进程环境表—>子进程进程环境表

C内存空间布局和库

C程序的内存空间结构

  • c程序的内存空间必须布局为c程序运行所需的空间结构,c程序才能运行。
    比如程序在调用函数时需要用到"栈"这个东西,那么就必须在内存空间中构建出"栈",否者c序程序没办法实现函数调用。
    如果空间没有布局好,进程将无法运行,因此程序的内存空间布局是非常重要的进程环境。

  • c的内存结构是由启动代码来搭建的,比如启动代码会把c内存空间的某一部分空间构建为"栈",或者说以"栈"的方式来管理这片空间。

  • c程序的内存空间为什么也叫”进程空间”?
    因为c程序在内存中运行起来后就是进程了,所以c程序的内存空间也叫"进程空间”。
    不光是c程序,所有高级语言的程序在运行时,都涉及内存空间的结构布局,不过它们的结构都是相似的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shilong Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值