C++学习笔记(43)

五、共享文件

fork()的一个特性是在父进程中打开的文件描述符都会被复制到子进程中,父进程和子进程共享同一

个文件偏移量。

如果父进程和子进程写同一描述符指向的文件,但又没有任何形式的同步,那么它们的输出可能会相

互混合。

示例:

#include <iostream>

#include <fstream>

#include <unistd.h>

using namespace std;

int main()

{

ofstream fout;

fout.open("/tmp/tmp.txt"); // 打开文件。

fork();

for (int ii=0;ii<10000000;ii++) // 向文件中写入一千万行数据。

{

fout << "进程" << getpid() << "西施" << ii << "极漂亮" << "\n"; // 写入的

内容无所谓。

}

fout.close(); // 关闭文件。

}

六、vfork()函数

vfork()函数的调用和返回值与 fork()相同,但两者的语义不同。

vfork()函数用于创建一个新进程,而该新进程的目的是 exec 一个新程序,它不复制父进程的地址空

间,因为子进程会立即调用 exec,于是也就不会使用父进程的地址空间。如果子进程使用了父进程的地

址空间,可能会带来未知的结果。

vfork()和 fork()的另一个区别是:vfork()保证子进程先运行,在子进程调用 exec 或 exit()之后父进

程才恢复运行。

316、僵尸进程

如果父进程比子进程先退出,子进程将被 1 号进程托管(这也是一种让程序在后台运行的方法)。

如果子进程比父进程先退出,而父进程没有处理子进程退出的信息,那么,子进程将成为僵尸进程。

僵尸进程有什么危害?内核为每个子进程保留了一个数据结构,包括进程编号、终止状态、使用 CP

U 时间等。父进程如果处理了子进程退出的信息,内核就会释放这个数据结构,父进程如果没有处理子进

程退出的信息,内核就不会释放这个数据结构,子进程的进程编号将一直被占用。系统可用的进程编号是

有限的,如果产生了大量的僵尸进程,将因为没有可用的进程编号而导致系统不能产生新的进程。

僵尸进程的避免:

1)子进程退出的时候,内核会向父进程发头 SIGCHLD 信号,如果父进程用 signal(SIGCHLD,SIG_I

GN)通知内核,表示自己对子进程的退出不感兴趣,那么子进程退出后会立即释放数据结构。

2)父进程通过 wait()/waitpid()等函数等待子进程结束,在子进程退出之前,父进程将被阻塞待。

pid_t wait(int *stat_loc);

pid_t waitpid(pid_t pid, int *stat_loc, int options);

pid_t wait3(int *status, int options, struct rusage *rusage);

pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);

返回值是子进程的编号。

stat_loc 是子进程终止的信息:a)如果是正常终止,宏 WIFEXITED(stat_loc)返回真,宏 WEXITST

ATUS(stat_loc)可获取终止状态;b)如果是异常终止,宏 WTERMSIG(stat_loc)可获取终止进程的信号。

3)如果父进程很忙,可以捕获 SIGCHLD 信号,在信号处理函数中调用 wait()/waitpid()。

示例一:

#include <iostream>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

using namespace std;

int main()

{

if (fork()>0)

{ // 父进程的流程。

int sts;

pid_t pid=wait(&sts);

cout << "已终止的子进程编号是:" << pid << endl;

if (WIFEXITED(sts)) { cout << "子进程是正常退出的,退出状态是:" << WEXITSTATUS(sts)

<< endl; }

else { cout << "子进程是异常退出的,终止它的信号是:" << WTERMSIG(sts) << endl; }

}

else

{ // 子进程的流程。

//sleep(100);

int *p=0; *p=10;

exit(1);

}

}

示例二:

#include <iostream>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

using namespace std;

void func(int sig) // 子进程退出的信号处理函数。

{

int sts;

pid_t pid=wait(&sts);

cout << "已终止的子进程编号是:" << pid << endl;

if (WIFEXITED(sts)) { cout << "子进程是正常退出的,退出状态是:" << WEXITSTATUS(sts) <<

endl; }

else { cout << "子进程是异常退出的,终止它的信号是:" << WTERMSIG(sts) << endl; }

}

int main()

{

signal(SIGCHLD,func); // 捕获子进程退出的信号。

if (fork()>0)

{ // 父进程的流程。

while (true)

{

cout << "父进程忙着执行任务。\n";

sleep(1);

}

}

else

{ // 子进程的流程。

sleep(5);

// int *p=0; *p=10;

exit(1);

}

}

317、多进程与信号

在多进程的服务程序中,如果子进程收到退出信号,子进程自行退出,如果父进程收到退出信号,则

应该先向全部的子进程发送退出信号,然后自己再退出。

示例:

#include <iostream>

#include <unistd.h>

#include <signal.h>

using namespace std;

void FathEXIT(int sig); // 父进程的信号处理函数。

void ChldEXIT(int sig); // 子进程的信号处理函数。

int main()

{

// 忽略全部的信号,不希望被打扰。

for (int ii=1;ii<=64;ii++) signal(ii,SIG_IGN);

// 设置信号,在 shell 状态下可用 "kill 进程号" 或 "Ctrl+c" 正常终止些进程

// 但请不要用 "kill -9 +进程号" 强行终止

signal(SIGTERM,FathEXIT); signal(SIGINT,FathEXIT); // SIGTERM 15 SIGINT 2

while (true)

{

if (fork()>0) // 父进程的流程。

{

sleep(5); continue;

}

else // 子进程的流程。

{

// 子进程需要重新设置信号。

signal(SIGTERM,ChldEXIT); // 子进程的退出函数与父进程不一样。

signal(SIGINT ,SIG_IGN); // 子进程不需要捕获 SIGINT 信号。

while (true)

{

cout << "子进程" << getpid() << "正在运行中。\n"; sleep(3); continue;

}

}

}

}

// 父进程的信号处理函数。

void FathEXIT(int sig)

{

// 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断。

signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);

cout << "父进程退出,sig=" << sig << endl;

kill(0,SIGTERM); // 向全部的子进程发送 15 的信号,通知它们退出。

// 在这里增加释放资源的代码(全局的资源)。

exit(0);

}

// 子进程的信号处理函数。

void ChldEXIT(int sig)

{

// 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断。

signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);

cout << "子进程" << getpid() << "退出,sig=" << sig << endl;

// 在这里增加释放资源的代码(只释放子进程的资源)。

exit(0);

}

相关推荐
青椒大仙KI1112 分钟前
24/11/14 算法笔记<强化学习> 马尔可夫
人工智能·笔记·机器学习
南城夏季12 分钟前
蓝领招聘二期笔记
前端·javascript·笔记
昔舍26 分钟前
C#笔记(3)
笔记·c#
VertexGeek34 分钟前
Rust学习(四):作用域、所有权和生命周期:
java·学习·rust
小小码神Sundayx1 小时前
三、模板与配置(下)
笔记·微信小程序
spy47_1 小时前
JavaEE 重要的API阅读
java·笔记·java-ee·api文档阅读
抱走江江1 小时前
SpringCloud框架学习(第二部分:Consul、LoadBalancer和openFeign)
学习·spring·spring cloud
槿花Hibiscus2 小时前
C++基础:Pimpl设计模式的实现
c++·设计模式
黑不拉几的小白兔2 小时前
PTA部分题目C++重练
开发语言·c++·算法
写bug的小屁孩2 小时前
websocket身份验证
开发语言·网络·c++·qt·websocket·网络协议·qt6.3