我们的shell其实就相当于进程替换,学了进程替换就可以手搓命令行了
目录
一、函数退出与进程结束
- main 函数退出:
- 在 C/C++ 程序中,当
main
函数执行完毕时,进程通常会结束。这是最常见的进程结束方式之一。例如:
- 在 C/C++ 程序中,当
int main() {
// 一些代码
return 0;
}
- 当
main
函数中的代码执行完成,并且没有其他后台进程在运行时,整个程序的执行就会终止。
- exit 函数表示进程结束:
exit
函数可以在代码的任何地方被调用,以立即终止进程的执行。它会进行一些清理工作,如关闭文件描述符、刷新缓冲区等。例如:
void someFunction() {
// 在某个函数中调用 exit
exit(0);
}
- _exit 与 exit 的区别及缓冲区问题:
_exit
和exit
都用于终止进程,但它们在处理缓冲区方面有所不同。_exit
会直接丢弃缓冲区内容,而exit
会先打印缓冲区内容。这是因为exit
在终止进程之前会进行一些清理工作,包括刷新缓冲区。- 在 C/C++ 中,语言级缓冲区不在操作系统内部。例如,当使用
printf
输出内容时,数据可能会先被存储在缓冲区中,直到缓冲区满或者遇到换行符等情况才会被输出到屏幕上。如果使用_exit
,这些未输出的内容可能会被丢弃。而使用exit
,则会确保缓冲区中的内容被输出。
二、进程等待
-
一般情况:
- 父进程创建子进程后,通常需要等待子进程结束,以便回收子进程的资源和获取子进程的退出状态。
- 等待的时候,子进程不退,父进程就要阻塞在
wait
内部。
-
wait 函数:
wait
函数用于等待子进程结束,并返回子进程的 PID。它会阻塞父进程,直到有一个子进程结束。- 例如:
pid_t pid = fork();
if (pid == 0) {
// 子进程代码
exit(0);
} else {
int status;
pid_t child_pid = wait(&status);
// 处理子进程的退出状态
}
- waitpid 函数:
waitpid
函数提供了更多的控制选项。它可以指定要等待的子进程的 PID,以及设置等待的方式(阻塞或非阻塞)。- 参数说明:
pid
:大于 0 时指定要等待的子进程的 PID;等于 -1 时表示等待任意子进程。status
:用于获取子进程的退出信息,不仅仅包含进程退出码。第八位到 16 位是退出码,可以通过(status>>8)&0xFF
获取退出码;通过status&0x7F
获取退出信号。options
:默认是 0,表示阻塞等待;可以设置为WNOHANG
表示非阻塞等待,进行轮询检测。
- 返回值:
pid_t > 0
表示等待成功,返回目标子进程的 PID;==0
表示等待成功,但子进程没有退出;<0
表示等待失败。
三、程序替换
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/wait.h>
4 #include<unistd.h>
5 #include<stdlib.h>
6 int main(){
7 pid_t id = fork();
8 if(id == 0)
9 {
10 execlp("ls","ls","-l","-a",NULL);
11 exit(0);
12 }
13 return 0;
14 }
15
以上是进程替换的一个代码,我创建的excepro实现了系统的ls -a -l功能
-
进程替换不是创建新进程:
- 程序替换(如使用
execl
、execv
、execlp
、execvpe
等函数)是在现有进程的基础上,用新的程序替换当前进程的代码和数据,而不是创建一个新的进程。 - 例如,使用
execl
成功时是没有返回值的,因为当前进程的代码已经被新的程序替换,不会再回到原来的代码执行。
- 程序替换(如使用
-
不同的程序替换函数:
execv
:接受一个字符串数组作为参数,其中第一个元素是要执行的程序的名称,后续元素是程序的参数。execl
:接受可变数量的参数,最后一个参数必须是NULL
。execlp
:与execl
类似,但会在PATH
环境变量中查找要执行的程序,不要求带路径。execvpe
:可以传递环境变量,使用全新的环境变量。
-
环境变量的处理:
- 子进程默认会继承父进程的全部环境变量。
- 如果要传递全新的环境变量,可以使用一些方法,如
putenv
将char*
类型的环境变量添加到环境变量表中。 - 实际上,没有必要专门使用
execvpe
来传递环境变量,因为putenv
等方法可以让子进程继承并修改环境变量。
-
程序替换的特点:
- 程序替换不影响命令行参数和环境变量,除非在替换过程中明确修改了它们。
- 如果程序替换失败,这些函数会返回 -1。
通过对进程的这些方面的理解,我们可以更好地控制程序的执行流程,管理进程的生命周期,以及实现更复杂的程序逻辑。