Linux下PHP多进程实现(1)

什么是一个进程?

进程这个概念是针对系统而不是针对用户的,对用户来说,他面对的概念是程序。当用户敲入命令执行一个程序的时候,对系统而言,它将启动一个进程。但和程序不同的是,在这个进程中,系统可能需要再启动一个或多个进程来完成独立的多个任务。多进程编程的主要内容包括进程控制和进程间通信,在了解这些之前,我们先要简单知道进程的结构。

 

Linux下进程的结构

Linux下一个进程在内存里有三部分的数据:"代码段"、"堆栈段"和"数据段"。"代码段",顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。"堆栈段"存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及动态数据分配的数据空间。

 

Linux下进程控制

在传统的Unix环境下,有两个基本的操作用于创建和修改进程:函数fork( )用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝;函数族exec( )用来启动另外的进程以取代当前运行的进程。

fork()之后exec()之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。

 

fork()函数一次调用却有两个返回值。对于父进程,fork()函数返回了子程序的进程号,而对于子程序,fork()函数则返回零。如果函数调用不成功,则返回-1。

 

在进程运行时,如果子进程先退出,它不会从进程列表里清除。而要发一个SIGCHLD(或SIGCLD)信号给父进程,父进程确认后子进程才会退出。在等待父进程确认期间,子进程处于“zombie”状态。所以我们就需要使用wait()函数。如果调用wait()函数时已经有一个处于“zombie”状态的子进程,那么函数立即返回的同时该子进程从内存中清除出去;否则,主进程会处于阻塞状态,直到子进程退出后才继续执行,而无法进行其他任务。还有一个办法可以解决办法就是拦截处理信号SIGCHLD(或SIGCLD)。子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。

 

在Linux系统中,子进程的正常/异常终止都会给父进程发送SIGCHLD的信号,当父进程接收到子进程(第一个)信号进行wait()或waitpid()时,会屏蔽掉下一个的SIGCHLD信号,实际的效果就是在信号处理函数返回前不会重入。

那么当父进程在执行信号处理函数时,又有子进程(第二个)退出,那么信号会被阻塞并等待处理,假如(第三个)又来了,那么它是被抛弃的,后续的都会抛弃。。。

所以说信号是阻塞但不排队的。

如果子进程可能会同时退出,那么父进程需要这样来处理以防止僵尸进程的出现:

while( (childpid = waitpid(-1, NULL, WNOHANG)) > 0)

{

 ...

}

这样,即使出现子进程同时退出的情况,SIGCHLD的信号被抛弃也没有关系。waitpid会收集所有当前已终止(实际就是处于僵尸状态)的子进程,直到没有这样的进程状态需要收集(返回0)。

 

一个简单的例子如下:

 
Linux下PHP多进程实现(1)_第1张图片
 

 0)
{
    echo "{$pid} exit; \n";
}

 

 执行结果如下:

Linux下PHP多进程实现(1)_第2张图片
 

 

你可能感兴趣的:(php,Linux)