目录
进程概念
查看进程
进程的特点(理解)
进程的资源分配(理解)
进程的状态(记住)
进程管理--PID
进程间关系(重点)
函数名:getpid()
函数名:getppid()
创建进程
函数名:fork()
函数名:vfork()
结束进程
函数名:_exit()
函数名:exit()
特殊进程
等待进程
函数名:wait()
函数名:waitpid()
进程概念
运行状态下的(硬盘中存储)程序(静态)----(内存中运行)进程(动态)
查看进程
Windows 任务管理器
2.2.2 Linux 终端指令
top #动态监控
ps -ef #显示系统中所有进程的详细信息
进程的特点(理解)
动态性:进程的实质是一次程序运行的过程,进程是动态产生、动态消亡的。
并发性:任何进程都可以和其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的基本单位
异步性:进程的执行具有间断性,多个进程间按照各自独立的不可预知的速度向下运行
进程的资源分配(理解)
系统会给每个进程开辟 4G 的虚拟内存( =3G 用户内存+1G 的内核内存)
----当物理内存中的数据需要参与时,便加载到4G的虚拟内存中
- 操作系统在为进程分配内存空间时,将其划分为堆、栈、数据段、未初始化数据段和文本段这五大区域。以达到基于内存管理和程序运行时数据组织有效划分的目的。
堆:手动开辟
栈:局部变量
数据段:全局变量和静态变量
未初始化数据段:默认值为 0
初始化数据段:会初始化设置的初始值
文本段:常量和代码
进程的状态(记住)
执行态:进程正在占用 CPU(CPU 正在执行该进程)
就绪态:进程已经满足本 CPU 分配时间片的条件(可以被 CPU 执行),在等待 CPU 给他分配时间片
等待态:进程没有满足被 CPU 分配时间片的条件,需要等到条件满足 CPU 才会给它分配时间片
进程管理--PID
一般情况下都是通过进程的 PID 号来操作进程
进程间关系(重点)
父子关系:进程 2 是由进程 1 创建的,那么进程 1 就是进程 2 的父进程,进程 2 就是进程 1 的子进程。 进程间有且只有父子关系
函数名:getpid()
头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:pid_t getpid(void);
函数功能:获取进程 pid
函数参数:无
函数返回值:返回 PID 号
函数使用:
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid = getpid();
printf("pid=%d\n",pid);
sleep(10);//拉长时间,验证
return 0;
}
查找进程,验证。可以使用
ps -ef | grep "./a.out"
函数名:getppid()
头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:pid_t getppid(void);
函数功能:获取父进程 pid
函数参数:无
函数返回值:获取的 PPID
函数使用:
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t ppid = getppid();
printf("ppid=%d\n",ppid);
sleep(10);//拉长时间,验证
return 0;
}
创建进程
方法一:直接运行可执行程序
方法二:在程序中去调用进程创建函数去创建进程--fork、vfork。
函数名:fork()
子进程复制父进程资源,然后和父进程从同一位置向下运行,执行的先后关系不确定
头文件:
#include<sys/types.h>
#include<unistd.h>
函数原型:pid_t fork(void);
函数功能:创建子进程
函数参数:无
函数返回值:-1 代表子进程创建失败,0 代表子进程,大于 0 代表父进程
函数使用:
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main(){
pid_t pid =0;
pid= fork();
if(pid<0){
perror("fork");
return 0;
}
else if(pid==0){
printf("pid=%d\n",getpid());
printf("ppid=%d\n",getppid());
}else{
printf("this pid=%d\n",getpid());
}
return 0;
}
函数名:vfork()
子进程占用父进程资源,然后子进程先使用资源执行,子进程结束后,父进程才能使用资源
头文件:
#include<sys/types.h>
#include<unistd.h>
函数原型:pid_t vfork(void);
函数功能:创建子进程,返回子进程 PID
函数参数:无
函数返回值:父进程返回子进程 PID 子进程返回 0
函数使用:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(){
pid_t pid; // 定义进程 ID 变量
pid = vfork(); // 创建子进程,并获取子进程的进程 ID
if(pid<0){ // 如果创建子进程失败
perror("vfork"); // 打印错误信息
return 1; // 程序返回 1 并结束
}else if(pid==0){ // 如果是子进程
printf("pid=%d ",pid); // 打印子进程的进程 ID
printf("子进程先执行\n"); // 输出提示信息
sleep(2); // 子进程暂停 2 秒
_exit(0); // 子进程退出
}else{ // 如果是父进程
printf("pid=%d ",pid); // 打印子进程的进程 ID
printf("父进程等待子进程结束\n"); // 输出提示信息
sleep(4); // 父进程暂停 4 秒
}
return 0; // 程序正常结束返回 0
}
当 vfork
被调用时,确实创建了一个新的子进程。在父进程中,pid
存储了新创建子进程的进程 ID(非 0 值),然后执行父进程中对应的代码块(第三个代码块)。
而在新创建的子进程中,pid
的值为 0 ,子进程执行第二个代码块。
需要注意的是,vfork
会让子进程先运行,并且子进程要尽快使用 exec
系列函数或 _exit
函数,,因为子进程和父进程共享地址空间。
结束进程
方法一:外部信号
在终端 ctrl+c 可以结束进程
在终端输入:kill -9 PID 可以结束进程
方法二:内部程序
1. main()中调用 return
2. 进程退出函数---exit()、_exit()
函数名:_exit()
头文件:#include<unistd.h>
函数原型:void _exit(int status);
函数功能:退出进程,不清空缓冲区
函数参数:获取进程退出时的状态 一般填 0
函数返回值:无
函数使用:
#include <unistd.h>
#include <stdio.h>
void write_to_buffer() {
printf("This is written to the buffer.");
}
int main() {
write_to_buffer();
_exit(0);
return 0;
}
函数名:exit()
头文件:#include
函数原型:void exit(int status);
函数功能:退出进程,清空缓冲区
函数参数:获取进程退出时的状态 一般填 0
函数返回值:无
函数使用:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
void _EXIT_();
int main(){
pid_t pid =0;
pid= fork();
if(pid<0){
perror("fork");
return 0;
}
else if(pid==0){
sleep(3);
printf("B");
_EXIT_();
printf("5678\n");
}else{
sleep(8);
printf("A");
exit(0);
}
return 0;
}
void _EXIT_(){
exit(0);
//return ;//5678
printf("1234\n");
}
特殊进程
0 号进程:操作系统启动的引导程序 祖先进程
1 号进程:操作系统启动的第一个程序
-
孤儿进程: 父进程死了,子进程还在运行。孤儿进程会被系统中设置好的一个进程收养,并且等孤儿进程 死了之后给它收尸 - 僵尸进程: 子进程死了,父进程没有给自己成收尸。这个时候子进程就会变成一个僵尸进程,僵尸进程所 占用的资源基本上会被完全释放,除了它所占用的进程号。父进程在退出之前,一定会给所有的僵尸进程收 尸。但是有些工程中的父进程需要长时间不停机运行,会导致僵尸进程的资源无法得到及时的回收处理。所以 咱们在编写代码的过程中,一定要尽量避免僵尸进程的产生。 可以通过在父进程中调用,下面的进程的等待函数来避免僵尸进程的产生。
等待进程
函数名:wait()
头文件:
#include<sys/types.h>
#include<sys/wait.h>
函数原型:pid_t wait(int *wstatus);
函数功能:阻塞等待任意子进程结束
函数参数:进程结束状态 一般填 NULL
函数返回值:返回结束的子进程 pid
函数使用:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid;
pid = fork(); // 创建子进程
if (pid < 0) { // 子进程创建失败
perror("Fork failed");
return 1;
} else if (pid == 0) { // 子进程
printf("I'm the child process!\n");
sleep(3); // 子进程暂停 3 秒
_exit(0); // 子进程退出
} else { // 父进程
int status;
pid_t child_pid = wait(&status); // 父进程等待子进程结束
if (child_pid == -1) { // 等待失败
perror("Wait failed");
return 1;
}
printf("Child process %d exited. Status: %d\n", child_pid, status);
}
return 0;
}
这时候,我有一个问题:fork+wait=vfork?
答案:不对,
fork
+wait
并不等同于vfork
。
fork
会创建一个新进程,子进程拥有父进程的完整副本,包括数据空间、堆、栈等。父进程和子进程可以并发执行。
vfork
创建的子进程会直接使用父进程的地址空间,并且子进程必须尽快调用exec
系列函数或者_exit
函数来替换当前进程的地址空间或者退出,否则可能会导致错误。
wait
函数用于父进程等待子进程结束并获取子进程的状态。
vfork
虽然也是创建子进程,但它与fork
的行为有很大的不同,不能简单地认为fork
加上wait
就等于vfork
。
函数名:waitpid()
头文件:
#include<sys/types.h>
#include<sys/wait.h>
函数原型:pid_t waitpid(pid_t pid, int *wstatus, int options);
函数功能:等待子进程结束
函数参数:
-
pid
:- 负数:表示等待进程组 ID 等于该负数绝对值的任意子进程。
- 0:等待当前进程组中的任意子进程。
- 正数:等待指定进程 ID 的子进程。
-
wstatus
:这是一个整数指针,用于接收子进程的退出状态信息。通过对这个状态值的解析,可以获取子进程退出的原因、是否正常退出等详细信息。通常,如果您不关心具体的状态细节,可以将其设置为NULL
。 -
options
:这是一个位掩码,用于控制waitpid
的行为。WNOHANG
:表示非阻塞等待。如果没有子进程结束,函数会立即返回 0 ,而不会阻塞。- 0:表示阻塞等待,直到指定的子进程结束。
函数返回值:
- 如果成功等待到指定子进程结束,返回结束子进程的进程 ID 。
- 如果设置了
WNOHANG
选项且没有子进程结束,返回 0 。 - 如果出现错误,返回 -1 ,同时会设置
errno
来指示错误类型。
函数使用:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid;
pid = fork(); // 创建子进程
if (pid < 0) { // 子进程创建失败
perror("Fork failed");
return 1;
} else if (pid == 0) { // 子进程
printf("I'm the child process!\n");
sleep(3); // 子进程暂停 3 秒
_exit(0); // 子进程退出
} else { // 父进程
int status;
// 阻塞等待指定子进程结束
pid_t result = waitpid(pid, &status, 0);
if (result == -1) {
perror("Waitpid failed");
return 1;
}
printf("Child process %d exited. Status: %d\n", pid, status);
// 非阻塞等待当前进程组中的任意子进程结束
result = waitpid(-1, &status, WNOHANG);
if (result > 0) {
printf("Another child process exited. PID: %d, Status: %d\n", result, status);
} else if (result == 0) {
printf("No child process exited yet.\n");
} else {
perror("Waitpid (WNOHANG) failed");
return 1;
}
}
return 0;
}