fork() 会复制父进程,生成一个几乎一模一样的子进程 ; 单纯 fork 出来的子进程,代码、数据和父进程完全相同,只能执行父进程现存代码。 exec 系列函数的作用:把当前进程彻底替换成全新程序。
exec 一族(execl/execlp/execv/execvp 等): 不创建新进程!在当前 PID 内部,覆盖整个地址空间,用新进程的镜像 替换 原有进程的镜像
-
丢弃原来的代码段、数据、堆
-
加载磁盘上指定的可执行文件
-
从新程序入口点开始运行
进程 = [text|data|bss|堆栈] + pcb
----镜像-----------int exec l(const char *path, const char *arg, ...);
int exec v(const char *path, char *const argv[]);@path ---表示要执行的文件的 路径 --- 要求路径名中 要包含可执行文件名
@arg ---表示的是可执行文件的名字
@... ---可变参数 注意: 最终以NULL作为结尾
这块往往给的是,可执行文件需要用到的一些参数
execl与execv的区别:
list --- 逐个罗列,从第二个参数开始,逐个罗列
vector --- 表示 从第二个参数开始 组织成 数组的形式
表示的是 从第二个参数开始 参数的给定方式
使用execl查看根目录下的文件:
execl("/bin/ls","ls","-l","/",NULL);
第一个参数,为可执行文件的路径,也就是ls命令的路径,为/bin/ls。第二个参数就是可执行文件的名字,就是ls本身,之后为可变长的执行命令参数,-l为查看全部,/为根目录,最后以NULL进行结尾
而execl的使用方法,就是将第二个参数,包含需要执行的文件的名字、执行该文件需要的参数,全部打包成一个指针类型的数组,存入其中,然后将字符型指针数组的首地址,传给execl即可
char *const args[] = {"ls","-l","/",NULL};
execv("/bin/ls",args);
执行效果与execl相同,因为我测试的时候并没有改变执行的命令,只是改变了执行方式

which 命令
which:查找可执行程序 在磁盘上的真实路径,顺着 PATH 环境变量搜索,只找二进制命令,不找内置 shell 命令、alias 别名。
基础用法;
which ls

execlp与execvp的使用
带P --- PATH
表示 寻找可执行文件,到系统的环境变量PATH中寻找
此时不需要指定路径
@file参数 --只需要指定可执行文件的名字即可
int execlp ( const char *file, const char *arg, ...);
int execvp ( const char *file, char *const argv[]);
Linux 的命令分两大类:外部命令(磁盘上真实可执行文件) 、Shell 内置命令。
✅ 只有外部命令,磁盘上存在独立可执行程序,才会被 PATH 搜索,which /execvp 能找到并执行。 ❌ shell 内置命令、别名 alias、shell 函数,不在磁盘,PATH 找不到,
which查不到,execvp也不能直接执行。
一、属于外部命令(PATH 可搜索)
这些命令磁盘上有二进制文件,which xxx 可以打出路径,execvp("命令名",args) 可以直接跑。 示例:
ls、cat、cp、mv、rm、mkdir、rmdir、gcc、g++、python3、ping、ps、top、
grep、find、chmod、chown、tar、zip、unzip、whoami、hostname、date、
wc、sort、awk、sed、less、more
存放位置常见目录(PATH 里面的目录)
/usr/bin绝大多数用户命令/bin系统基础命令/usr/sbin、/sbin管理员命令
二、Shell 内置命令(PATH找不到,execvp 不能直接调用)
这些命令没有磁盘可执行文件,是 bash 程序内部实现,execvp 执行会报找不到文件。 常见内置:
cd、exit、pwd、umask、echo(部分版本)、history、jobs、
fg、bg、set、unset、source(. )
使用execlp执行cp命令
execlp("cp","cp","execl.c","5.txt",NULL);

execvp的使用方法与execv类似,就是将第二参数中的命令参数放入数组,使用数组传参
char *const argx[] = {"cp","execl.c","5.txt",NULL};
// execlp("cp","cp","execl.c","5.txt",NULL);
execvp("cp",argx);
最后两个为execvpe和execle,带e --- enviroment ---环境变量
如果要执行的文件需要使用到环境变量则,可以通过带e的这两个函数传递环境变量个可执行文件
int exec l e(const char *path, const char *arg,..., char * const envp[]);
int exec vp e(const char *file, char *const argv[], char *const envp[]);
使用execvpe打印环境变量:
char * const envp[] = {"USR=linux","PASSWD=123456",NULL};
execvpe("env",args,envp);

