Linux 系统调用:进程控制函数 fork、wait 等使用详解
目录
- fork()函数
- `fork()` 的返回值
- `fork()` 的功能
- 进程的退出
- 处理函数退出值的宏定义
- 进程回收
- `wait()`函数
- 作用&函数原型
- exit()退出值存储到status
- `waitpid()`函数
- 作用&函数原型
- 获取进程的id以及获取父进程的id
fork()函数
fork()
是 Linux 系统中的系统调用,用于 创建子进程。它是进程管理中最常用的函数,通过调用 fork()
,操作系统会创建一个与当前进程(父进程)几乎完全相同的子进程。
fork()
的返回值
返回值 | 含义 |
---|---|
> 0 | 在父进程中,fork() 返回子进程的 PID。 |
0 | 在子进程中,fork() 返回 0。 |
-1 | 创建子进程失败,通常由于资源不足或系统限制。 |
fork()
的功能
-
创建一个子进程
- 子进程是父进程的副本,继承了父进程的大部分资源,例如打开的文件描述符、环境变量、代码段等。
- 子进程有自己的独立 PID(进程 ID) 和进程控制块(PCB)。
-
返回值用于区分父进程和子进程
- 在父进程中,
fork()
返回子进程的 PID。 - 在子进程中,
fork()
返回 0。 - 如果调用失败(例如系统资源不足),
fork()
返回 -1。
- 在父进程中,
-
fork()底层原理分析
#include
#include
/*
子进程复制了父进程的什么资源
情况1:全局变量,子进程会拷贝一份,父子独立
情况2:局部变量,子进程会拷贝一份,父子独立
情况3:
*/
//全局变量
int num=789;
int main()
{
//局部变量
float num1=58.9;
pid_t id;
//创建一个子进程
id=fork();
if(id>0) //父进程
{
int n=0;
while(1)
{
printf("父进程在运行,全局变量是: %d 局部变量是: %f!
",num,num1);
//偷偷摸摸把num,num1修改,看看子进程的num,num1受不受影响
n++;
if(n==5)
{
num=666;
num1=96.9;
}
sleep(1);
}
}
else if(id==0) //子进程
{
while(1)
{
printf("子进程在运行!,全局变量是: %d 局部变量是: %f
",num,num1);
sleep(1);
}
}
else
{
perror("生子进程失败了!
");
return -1;
}
while(1)
{
}
return 0;
}
进程的退出
结束进程:
💡void exit(int status);
void _exit(int status);
#include
#include
#include
#include // For wait()
int main()
{
// 局部变量
float num1 = 58.9;
pid_t id;
int i;
// 创建一个子进程
id = fork();
if (id > 0) // 父进程
{
int n = 0;
for (int j = 0; j < 10; j++)
{
printf("父进程在运行,子进程 id 为: %d 局部变量是: %f!
", id, num1);
// 偷偷摸摸把 num1 修改,看看子进程的 num1 受不受影响
n++;
if (n == 5)
{
num1 = 96.9;
}
sleep(1);
}
int status;
wait(&status); // 等待子进程退出
if (WIFEXITED(status)) {
printf("子进程正常退出,退出码为: %d
", WEXITSTATUS(status));
}
}
else if (id == 0) // 子进程
{
for (int j = 0; j < 10; j++)
{
printf("子进程在运行! 子进程 id 为: %d 局部变量是: %f
", getpid(), num1);
sleep(1);
}
exit(0); // 让子进程彻底结束
}
else
{
perror("创建子进程失败!
");
return -1;
}
printf("主函数准备退出了!
");
for (i = 0; i < 10; i++)
{
printf("good bye!
");
}
return 0;
}
区别 :
💡 exit结束进程的时候会刷新缓冲区,但是_exit()不会刷新缓冲
正常情况下:和
return
语句都能帮我们刷新缓冲区
return
和exit
的区别:
💡 区别一:
return
是关键字,exit()
是函数区别二:return结束函数,返回返回值 exit()结束进程
#include
#include
#include
int main() {
printf("hello world");
//_exit(0);
exit(0);
return 0;
}
处理函数退出值的宏定义
进程回收
wait()
函数
作用&函数原型
wait
函数用于阻塞父进程,直到任意一个子进程终止。- 当子进程结束时,
wait
会收集该子进程的退出状态,并将其返回给父进程。
#include
pid_t wait(int *status);
- 参数:
status
:指向一个整数的指针,用于存储子进程的退出状态。- 返回值:
- 返回已终止的子进程的 PID。
- 如果没有子进程可等待,返回
-1
,并设置errno
。
补充:
kill -l #罗列linux系统中所有的信号
exit()退出值存储到status
#include
#include
#include
#include
/*
进程的退出与回收
*/
int main()
{
//局部变量
float num1=58.9;
pid_t id;
int state; //存放我回收的那个子进程的退出信息
int i;
//创建一个子进程
id=fork();
if(id>0) //父进程
{
int n=0;
for(int j=0; j<10; j++)
{
printf("父进程在运行,id的值是: %d 局部变量是: %f!
",id,num1);
//偷偷摸摸把num1修改,看看子进程的num1受不受影响
n++;
if(n==5)
{
num1=96.9;
}
sleep(1);
}
}
else if(id==0) //子进程
{
for(int j=0; j<10; j++)
{
printf("子进程在运行!,id的值是: %d 局部变量是: %f
",id,num1);
sleep(1);
}
exit(5); //让你的子进程彻底结束
}
else
{
perror("生子进程失败了!
");
return -1;
}
printf("主函数准备退出了!
");
for(i=0; i<10; i++)
{
printf("good bye!
");
}
//回收子进程
wait(&state);
printf("父进程回收子进程,得到的状态信息是: %d
",state);
//还原退出值2--》我们自己动手写的
int result=state&0x0000ff00;//按位域取第二个八位二进制的值
printf("result is: 十六进制%x
",result);
//开外挂--》使用系统提供的宏定义
//printf("子进程的退出值是: %d
",WEXITSTATUS(state));
return 0;
}
waitpid()
函数
作用&函数原型
waitpid
提供了更精确的控制,可以:- 等待特定的子进程结束。
- 非阻塞式地等待子进程结束(使用
WNOHANG
选项)。 - 等待所有子进程结束(使用
-1
作为pid
)。
函数原型:
#include
pid_t waitpid(pid_t pid, int *status, int options);
返回值:成功 回收到的那个子进程的id号
失败 -1参数:pid --》
< -1
回收进程组id是pid绝对值中的某个进程
比如: waitpid(-10000,) 回收进程组ID是10000的这个组里面的某个进程
== -1
回收任意一个进程
比如: waitpid(-1,);
== 0
回收本进程组中的某个进程
比如: waitpid(0)
> 0
指定回收进程id是pid的这个进程
比如: waitpid(10000,); 回收id是10000的这个进程
options --》WNOHANG
: 非阻塞等待,父进程在退出的时候,如果子进程还没有退出, 那么父进程不会阻塞,也不会去回收子进程,直接退出
WUNTRACED
:也返回已停止的子进程。 WCONTINUED:返回已继续运行的子进程(SIGCONT 信号)。
0
: 阻塞等待
第一个参数的四种情况
获取进程的id以及获取父进程的id
获取当前进程的id号
pid_t getpid(void);
获取父进程的id
pid_t getppid(void);
获取进程组的ID
gid_t getgid(void);
示例代码:
#include "myhead.h"
/*
waitpid指定回收某个进程
*/
int main()
{
pid_t id1,id2;
int status;
pid_t ret;
//创建一个子进程
id1=fork();
if(id1>0) //父进程
{
//接着生
id2=fork();
if(id2>0) //父进程
{
printf("父进程杀毒!,父进程的ID是: %d
",getpid());
}
else if(id2==0) //第二个子进程
{
printf("子进程2扫描漏洞,它的ID是: %d,它老爸的ID:%d
",getpid(),getppid());
exit(2); //退出子进程
}
}
else if(id1==0) //第一个子进程
{
printf("子进程1清除垃圾,它的ID是: %d,它老爸的ID:%d
",getpid(),getppid());
exit(1); //退出子进程
}
printf("id1存放的是第一个子进程的ID: %d
",id1);
printf("id2存放的是第二个子进程的ID: %d
",id2);
//我指定回收进程2
ret=waitpid(id2,&status,0);
printf("我回收的子进程ID是: %d 退出值是:%d
",ret,WEXITSTATUS(status));
return 0;
}