进程终止
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 传递给它所调用的其他函数? 没有
.。。。。。。
剩下不看了 赶时间