分割字符串函数:strtok
char *strtok(char *str, const char *delim);
字符串分割,按分隔符delim把字符串切分成一个个子串
- 参数
str:待切割的字符串。第一次调用传原字符串;后续调用传 NULLdelim:分隔符集合,可以是多个分隔字符
- 返回值:返回分割出来的子串指针;分割完毕返回
NULL - strtok 会直接修改原字符串 ,把分隔符位置改写为
'\0',原字符串必须可写,不能传字符串字面量"abc,def"(只读段会崩溃)。
示例:分割操作完成后buf中的全部逗号都被替换成了'\0'的字符串分隔符
#include <stdio.h>
#include <string.h>
int main(void)
{
char buf[] = "apple,banana;orange grape";
char *p;
// 第一次传入原buf
p = strtok(buf, ",; ");
while(p != NULL)
{
printf("%s\n", p);
// 后续全部传NULL
p = strtok(NULL, ",; ");
}
return 0;
}
进程终止的8种情况。
1、main函数执行完毕return返回值
2、exit执行结束,清理io库的所有流文件,注册清理函数
3、_exit执行,直接关闭所有文件流,不执行清理。
4、主线线程退出,类似main函数return结束
5、主线程调用pthread_exit结束,类似kill命令中止进程
异常中止:
6、调用abort()结束
7、接收到kill发送的结束信号,如kill -pid -9;
8、最后一个线程被pthread_cancel取消中止
进程结束:
1、正常rerun结束返回
main函数完成、exit返回、_exit返回、主线程退出、主线程调用了pthread_cancel
2、异常结束接受了外部信号
abort调用结束、接收kill信号结束
退出函数exit与_exit
用户调用函数exit原型示例
void exit(int status)
exit(1);
功能:
让进程退出,并刷新缓存区
参数:
status:进程退出的状态
返回值:
缺省
EXIT_SUCCESS 0
EXIT_FAILURE 1
exit的执行流程:exit -> 刷新缓存区 -> atexit注册的退出函数 -> _exit
系统调用函数_exit:
void _exit(int status);
功能:
让进程退出,不刷新缓存区
参数:
status:进程退出状态
返回值:
缺省
回调函数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.最后"退出清理函数"的调用顺序,与注册顺序相反。
回收子进程wait与waitpid
阻塞式回收wait
pid_t wait(int *status);//wait 来获取到
功能:
该函数可以阻塞等待任意子进程退出
并回收该进程的资源和获取退出状态值。
一般用于父进程回收子进程状态。
参数:
status 进程退出时候的状态
如果不关心其退出状态一般用NULL表示
// 如果要回收进程退出状态,则用WEXITSTATUS回收。
返回值:
成功 回收的子进程pid
失败 -1
解析进程返回的状态值:
| 宏 | 说明 | 使用前提 |
|---|---|---|
WIFEXITED(wstatus) |
子进程正常退出(exit/_exit/main return) | 返回非 0 代表正常结束 |
WEXITSTATUS(wstatus) |
拿到子进程退出码 (exit (code) /return code) | 只有 WIFEXITED 为真才能调用 |
WIFSIGNALED(wstatus) |
子进程被信号杀死(段错误、kill 等) | 返回非 0 代表被信号终止 |
WTERMSIG(wstatus) |
获取杀死进程的信号编号 | 只有 WIFSIGNALED 为真才能调用 |
WCOREDUMP(wstatus) |
是否生成 core dump 核心转储文件 | 配合 WIFSIGNALED,部分平台支持 |
WIFSTOPPED(wstatus) |
子进程被暂停(SIGSTOP) | 开启 WUNTRACED 选项才有效 |
WSTOPSIG(wstatus) |
导致暂停的信号号 | WIFSTOPPED 为真使用 |
WEXITSTATUS 只能在正常退出时用;如果进程是被信号干掉,调用它结果无意义。
可调式回收 pid_t waitpid
@参数pid的值:
<-1 回收指定进程组内的任意子进程
-1 回收任意子进程,组内外
0 回收和当前调用waitpid一个组的所有子进程,组内
>0 回收指定ID的子进程
参数:
status 子进程退出时候的状态,
如果不关注退出状态用NULL;
options 选项:
0 表示回收过程会阻塞等待
WNOHANG 表示非阻塞模式回收资源。
返回值:
成功 返回接收资源的子进程pid
非阻塞是 返回值为 0 表示 没有任何子进程状态改变
失败 -1