首页 > 技术教程 > fork子进程原理

fork子进程原理

I am jm

2023-09-20

pcntl_fork用途:

在当前子进程,当前位置产生分支(子进程)

1、fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程号,而子进程得到的是0。

返回值:成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0。失败时,在 父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。

2、fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,但只有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork失败,父进程会返回错误。

可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了,这也是fork为什么叫fork的原因。

至于哪一个进程最先运行,这与操作系统平台的调度算法有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同运作,可以通过控制语法结构的办法解决。

注意这不是父进程了,虽然是同一个程序,但是这是同一个程序的另外一次执行,在操作系统中这次执行是由另外一个进程表示的,
从执行的角度说和父进程相互独立为什么看上去程序中互斥的两个分支都被执行了,在一个程序的一次执行中,这当然是不可能的,
事实上你看到的两行输出是来自两个独立的进程,而这两个进程来自同一个程序的两次执行。

实例代码:

<?php
$pids = array();
$child_pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($child_pid == -1) {
    //创建子进程失败时返回-1
    throw new Exception(__METHOD__ ."|".__FILE__ .":fork()error");
} else if ($child_pid) {
    //父进程得到的是子进程的进程号,所以这是父进程执行的逻辑 
    pcntl_wait($status);//等待子进程终端,防止子进程成为僵尸进程
} else {
    //子进程得到的pid为0,所以这是子进程执行的逻辑
    for ($i = 0; $i < 3; $i++) {
        $child_pid = pcntl_fork();
        if ($child_pid) {
            //parent
            $pids[] = $child_pid;
            sleep(5);
            print_r($pids);
            echo "\n";
        } else {
            //child
            break;
        }
    }
}
while (true) {
    //你要执行的代码
    sleep(2);
}

修改后:

<?php
/**
* @Desc:fork子进程原理.
* 这段代码在执行pcntl_signal前,先加入了declare(ticks = 1)。
* 因为PHP的函数无法直接注册到操作系统信号设置中,所以pcntl信号需要依赖tick机制。
* pcntl_signal的实现原理是,触发信号后先将信号加入一个队列中。
* 然后在PHP的ticks回调函数中不断检查是否有信号,如果有信号就执行PHP中指定的回调函数,
* 如果没有则跳出函数。
*/
declare(ticks=1);
$ppid = getmypid();
$pids = array();
echo "父进程ID--{$ppid}\n";
try {
    for ($i = 1; $i < 4; $i++) {
        $pid = pcntl_fork();
        pcntl_signal(SIGCHLD, 'sig_func');
        if ($pid == -1) {
            throw new Exception(__LINE__ . ": fork() error");
        } elseif ($pid) {//父进程得到的pid为子进程ID
            //一个信号接收,用于回收进程
            echo "第{$i}个子进程ID--$pid\n";
            $pids[] = $pid;
//            if ($pid != getmypid()) {
//                posix_kill(getmypid(), 9);
//            }
            //等待或返回fork子进程的状态,如果是僵尸进程直接回收
            $rs = pcntl_waitpid($pid, $status, WNOHANG);
            echo "子进程状态--{$rs}\n";
            //1:等待任意子进程;与pcntl_wait函数行为一致。
            //>0:等待进程号等于参数pid值的子进程。
            if ($rs == -1 || $rs > 0) {
                if (!pcntl_wifexited($status)) {
                    echo "非正常退出,进程ID -- $pid";
                } else {
                    $code = pcntl_wexitstatus($status);
                    echo "进程退出码--{$code},进程ID -- $pid";
                }
                if (pcntl_wifsignaled($status)) {
                    echo "进程不是通过接受信号中断,进程ID -- $pid";
                } else {
                    $signal = pcntl_wtermsig($status);
                    echo "进程是通过接受信号 $signal 中断;进程ID -- $pid";
                }
            }
//        sleep(1);
        } else {
//            echo '子进程得到的pid为0,所以这是子进程执行的逻辑'."\n";
            break;
        }
    }
} catch (Exception $e) {
    echo $e->getMessage();
}
function sig_func()
{
    pcntl_wait($status);//父进程必须等待一个子进程退出后,再创建下一个子进程。
}
$n = 0;
while (true) {
    sleep(5);
    $n++;
    echo "第{$n}次执行\n";
} 


文章版权声明
1、本网站名称:阿V编程
2、本站永久网址:https://www.1892zyw.com
3、本网站的部分文章内容/部分资源可能来源于网络,仅提供给大家学习或参考,如有侵权,请联系站长QQ进行删除处理。
4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、本站一律禁止以任何方式发布或转载任何违法的相关信息,如有发现请向站长举报