APUE学习:第七章 进程环境

进程终止

1.退出函数

_exit和_Exit立即进入内核

exit先执行一些清理处理,然后返回内核(对于所有打开流调用fclose,使得所有数据被冲洗(写道文件上))

exit(0) 等价于 return 0

2.atexit函数

 

对atexit函数的理解:

相当于一个存放函数的栈(先入后出),栈中存放的函数不能带参数且最大容量为32,调用atexit函数 可以把一个不带参数的函数放入栈,那么什么时候出栈呢?当调用exit的时候,栈中函数陆续出栈并执行。

示例代码:

#include"apue.h"
static void my_exit1();
static void my_exit2();

int main()
{
    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()
{
    printf("first exit handler\n");
}

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

C程序的存储空间布局

分为:正文段、初始化数据段、未初始化数据段、栈、堆

正文段:由CPU执行的机器指令部分。通常是只读的。

初始化数据段:包含了程序中明确地赋初值的变量。eg:已经赋过值的全局变量

int maxcount=99; //存储于初始化数据段
int main()
{

    return 0;
}

上面代码中的maxcount就存储于初始化数据段

未初始化数据段(bss):已经申明了变量但未赋值。

栈:

存放自动变量(chatGPT解释为函数内部定义的变量)以及每次函数调用时保存的信息(函数的返回地址、返回值等)

堆:通常再堆中进行动态分配,例如malloc函数开辟的内存就在堆区中。

tips:只有正文段和初始化数据段需要放在磁盘文件中;未初始化数据段不需放入磁盘文件,在程序运行前自动将它们初始化为0。

size函数报告正文段、数据段和bss端的长度

存储空间分配

环境变量

代码示例: 

对于环境变量的修改: 

执行跳转功能——setjmp和longjmp

功能:实现非局部性goto

非局部指的是什么??

普通的goto语句只能在一个函数内实现跳转,而setjmp和longjmp可以在栈上跳过若干的栈帧

在希望返回的位置调用setjmp,env存放在调用longjmp时能用来恢复栈状态的所有信息,通常设置为全局变量

longjmp的第二个参数为返回setjmp处时,setjmp函数的返回值(可能有多个longjmp,依靠不同的返回值来判断是从哪个位置返回的)

eg:

可以在do_line和cmd_add处出错时使用longjmp,并设置不同的val来区分

执行main函数时调用setjmp会返回0

代码示例:

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

jmp_buf jmpbuffer;

int main()
{

    char line[MAXLINE];
    int val = setjmp(jmpbuffer);
    switch(val)
    {
    case 0:break;
    case 1:printf("do_line error");break;
    case 2:printf("cmd_add error");break;
    
    }
    ......

}

假设cmd_add发生了错误

则cmd_add调用longjmp返回前的栈帧:

:

调用longjmp返回setjmp后的的栈帧:

有图可知,直接从cmd_add的栈帧跳回了main函数的栈帧

在调用longjmp返回setjmp处时,自动变量和寄存器变量的状态如何?——看情况

对于普通的自动变量,很明显是清除的(自动变量保存在栈帧中,你栈帧都没了,自动变量不可能保存的)

但是,声明为全局变量或者静态变量的值在执行longjmp时保持不变,用volatile申明的自动变量值也不回滚。= 。=

用volatile申明的变量为易失变量,在编程中用来声明一个变量可能会被意外修改的情况,因此编译器不应该对这个变量进行优化

对于寄存器变量是啥

register声明变量的作用是告诉编译器将该变量存储在寄存器中,以便提高程序的执行速度。

寄存器变量会不会回滚呢?详细的在代码示例后面进行解释。

代码示例:

#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()
{
    int autoval;
    register int regival;//用register申明的为寄存器变量 
    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);
        return 0;
    }
    globval=95;autoval=86;regival=97;volaval=98;statval=99;
    f1(autoval,regival,volaval,statval);

    return 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);
}

 

全局变量、静态变量和易失变量不受优化的影响,在longjmp之后,它们的值为最近所呈现的值

简单解释:

调用longjmp回到setjmp时,存储器中的变量值保持不变CPU和浮点数寄存器中的变量进行回滚

如果不进行优化,那么这五个值(全局变量、自动变量、寄存器变量、易失变量、静态局部变量)都存储在存储器中;如果进行了优化,那么自动变量和寄存器变量都存放在了寄存器中。

总结;如果要编写一个使用非局部跳转(setjmp、longjmp)的可移植(无论编译器是否进行优化)程序,则必须使用volatile属性 。

自动变量存在的问题

示例代码:

有什么问题呢?

databuf数组是一个自动变量,它开辟在函数open_data的栈帧中的一块区域,fp流指针指向该区域,但是函数返回之后,open_data的栈帧就被系统收回了

 

解决办法:

应该使用alloc类函数给databuf数组分配堆区内存,这样即使open_data函数调用返回,空间也不会被收回.

更改进程系统资源限制——getrlimit、setrlimit

 解释参数:

1.rlptr

结构如下

struct rlimit{
    rlim_t rlim_cur;
    rlim_t rlim_max;

};

cur表示当前的限制,max表示cur能够允许的最大值

限制:

a.cur<=max

b.硬限制值可以降低,硬限制值的降低对于普通用户不可逆

c.只有超级用户可以提高硬限制值

2.resource 表示限制资源的编号

示例代码:

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

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

static void pr_limits(char *, int);

int main() {
#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

#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("%-20s ", 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');
}

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

这个#是什么意思???

ISO C的字符串创建符,用于将参数转换为字符串常量。

举个例子,如果您调用doit宏并传递一个变量名,比如:

doit(MAX_VALUE)

在宏展开时,#name将把MAX_VALUE转换为字符串常量"MAX_VALUE",然后传递给pr_limits函数。

 

 习题

7.1 在 Intel x86 系统上,使用 Linux ,如果执行一个输出 “hello, world” 的程序但不调用 exit 或 return ,则程序的返回代码为 13 (用 shell 检查),解释其原因。

main函数不用return,main退出的时候返回的是最后一行代码的返回值

代码示例:

#include <stdio.h>

void main() {
    printf("hello, world\n");
}

这里hello, world\n的长度为13个字节,所以main的返回值为13

### 7.2

**图 7-3 中的 printf 函数的结果何时才被真正输出。**  
当程序处于交互运行方式时,标准输出通常处于行缓冲方式,所以当输出换行符时,上次的结果才被真正输出。如果标准输出被定向到一个文件,而标准输出处于全缓冲方式,则当标准 I/O 清理操作执行时,结果才真正被输出。

7.3

是否有方法不适用(a)参数传递、(b)全局变量这两种方法,将 main 中的参数 argc 和argv 传递给它所调用的其他函数? 没有

.。。。。。。

剩下不看了  赶时间

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值