UNIX高级编程总结-----进程环境

1、进程的终止方式

        通常情况下,进程有八种终止方式(5种正常终止 + 3种异常终止)

        正常终止:

      (1) main 的 正常 return

      (2)调用 exit 退出

      (3)调用 _exit 和 _Exit

      (4)最后一个线程从启动历程返回

      (5)最后一个线程调用pthread_exit退出

        异常终止:

      (1)调用 abort()

      (2)接到一个信号

      (3)最后一个线程 对 取消请求做出响应

1、有三种退出函数如下:

        这三个函数都属于进程正常退出的函数,_exit 和 _Exit 会直接进入内核,exit会先执行一些清理处理,然后进入到内核。

exit 函数总会执行一个标准IO的清理关闭操作,对所有打开的文件流都会调用fclose().这三个函数都有一个整型参数,代表终止状态。

2、atexit 函数

        可以登记一个在退出时执行的函数,有点析构函数的感觉。在ISO C里面规定了,一个进程最多只能登记32个函数。登记的函数无需参数,也没有返回值。atexit 也没有传参和处理返回值的方式。注意:exit 之后调用他们的顺序与登记时候的顺序相反,并且同一个函数被登记多次,也会被调用多次。下面是函数原型以及源码例子:

#include "apue.h"

static void    my_exit1(void);
static void    my_exit2(void);

