个人主页:chian-ocean
文章专栏-Linux
前言:
进程控制是操作系统对进程的创建、运行、调度、中止等活动进行管理和协调的行为。它是操作系统中至关重要的一部分,保证多任务处理环境下的资源分配和系统稳定性。
进程创建
fork( )
-
fork()
调用后:- 子进程是父进程的副本,拥有相同的代码和数据。
- 子进程的所有资源(如内存、打开的文件描述符)最初与父进程共享,但在写入数据时会触发**写时复制(Copy-on-Write,COW)**机制。
返回值:
- **在父进程中:**返回子进程的 PID(进程 ID)。
- **在子进程中:**返回
0
。 - **如果失败:**返回
-1
并设置错误码(通常是因为系统资源不足)
int main()
{
pid_t id = fork();
if(id > 0){
while(1){
cout << "父进程"<< "pid :"<<getpid() <<endl;
sleep(2);
}
}
else if(id == 0){
while(1)
{
cout << "子进程"<<endl;
sleep(2);
}
}
else
{
perror("fork");
cout << "子进程"<< "pid :"<<getpid() <<endl;
sleep(2);
}
}
else
{
perror("fork");
}
return 0;
}
执行代码
进程退出(终止)
进程终止是操作系统中一个进程完成任务或由于某种原因被终止运行的过程。在进程终止后,操作系统会释放该进程占用的资源,并将其从系统中的进程表中移除
退出码
退出码是进程在退出时返回给操作系统或父进程的一个状态值,表示进程的退出状态。退出码常用于判断进程是否成功完成任务或发生了错误
成功退出:
- 退出码为
0
,表示进程正常结束,没有错误。 - 这也就是为什么默认
return 0
失败退出:
- 非
0
值通常表示进程发生了错误或异常。 - 不同的退出码可以表示不同的错误原因(由程序员定义)。
检查退出码
#终端检查
echo $?
demo
- 在这里面设置退出码为 8 ,以便观察。
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main()
{
int i = 10;
while(i--)
{
cout << i <<" ";
}
cout << endl;
return 8;
}
执行
进程退出的方式
- **正常退出:**主函数执行到最后,正常返回。
- **异常退出:**发生异常(如段错误、非法访问内存、除以零)导致进程被操作系统终止。
正常退出
主函数返回:
- 主函数(
main()
)返回值即为进程的退出码。
调用 exit()
函数:
- 显式终止进程,返回退出码。
- 执行清理操作(如刷新缓冲区、关闭文件等)。
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int func()
{
cout << "调用func"<<endl;
exit(1);
return 1;
}
int main()
{
int funcret = func();
cout << funcret << endl;
return 0;
}
代码功能说明
func
函数- 输出一条消息:
调用func
。 - 调用
exit(1)
直接终止程序,返回退出码1
。 - 后面的
return 1
永远不会被执行,因为exit(1)
会导致程序立即退出。
- 输出一条消息:
main
函数- 调用
func()
。 - 由于
func()
中调用了exit(1)
,程序会立即退出,func
返回值永远不会被赋给funcret
。 main
中的cout << funcret << endl;
和return 0;
永远不会被执行。
- 调用
_exit和exit
**_exit()
不会执行任何用户态的清理工作,直接终止进程,不会:
- 调用
atexit()
注册的清理函数。 - 刷新标准 I/O 流。
- 关闭打开的文件缓冲区。
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
cout << "This is exit"<< endl;
cout << "This will not be flushed."; // 没有换行符,缓冲区未刷新
exit(0); // 直接终止程序,刷新缓冲区
cout << "This is _exit"<< endl;
cout << "This will not be flushed."; // 没有换行符,缓冲区未刷新
_exit(0); // 直接终止程序,不刷新缓冲区
return 0;
}
cout << "This is exit << endl;"
:
- 输出
"This is exit"
并刷新缓冲区。 - 输出立即显示在屏幕上。
cout << "This will not be flushed.";
:
- 输出内容存储在缓冲区中,但未刷新(没有换行符)。
_exit(0);
:
- 程序立即终止,不刷新缓冲区。
"This will not be flushed."
不会被输出,因为缓冲区未刷新。
return
、exit()
和 _exit()
总结对比表
特性/行为 | return | exit() | _exit() |
---|---|---|---|
作用范围 | 退出当前函数,返回控制权给调用者 | 终止整个程序,返回控制权给操作系统 | 立即终止当前进程,不返回控制权 |
适用范围 | 函数正常结束,返回值给调用者 | 程序正常或错误退出,需要完成清理工作 | 子进程退出或紧急情况下立即终止程序 |
I/O 缓冲区刷新 | 自动刷新(如果退出程序) | 刷新所有标准 I/O 缓冲区 | 不刷新标准 I/O 缓冲区 |
atexit() 注册函数 | 不调用 | 调用所有通过 atexit() 注册的清理函数 | 不调用 |
局部变量析构 | 调用局部变量的析构函数(C++ 对象) | 不调用析构函数 | 不调用析构函数 |
性能 | 性能高,适用于正常函数返回 | 较慢,涉及缓冲区刷新和清理操作 | 性能最高,直接调用系统内核 |
适用场景 | 函数正常结束,逻辑返回值给调用者 | 程序终止,需要完成缓冲区刷新和清理工作 | 子进程退出或需要立即终止进程时 |
调用位置 | 仅在当前函数中有效 | 可在程序任意位置调用 | 可在程序任意位置调用 |
异常退出
异常终止的表现
非零退出码:表示异常终止的原因。
#include<iostream>
using namespace std;
int main()
{
int a = 10, b = 0;
int c = a / b; // 除以零导致异常终止
cout << c << endl;
}
执行代码
常见退出码:
退出码 | 原因 |
---|---|
139 | 段错误(SIGSEGV )。 |
136 | 浮点异常(SIGFPE )。 |
1 | 一般错误。 |
6 | 调用 abort() (SIGABRT )。 |
9 | 强制终止(SIGKILL ) |
# 查看错误信号
kill -l
错误码
打印错误码
#include <iostream>
#include <cerrno>
#include <cstring>
using namespace std;
int main()
{
for(int i = 0; i < 135; i++)
{
const char * errmessage = strerror(i);
if(errmessage)
{
cout<< "code error" <<" "<< i <<":"<< errmessage <<endl;
}
}
return 0;
}
执行代码
- 可以观察到默认0编码是
success
- 这里面只显示一部分
- 标准错误码:
- 通常范围是 1 到 134,其中每个错误码都由 POSIX 标准定义,常见于
errno.h
。
- 通常范围是 1 到 134,其中每个错误码都由 POSIX 标准定义,常见于
- 系统特定错误码:
- 某些错误码可能特定于操作系统,错误描述可能不同。