Linux下fork与线程池
前言
这个知识点也是老生常谈了,咱们今天就把他讲清楚弄明白。
线程池
线程池是指在一个多线程程序中创建一个线程集合,在执行新的任务的时候不是新建一个线程,而是使用线程池中已创建好的线程,一旦任务执行完毕,线程就会休眠等待新的任务分配下来。
这么看来,线程池的优点就很明显了,在频繁的线程切换环境中,线程池可以省去多次线程的创建与释放所需要的时间。
Linux下的线程是如何创建的?
这个我还是稍微了解一点的。创建一个线程非常简单。
1.向内核空间申请一段内存用来存放新线程的PCB;
2.PCB中预留指针指向内核栈;
3.PCB中存放待执行线程函数与函数参数;
4.加入进程调度队列或直接ret执行(需要先将函数指针放在栈顶,然后ret会将其加载到eip寄存器上,完成立刻执行线程操作)
fork
简言之,fork就是创建了一个父进程的拷贝。
使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
子进程与父进程的区别在于:
1、父进程设置的锁,子进程不继承(因为如果是排它锁,被继承的话,矛盾了)
2、各自的进程ID和父进程ID不同
记住,fork是一次调用两次返回值,因为会在两个地址空间中都执行返回。
如果成功创建一个子进程,对于父进程来说返回子进程ID
如果成功创建一个子进程,对于子进程来说返回值为0
如果为-1表示创建失败。
fork系统调用复制产生的子进程与父进程(调用进程)基本一样:代码段+数据段+堆栈段+PCB,当前的运行环境基本一样,所以子进程在fork之后开始向下执行,而不会从头开始执行。
孤儿进程与僵尸进程
fork系统调用之后,父子进程将交替执行,执行顺序不定。
如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程(托孤给了init进程)。(注:任何一个进程都必须有父进程)
如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程(僵尸进程:只保留一些退出信息供父进程查询)。僵尸进程可以在进程列表中查看。
Linux如何查看进程状态呢?
ps指令了解一下?process state。多的咱就不说了,百度上多得是介绍。
孤儿进程与僵尸进程的危害
首先任何进程都有父进程(init进程是所有进程的祖宗,它没有爸爸),当父进程抛弃了自己fork出来的子进程而执行完成后,子进程就被托管给init进程。
孤儿进程其实没啥危害。
僵尸进程就不一样了。本来吧,父进程在fork一个子进程的时候,接下来需要wait操作等待子进程结束,然后子进程才会全部释放资源(子进程执行完成后并没有全部释放资源,还剩下PID这种东西,退出状态这种东西,必须要父进程wait索取才会释放)。这下好了,父进程直接没wait就退出了,于是僵尸进城产生后,这个进程ID就再也不能被后续使用了。
如何解决僵尸进程
很简单,第一个是signal(可能有的朋友不了解什么是signal)。Linux的信号机制就提供了其中一种专门用来处理子进程退出问题的,叫SIGCHLD,写法也非常简单:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
static void sig_child(int signo);
int main()
{
pid_t pid;
//创建捕捉子进程退出信号
signal(