int
main(void)
{
    if (atexit(my_exit2) != 0)
        err_sys("can't register my_exit2");

    if (atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");
    if (atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");

    printf("main is done\n");
    return(0);
}

static void
my_exit1(void)
{
    printf("first exit handler\n");
}

static void
my_exit2(void)
{
    printf("second exit handler\n");
}
 

运行结果:

main is done
first exit handler
first exit handler
second exit handler

3、环境表

        每一个程序都有一个环境表,它是以字符串数组形式呈现的,每一项都以 '\0' 结尾。其格式也是name=value, 如书中下表:

主函数中的参数形式如下:

          

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

       C语言的存储空间布局主要有:正文段、初始化数据段、未初始化数据段、栈、堆

        (1) 正文段:CPU执行的机器指令部分。通常是可共享的,频繁执行的程序在存储器中也只需要一个副本。并且他是只读的,以防止被修改。

        (2) 初始化数据段:通常称为数据段。主要存放一些已初始化的全局变量。

        (3) 未初始化数据段:通常称为 bss 段(block started by symbol 有符号开始的块)。在程序开始时,内核会将这些数据初始化为0,或者是NULL指针。

        (4) 栈。程序在运行时,编译器自动分配的一块连续存储区域,存放函数中的参数、局部变量等。不能动态申请、一切都靠编译器控制。如果超出了存储大小会出现栈溢出错误。入栈弹栈方式:后进先出

        (5) 堆。可以动态申请的存储区。通常是大块存储。该区域需要手动申请手动释放,若不释放会造成内存泄漏。或是在程序结束后系统回收。

注意:

        A 、static 局部变量,初始化的放在数据段,未初始化的放在 bss 段。数据段和bss段的区别就在于,是否初始化。在网上会有静态存储区一说,我觉得就是整个数据段 + bss段 + 代码段,数据段和bss段主要包含的是全局变量、静态局部变量、常量。

        B、linux 中 可以使用size命令,查看各段长度:

书中有张图:

 

5、对存储空间进行动态分配

        主要有三个函数:

        malloc 和 free 不解释;

        calloc :为指定的对象,分配一块相应个数的空间,并且将这段空间清零。

        realloc:在一个存储空间的位置上,申请一块空间,可能小,可能大。如果大,并且影响到了其他内存,则会申请一块新区域将该区域的数据拷贝到这块新区域并返回指针。老区域会释放。 

        alloca 栈上的分配:

 

6、可跨函数跳跃的 setjmp 和 longjmp

        C语言的 goto 只能实现函数内的跳跃,要跨函数的跳跃需要用到 setjmp 和 longjmp。书上举了一段代码的例子,他存储的地址如下:

        

        在上述的案例中,假设 cmd_add 出现了错误,函数会打印错误信息后返回,一直到main函数。该例子cmd_add比main函数低两个层级,如果低五个层级的话,一层一层的返回就显得特别麻烦。为了解决这一问题,就是用了setjmp 和 longjmp。

        goto是在函数内跳转;setjmp 和 longjmp 是在栈上跳转。

#include <setjmp.h>

int setjmp(jmp_buf env);

           返回值:若直接调用,返回0;若从longjmp返回,则为非0

void longjmp(jmp_buf env, int val);

#include "apue.h"
#include <setjmp.h>

static void    f1(int, int, int, int);
static void    f2(void);

static jmp_buf    jmpbuffer;
static int        globval;

int
main(void)
{
    int                autoval;
    register int    regival;
    volatile int    volaval;
    static int        statval;

    globval = 1; autoval = 2; regival = 3; volaval = 4; 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);
    }

    /*
     * Change variables after setjmp, but before longjmp.
     */
    globval = 95; autoval = 96; regival = 97; volaval = 98;
    statval = 99;

    f1(autoval, regival, volaval, statval);    /* never returns */
    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(void)
{
    longjmp(jmpbuffer, 1);
}

 运行结果:

in f1():
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99 

         将希望返回的位置调用一次 setjmp,然后通过 longjmp 可以跳跃回 setjmp 的地方。

        在设置这个栈跳跃点的时候setjmp 返回的是0,

        在调用 longjmp 跳跃到栈点时,setjmp 返回的是longjmp的二参,这样可以方便判断这个跳跃是从哪里来的

        setjmp 的参数是一个特殊类型的 jmp_buf  的变量,在调用 longjmp 时需要一个保存了栈信息的变量,就用 jmp_buf 数据类型保存栈的信息。

        自动变量、寄存器变量、易变变量是否会出现值得回滚(也就是上例中的1、2、3、4、5)?

        根据上例的结果看是不会回滚的,但是还要看情况,大多数的实现不会回滚自动变量和寄存器变量中的值,但这都不一定。如果有一个自动变量,我们不想让他回滚,那么我们可以加一个volatile 属性。声明为static 或者是 全局变量,在执行longjmp时保持不变。

注意:

        1、如果将上述程序进行1级优化,得到的结果如下:

gcc -O testjmp.c

in f1():
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 2, regival = 3, volaval = 98, statval = 99

        -O、-O1:优化编译需要更多时间,并且大型函数需要更多内存。使用-O选项,编译器会尝试减小代码尺寸减少执行时间,不执行任何需要大量编译时间的优化。

        经过优化之后,自动变量 和 寄存器变量现在在寄存器中,在调用longjmp之后,恢复为之前setjmp的栈环境,这时如果一直在内存中的数据就会改变,在寄存器中的数据不改变。 

       2、书中举了一个局部变量作为缓冲区的错误例子:

GCC -O 优化等级详解

 

7、getrlimit 函数 和 setrlimit 函数   

 注意:

        进程资源的限制,会影响到子进程的继承。为了影响一个用户的所有后续进程,需将资源限制的设置构造在shell中,可以使用ulimit 或者 limit

#include "apue.h"
#include <sys/resource.h>

#define    doit(name)    pr_limits(#name, name)

static void    pr_limits(char *, int);

int
main(void)
{
#ifdef    RLIMIT_AS
    doit(RLIMIT_AS);
#endif

    doit(RLIMIT_CORE);
    doit(RLIMIT_CPU);
    doit(RLIMIT_DATA);
    doit(RLIMIT_FSIZE);

#ifdef    RLIMIT_MEMLOCK
    doit(RLIMIT_MEMLOCK);
#endif

#ifdef RLIMIT_MSGQUEUE
    doit(RLIMIT_MSGQUEUE);
#endif

#ifdef RLIMIT_NICE
    doit(RLIMIT_NICE);
#endif

    doit(RLIMIT_NOFILE);

#ifdef    RLIMIT_NPROC
    doit(RLIMIT_NPROC);
#endif

#ifdef RLIMIT_NPTS
    doit(RLIMIT_NPTS);
#endif

#ifdef    RLIMIT_RSS
    doit(RLIMIT_RSS);
#endif

#ifdef    RLIMIT_SBSIZE
    doit(RLIMIT_SBSIZE);
#endif

#ifdef RLIMIT_SIGPENDING
    doit(RLIMIT_SIGPENDING);
#endif

    doit(RLIMIT_STACK);

#ifdef RLIMIT_SWAP
    doit(RLIMIT_SWAP);
#endif

#ifdef    RLIMIT_VMEM
    doit(RLIMIT_VMEM);
#endif

    exit(0);
}

static void
pr_limits(char *name, int resource)
{
    struct rlimit        limit;
    unsigned long long    lim;

    if (getrlimit(resource, &limit) < 0)
        err_sys("getrlimit error for %s", name);
    printf("%-14s  ", name);
    if (limit.rlim_cur == RLIM_INFINITY) {
        printf("(infinite)  ");
    } else {
        lim = limit.rlim_cur;
        printf("%10lld  ", lim);
    }
    if (limit.rlim_max == RLIM_INFINITY) {
        printf("(infinite)");
    } else {
        lim = limit.rlim_max;
        printf("%10lld", lim);
    }
    putchar((int)'\n');
}

运行结果:

RLIMIT_AS       (infinite)  (infinite)
RLIMIT_CORE              0  (infinite)
RLIMIT_CPU      (infinite)  (infinite)
RLIMIT_DATA     (infinite)  (infinite)
RLIMIT_FSIZE    (infinite)  (infinite)
RLIMIT_MEMLOCK       65536       65536
RLIMIT_MSGQUEUE      819200      819200
RLIMIT_NICE              0           0
RLIMIT_NOFILE         1024     1048576
RLIMIT_NPROC          3611        3611
RLIMIT_RSS      (infinite)  (infinite)
RLIMIT_SIGPENDING        3611        3611
RLIMIT_STACK       8388608  (infinite)

         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值