暗无天日

=============>DarkSun的个人博客

进程环境

1 进程的中止

1.1 中止的方式

有5种方式正常终止

  • 从main返回
  • 调用exit
  • 调用_exit或_Exit
  • 最后一个线程从其启动历程返回, 但是该线程的返回值不会用作进程的返回值,改进程以终止状态0返回
  • 最后一个线程调用pthread_exit, 这时,进程的终止状态也为0,而以pthread_exit的参数无关

有3种方式异常终止

  • 调用abort
  • 接到一个线号终止
  • 最后一个线程对取消请求做出响应

1.2 exit函数

#include <stdlib.h>

void exit(int status);

void _Exit(int status);

#include <unistd.h>

void _exit(int status);
  • exit函数会先执行清理动作(调用执行各终止处理程序,关闭标准IO流),然后进入内核
  • _exit和_Exit则会立即进入内核
  • 当进程从main函数返回时,会立刻调用exit函数.

    exit(main(argc,argv));
    

1.3 atexit函数

进程退出时,可以注册至少32个函数由exit函数自动调用,这些函数被称为终止处理函数.

需要使用atexit函数来注册这些函数

#include <stdlib.h>

int atexit(void (*func)(void));
  • exit调用这些函数的顺序与它们注册时的顺序相反.
  • 同一函数若登录多次,则也会被调用多次.
  • 进程退出时,先调用注册的终止处理函数,然后再执行清理动作,最后进入内核

2 环境变量表

每个进程都有一个字符指针数组,指向一张存储了环境变量的列表.

可以通过全局变量environ引用到该字符指针数组:

extern char **environ;
  • 一般来说,环境变量字符串的格式为`name=value'
  • 我们通常不直接使用environ变量,而是使用getenv和putenv函数来访问/设置指定的环境变量

2.1 获取环境变量值

#include <stdlib.h>

char* getenv(const char* name);
  • getenv函数返回一个指针,该指针指向`name = value'字符串中的value

2.2 修改函数变量值

#include <stdlib.h>

int putenv(char* str);

int setenv(const char* name,const char* value,int rewrite);

int unsetenv(const char* name);
  • putenv中的参数str需为格式`name = value'的字符串. 它添加环境变量,若环境变量已存在,则会覆盖之
  • 有些实现中,putenv直接将str的值放入环境表中,因此,若str存放于函数栈中,则可能会发生错误.
  • setenv将name设置为value,参数rewrite表示当name已存在时,是否覆盖原值(非0表示覆盖,0表示不覆盖)
  • unsetenv删除name的定义. 即使不存在这种定义也不出错.
  • 当向环境变量表中增加一个环境变量时,会调用malloc为新的环境变量表分配空间,并将原来的环境变量表复制到新分配空间中. 例如:

    #include <stdio.h>
    #include <stdlib.h>
    
    extern char** environ;
    int main(int argc, char *argv[])
    {
            printf("before set env:%x\n",environ);
            setenv("Hello","World",1);
            printf("after set env:%x\n",environ);
            return 0;
    }
    
    
    before set env:20010100
    after set env:20020258

3 存储器分配

有三个用于存储空间动态分配的函数

#include <stdlib.h>

void* malloc(size_t size);
void* calloc(size_t nobj,size_t size_of_obj);
void* realloc(void* ptr,size_t newsize);
char* alloca(int size);

void free(void* ptr);
  • calloc为指定数量的指定长度的对象分配存储空间,其与malloc的不同之处在于该空间的每一位都初始化为0
  • 这三个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象
  • 注意不要释放已经释放了的块
  • alloca与malloc的不同在于,它在当前函数的栈上分配存储空间,而不是在堆中. 其优点在于当函数返回时,会自动释放它所使用的栈空间.

4 setjmp和longjump

C中的goto是不能跨越函数的,要跨越函数的跳转只能通过`setjmp'和`longjmp'来实现.

这两个函数常用于处理发生在深层嵌套函数的出错情况.

#include <setjmp.h>

int setjmp(jmp_buf env);

void longjmp(jmp_buf env,int val);
  • setjmp中的参数env和longjmp中的参数env需要是同一个对象,因此比较常用的处理方式是将 env变量设置为全局变量
  • longjmp函数跳转回setjmp函数处时, 并不确定是否回滚自动变量(auto int)和寄存器变量(register int)的值. 但 *带有volatile的变量,全局变量和静态变量(static int)并不回滚原来的值.

    #include <stdio.h>
    #include <stdlib.h>
    #include <setjmp.h>
    
    static void f1(int,int,int,int);
    static void f2();
    
    static jmp_buf jmpbuffer;
    static int globval = 1;
    
    int main()
    {
            int autoval = 2;
            register int regival = 3;
            volatile int volaval = 4;
            static int statval = 5;
    
            if(setjmp(jmpbuffer) != 0)
            {
                    printf("after_longjmp:\n");
                    printf("globval=%d,autoval=%d,regival=%d,volaval=%d,statval=%d\n",globval,autoval,regival,volaval,statval);
                    exit(0);
            }
    
            globval = 95;
            autoval = 96;
            regival = 97;
            volaval = 98;
            statval = 99;
            f1(autoval,regival,volaval,statval);
            exit(0);
    }
    
    static void f1(int i ,int j,int k,int l)
    {
            printf("in_f1():\n");
            printf("globval=%d,autoval=%d,regival=%d,volaval=%d,statval=%d\n",globval,i,j,k,l);
            f2();
    }
    
    static void f2()
    {
            longjmp(jmpbuffer,1);
    }
    
    /* 输出结果为 */
    /* in f1(): */
    /* globval = 95,autoval = 96,regival = 97,volaval = 98,statval = 99 */
    /* after longjmp: */
    /* globval = 95,autoval = 2,regival = 3,volaval = 98,statval = 99 */
    
    in_f1():
    globval=95,autoval=96,regival=97,volaval=98,statval=99
    after_longjmp:
    globval=95,autoval=96,regival=3,volaval=98,statval=99
  • 有些系统的设计原则是:存放在内存中的变量不会回滚longjmp前的值,而存放在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值.

5 资源限制

每个进程都有一组资源限制,其中一些可以使用getrlimit和setrlimit函数来查询或更改

#include <sys/resource.h>

int getrlimit(int resouce,struct rlimit* rlptr);

int setrlimit(int resource,const struct rlimit*rlptr);

struct rlimit{
  rlim_t rlim_cur;              /* 软限制 */
  rlim_t rlim_max;              /* 硬限制 */
}
  • 任何进程都可以将一个软限制值更改为小于或等于其硬限制值
  • 任何一个进程都可以降低其硬限制值,但它必须大于或等于其软限制值. 且 这种降低是不可逆的
  • 只有超级用户进程可以提高硬限制值.
  • 常量RLTM_INFINITY表示unlimited
  • 资源限制影响到调用进程,并由其子进程继承