往文件里面写内容,fork在open之前,会接着父进程写的内容写,继承父进程的状态信息,文件描述符一直往下
往文件里面写内容,fork在open之后,分别打开文件,后运行的会覆盖先运行的内容
让低8位有效
进程的终止:8种情况
1)main 中 return
2)exit() //库函数
c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。
注册清理函数(atexit)。
3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。 //系统调用
//4) 主线程退出
//5)主线程调用pthread_exit
异常终止:
6)abort()
7)signal kill pid
//8) 最后一个线程被pthread_cancle
僵尸进程:
进程执行结束但空间未被回收变成僵尸进程
1.exit 库函数
退出状态,终止的进程会通知父进程,自己使如何终止的。
如果是正常结束(终止),则由exit传入的参数。
如果是异常终止,则有内核通知异常终止原因的状态。
任何情况下,父进程都能使用wait,waitpid获得这个状态,以及资源的回收。
void exit(int status)
exit(1);
功能:
让进程退出,并刷新缓存区
参数:
status:进程退出的状态
返回值:
缺省
EXIT_SUCCESS 0
EXIT_FAILURE 1
return 当该关键字出现在main函数中时候可以结束进程
如果在其他函数中则表示结束该函数。
exit -> 刷新缓存区 -> atexit注册的退出函数 -> _exit
2._exit 系统调用
void _exit(int status);
功能:
让进程退出,不刷新缓存区
参数:
status:进程退出状态
返回值:
缺省
mian(int agc,argv)
{
main();
}
回调函数
3.atexit
int atexit(void (*function)(void));
功能:
注册进程退出前执行的函数
参数:
function:函数指针
指向void返回值void参数的函数指针
返回值:
成功返回0
失败返回非0
当程序调用exit或者由main函数执行return时,所有用atexit
注册的退出函数,将会由注册时顺序倒序被调用
注意:
a. 是exit函数调用时,会调atexit函数
_exit函数调用时,不会调到atexit
b. atexit 程序正常结束
(1).main 返回 //exit
(2).exit()
c.atexit函数 可以多次注册
d.最后"退出清理函数"的调用顺序,与注册顺序相反。
3.进程空间的回收
exit(20);----
void exit(int status);
/系统镜像
img == 镜像
exec
int exec l(const char *path, const char *arg, ...);
int exec l p(const char *file, const char *arg, ...);
int exec l e(const char *path, const char *arg,..., char * const envp[]);
int exec v(const char *path, char *const argv[]);
int exec v p(const char *file, char *const argv[]);
int exec v pe(const char *file, char *const argv[], char *const envp[]);
int exec l(const char *path, const char *arg, ...);
int exec v(const char *path, char *const argv[]);
l --- list
//ls -l /
path ---文件路径
"/bin/ls"
arg ---> 要执行的 文件的名字
"ls"
"-l"
"/"
NULL 表示结束
v --- vector (向量) victor
p --- path //PATH (可执行文件路径)
execute
exec族(环境变量用NULL作为终止符才能在子程序中运行)
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),
子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的
用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建
新进程,所以调用exec前后该进程的id并未改变。
其实有六种以exec开头的函数,统称exec函数:
vector
ls -l -i list
execl("/bin/ls","-l","-i",NULL);
execlp("ls","-l","-i",NULL);
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
key=value
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char*path,char*const argv[],char*const evnp[]);
int execlp(const char *file, const char *arg, ...);
echo $PATH
PATH=
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
这些函数的区别
1),前4个使用路径名作为参数,后面两个使用文件名做参数
当filename中,含有/时视为路径名,否则就按PATH变量,在指定目录下查找可执行文件。
2)相关的参数表传递
l表示list,v表示vector
execl,execlp,execle,需要将参数一个一个列出,并以NULL结尾。
execv,execvp,execve,需要构造一个参数指针数组,然后将数组的地址传入。
3)以e结尾的函数,可以传入一个指向环境字符串的指针数组的指针。其他未指定环境变量,使用父进程继承过来的。
execve 是真正的系统调用
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错
则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
在 C 语言中,当你使用 exec* 系列函数(如 execve, execl, execle, execvp, execvpe 等)来执行一个新的程序时,环境变量通常需要以 NULL 指针作为数组的终止符。这是因为 exec* 函数期望环境变量以一个字符串数组的形式传递,并且这个数组的最后一个元素必须是 NULL 指针。为什么需要 NULL 终止符1. 数组的边界:•在 C 语言中,数组的边界通常通过 NULL 指针来确定。NULL 指针作为数组的最后一个元素,告诉函数处理到此处为止。2. 环境变量的格式:•环境变量通常是以字符串数组的形式传递给程序的。每个环境变量本身是一个字符串,例如 "VAR=value"。为了指示环境变量数组的结束,需要一个 NULL 指针。
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);
-----------------------------
注意:
路径加文件名
1.excle
带l的默认会搜索,当前路径下。
2.带p的只搜索
只搜索PATH系统环境变量
-----------------------------
int exec l (const char *path, const char *arg, ...);
int exec l p (const char *file, const char *arg, ...);
int exec l e (const char *path, const char *arg,..., char * const envp[]);
int exec v (const char *path, char *const argv[]);
int exec v p (const char *file, char *const argv[]);
int exec v p e(const char *file, char *const argv[],char *const envp[]);
(1).带l vs 带 v
int execl (const char *path, const char *arg, ...); //list
int execv (const char *path, char *const argv[]);//vector ---
功能:
执行一个文件 (可执行文件 a.out / 1.sh )
image 映像/镜像
text|data|bss|堆 栈|环境变量及命令行参数 + pcb
用新进程的 镜像 替换 调用进程的 镜像
参数:
@path 要执行的文件的路径(包含可执行文件的名字)
eg:
ls
/bin/ls
@arg 表示 可执行文件的文件名
ls
... 可变参数
ls -l /
最后写一个NULL 表示结束
ls -l /
execl("/bin/ls","ls","-l","/",NULL);
区别:
在于,参数传递的方式不同,
l --- list ---参数逐个列举
eg:
execl("/bin/ls","ls","-l","/",NULL);
v ---vector --- 参数组织成 指针数组的形式
eg:
char *const arg[] = {"ls","-l","/",NULL};
execv("/bin/ls",arg);
2). p vs e
int execl (const char *path, const char *arg, ...); //list
int execv (const char *path, char *const argv[]);//vector ---
int execl p (const char *file, const char *arg, ...);
int execv p (const char *file, char *const argv[]);
int execl e (const char *path, const char *arg,..., char * const envp[]);
int execvp e(const char *file, char *const argv[] , char * const envp[]);
p --- path ->PATH (环境变量 --- 都是可执行文件的路径)
带p 表示可执行文件的寻找方式,是从系统的环境变量PATH中的路径下面去找
带e 表示的是可以给要执行的 新程序 传递需要的 环境变量
extern char **environ; //系统的环境变量信息 的指针数组的首地址
int execle (const char *path, const char *arg,..., char * const envp[]);
int execvpe(const char *file, char *const argv[] , char * const envp[]);
[ ] //字符串的的 首地址
["HOME"="/linux/home"]
["shell"="/bin/bash" ]
...
真正的系统调用:
execve
进程执行:
//1.父进程干相同的事情
fork
eg:
12306
//2.创建的子进程
进行新程序的执行
fork + exec
bash
|
vfork
/ \
bash(f) bash+exec(a.out) //virtual (虚拟)