文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

PHP多进程开发面试的常见问题怎么解决

2023-07-04 20:41

关注

本篇内容介绍了“PHP多进程开发面试的常见问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

PHP多进程开发

先介绍一些简单命令

echo $$ //输出当前bash进程strace -s 65500 -p 进程号    //打印进程系统调用kill -s 10 pid //发送信号kill -s SIGUSR2 pid //发送信号pstree -ap //查看进程树ps -ajx //查看进程信息ps 命令字段解析:PPID:父进程IDPID:进程IDPGID:进程组IDSID:会话IDTTY:所在终端STAT:进程状态 R运行 Z僵尸 S睡眠 T停止 D睡眠无法被唤醒UID:unix用户IDCOMMAND:启动命令

什么是程序?

一般指可执行文件,在 Linux 系统中它按 ELF 格式进行存储,没有后缀可言,file 命令可以查看 elf 文件的具体类型

ELF 全程 Executable Linkable Format 可执行可链接格式

ELF 分为四大种类

可通过 objdump/readelf 命令查看 ELF 文件相关信息

什么是终端?

tty 是最令人熟悉的了,在 Linux 中,/dev/ttyX 代表的都是上述的物理终端,其中,/dev/tty1~/dev/tty63 代表的是本地终端,也就是接到本机的键盘显示器可以操作的终端

/dev/console 当前焦点终端

通过 tcp/ip 协议实现的终端,比如用 SSH 进行的登录,或者 telnet, 那么你将得到一个叫做 /dev/pts/X 的伪终端同时在

/proc/bash pid/fd 生成三个标识符指向当前的 /dev/pts/X

0 标准输入 鼠标,键盘

1 标准输出 显示器

2 标准错误 显示器

PHP多进程开发面试的常见问题怎么解决

什么是进程?

进程退出

进程结束时并不会真的退出,还会驻留在内在中,pcntl_wait (pcntl_waitpid) 函数来获取进程的终止状态码同时该函数会释放终止进程的内存空间,如果不这么做,会产生很多僵尸进程占用大量的内存空间

孤儿进程

父进程运行完,子进程在运行,则子进程会被头号进程 init 接管,这类型的进程成为孤儿进程

僵尸进程

子进程运行完,父进程没有调用 pcntl_wait () 回收,进程状态变成 Z+

守护进程

父进程是 init 进程,一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。

什么是进程组?

多个进程组成一个进程组,每个进程组只有一个组长,组长的 PID 就是进程组的 ID;组内所有进程退出时,进程组才会消失,可以通过 ps -ajx 命令查看 pgid

PHP多进程开发面试的常见问题怎么解决

什么是会话?

多个进程组组成一个会话,每个会话都有一个会话首进程。会话的特点

1) 使用 setsid () 函数可以创建一个新的会话

2) 会话首进程无法调用 setsid,会报错

3) 非会话首进程进程可调用 setsid 创建出一个新的会话,这个行为会导致该进程会创建一个新的进程组且自身为该进程组组长,该进程会创建出一个新的会话组且自身为该会话组组长,该进程会脱离当前命令行控制终端

现实上的比喻就是除了老板之后,员工都可以调用 我上我也行 () 这个函数变成老板且不受原公司的控制

什么是信号?

信号是进程间通信的其中一种方式,平时用到的 kill -9 pid, 指的不是用第九种方式杀死进程,而是发送信号值为 9 的信号给进程,而刚好信号 9 是 SIGKILL, 功能是停止进程,查看操作系统支持的信号命令: kill -l

PHP多进程开发面试的常见问题怎么解决

一般使用 1-31, 注意看没有 32,33 这两个信号

信号的产生来源可能是:

当一个进程收到一个信号时,三个可选操作

pcntl_signal () 信号处理器是会被子进程继承的,所以 fork () 之前最后先行处理信号处理器

posix 命令

//需要安装posix扩展posix_getpid();    //获取进程IDposix_getppid();//获取父进程IDposix_getpgid(posix_getppid());//获取进程组IDposix_getpgrp());//同上posix_getsid(posix_getpid()));//获取会话IDposix_getuid();//获取当前登录用户UIDposix_getgid();//获取当前登录用户组UIDposix_geteuid();//获取当前有效用户UIDposix_getguid();//获取当前有效用户组UIDposix_kill();//发送信号

pcntl 命令

//创建一个计时器,在指定的秒数后向进程发送一个SIGALRM信号。每次对 pcntl_alarm()的调用都会取消之前设置的alarm信号。如果seconds设置为0,将不会创建alarm信号。 pcntl_alarm(int $seconds);//在当前进程当前位置产生子进程,子进程会复制父进程的代码段和数据段(Copy on write 写时复制,当子进程要修改内存空间时,操作系统会分配新的内存给子进程),ELF文件的结构,如果父进程先退出,子进程变成孤儿进程,被pid=1进程接管pcntl_fork();//安装一个信号处理器pcntl_signal(int $signo, callback $handler);//调用等待信号的处理器,触发全部未执行的信号回调pcntl_signal_dispatch()//设置或检索阻塞信号pcntl_sigprocmask(int $how, array $set[, array &$oldset])//等待或返回fork的子进程状态,wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。pcntl_wait($status)//加个WNOHANG参数,不挂起父进程,如果没有子进程退出返回0,如果有子进程退出返回子进程pid,如果返回-1表示父进程已经没有子进程pcntl_wait($status, WNOHANG)//基本同pcntl_wait,waitpid可以指定子进程idpcntl_waitpid ($pid ,$status)pcntl_waitpid ($pid ,$status, WNOHANG)//检查状态代码是否代表一个正常的退出。参数 status 是提供给成功调用 pcntl_waitpid() 时的状态参数。pcntl_wifexited($status)//返回一个中断的子进程的返回代码  当php exit(10)时,这个函数返回10,这个函数仅在函数pcntl_wifexited()返回 TRUE.时有效pcntl_wexitstatus($status)//检查子进程状态码是否代表由于某个信号而中断。参数 status 是提供给成功调用 pcntl_waitpid() 时的状态参数。pcntl_wifsignaled($status)//返回导致子进程中断的信号pcntl_wtermsig($status)//检查子进程当前是否已经停止,此函数只有作用于pcntl_wait使用了WUNTRACED作为 option的时候pcntl_wifstopped($status)//返回导致子进程停止的信号pcntl_wstopsig($status)//检索由最后一个失败的pcntl函数设置的错误数pcntl_errno() pcntl_get_last_error()//检索与给定errno关联的系统错误消息pcntl_strerror(pcntl_errno())

pcntl_fork () 执行之前先与 Redis 建立一个连接,然后再开 3 个子进程之后多少个 Redis 连接?

<?php$o_redis = new Redis();$o_redis->connect( '127.0.0.1', 6379 );// 使用for循环搞出3个子进程来for ( $i = 1; $i <= 3; $i++ ) {  $i_pid = pcntl_fork();  if ( 0 == $i_pid ) {    // 使用while保证三个子进程不会退出...    while( true ) {      sleep( 1 );    }  }}// 使用while保证主进程不会退出...while( true ) {   sleep( 1 );}netstat -ant |grep 6379

PHP多进程开发面试的常见问题怎么解决

说明父进程和三个子进程一共四个进程,实际上共享了一个 Redis 长连接

上面这种写法会有什么问题?

因为 Redis 是一个单进程单线程的服务器,所以接收到的命令都是顺序执行顺序返回的,所以当客户端多个进程共享一个 redis 连接时,当有四个进程向 Redis 服务端发起请求,返回四个结果,谁先抢到就是谁的,正确的做法是每个子进程创建一个 Redis 连接,或者用连接池

孤儿进程怎么产生?

$i_pid = pcntl_fork();if (0 == $i_pid) {    // 子进程10秒钟后退出.    for ($i = 1; $i <= 10; $i++) {        sleep(1);        echo "我的父进程是:" . posix_getppid() . PHP_EOL;    }} else if ($i_pid > 0) {    // 父进程休眠2s后退出.    sleep(2);}

僵尸进程怎么产生?

$i_pid = pcntl_fork();if (0 == $i_pid) {    // 子进程10s后退出,变成僵尸进程    sleep(10);} else if ($i_pid > 0) {    // 父进程休眠1000s后退出.    sleep(1000);}

子进程怎么回收?

$i_pid = pcntl_fork();if (0 == $i_pid) {    // 在子进程中    for ($i = 1; $i <= 10; $i++) {        sleep(1);        echo "子进程PID " . posix_getpid() . "倒计时 : " . $i . PHP_EOL;    }} else if ($i_pid > 0) {    $i_ret = pcntl_wait($status);    echo $i_ret . ' : ' . $status . PHP_EOL;    // while保持父进程不退出    while (true) {        sleep(1);    }}

子进程怎么回收?非阻塞版本

<?php// fork出十个子进程for ($i = 1; $i <= 10; $i++) {    $i_pid = pcntl_fork();    // 每个子进程随机运行1-5秒钟    if (0 == $i_pid) {        $i_rand_time = mt_rand(1, 5);        sleep($i_rand_time);        exit;    } // 父进程收集所有子进程PID    else if ($i_pid > 0) {    }}while (true) {    // sleep使父进程不会因while导致CPU爆炸.    sleep(1);    //设置WNOHANG参数不会阻塞,就是需要外层包个循环    $pid = pcntl_wait($status, WNOHANG);    if ($pid == 0) {   //目前还没有结束的子进程        continue;    }    if ($pid == -1) { //已经结束啦 很蓝的啦        exit("所有进程均已终止" . PHP_EOL);    }    // 如果子进程是正常结束    if (pcntl_wifexited($status)) {        // 获取子进程结束时候的 返回错误码        $i_code = pcntl_wexitstatus($status);        echo $pid . "正常结束,最终返回:" . $i_code . PHP_EOL;    }    // 如果子进程是被信号终止    if (pcntl_wifsignaled($status)) {        // 获取是哪个信号终止的该进程        $i_signal = pcntl_wtermsig($status);        echo $pid . "由信号结束,信号为:" . $i_signal . PHP_EOL;    }    // 如果子进程是[临时挂起]    if (pcntl_wifstopped($status)) {        // 获取是哪个信号让他挂起        $i_signal = pcntl_wstopsig($status);        echo $pid . "被挂起,挂起信号为:" . $i_signal . PHP_EOL;    }}

如何创建守护进程?

$pid = pcntl_fork();if ($pid > 0) { //1)在父进程中执行fork并exit推出    exit();} elseif ($pid == 0) {    if (posix_setsid() < 0) {   //2)在子进程中调用setsid函数创建新的会话        exit();    }    chdir('/'); //3)在子进程中调用chdir函数,让根目录 ” / ” 成为子进程的工作目录    umask(0);   //4)在子进程中调用umask函数,设置进程的umask为0    echo "create success, pid = " . posix_getpid();    //5)在子进程中关闭任何不需要的文件描述符    fclose(STDIN);    fclose(STDOUT);    fclose(STDERR);}//可以把上面封装成函数daemon();while (true) {} //具体业务如何修改进程名?for ($i = 1; $i <= 4; $i++) {    $i_pid = pcntl_fork();    if (0 == $i_pid) { //子进程        cli_set_process_title("Worker Process"); //修改子进程的名字        while (true) {            sleep(1);        }    }}cli_set_process_title("Master Process");    //修改父进程的名字while (true) {    sleep(1);}

PHP多进程开发面试的常见问题怎么解决

进程怎么接收信号?

// 信号处理回调function signal_handler($signal){    switch ($signal) {        case SIGTERM:            echo "sigterm信号." . PHP_EOL;            break;        case SIGUSR2:            echo "sigusr2信号." . PHP_EOL;            break;        case SIGUSR1:            echo "sigusr1信号." . PHP_EOL;            break;        default:            echo "其他信号." . PHP_EOL;    }}// 给进程安装3个信号处理回调pcntl_signal(SIGTERM, "signal_handler");pcntl_signal(SIGUSR1, "signal_handler");pcntl_signal(SIGUSR2, "signal_handler");while (true) {    posix_kill(posix_getpid(), SIGUSR1);//发送一个信号给当前进程    posix_kill(posix_getpid(), SIGUSR1);    pcntl_signal_dispatch(); //调一次分发一次信号,调用之前,信号累积在队列里    posix_kill(posix_getpid(), SIGUSR2);    posix_kill(posix_getpid(), SIGUSR2);    sleep(1);   //稍微休息一下}

PHP多进程开发面试的常见问题怎么解决

其中第 1,2 行与第 3,4,5,6 行中间隔了一秒,体会一下 pcntl_signal_dispatch 这个函数

进程怎么接收信号 (不阻塞版本)?

//php7.1及以上才能用这个函数pcntl_async_signals(true);// 信号处理回调function signal_handler($signal){    switch ($signal) {        case SIGTERM:            echo "sigterm信号." . PHP_EOL;            break;        case SIGUSR2:            echo "sigusr2信号." . PHP_EOL;            break;        case SIGUSR1:            echo "sigusr1信号." . PHP_EOL;            break;        default:            echo "其他信号." . PHP_EOL;    }}// 给进程安装信号...pcntl_signal(SIGTERM, "signal_handler");pcntl_signal(SIGUSR1, "signal_handler");pcntl_signal(SIGUSR2, "signal_handler");while (true) {    posix_kill(posix_getpid(), SIGUSR1);//发送一个信号给当前进程    posix_kill(posix_getpid(), SIGUSR2);    sleep(1);   //稍微休息一下}

进程怎么阻塞信号

pcntl_async_signals(true);// 信号处理回调function signal_handler($signal){    switch ($signal) {        case SIGTERM:            echo "sigterm信号." . PHP_EOL;            break;        case SIGUSR2:            echo "sigusr2信号." . PHP_EOL;            break;        case SIGUSR1:            echo "sigusr1信号." . PHP_EOL;            break;        default:            echo "其他信号." . PHP_EOL;    }}// 给进程安装信号...pcntl_signal(SIGTERM, "signal_handler");pcntl_signal(SIGUSR1, "signal_handler");pcntl_signal(SIGUSR2, "signal_handler");//把SIGUSR1阻塞,收到这个信号先不处理pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $a_oldset);$counter = 0;while (true) {    posix_kill(posix_getpid(), SIGUSR1);//发送一个信号给当前进程    posix_kill(posix_getpid(), SIGUSR2);    sleep(1);   //稍微休息一下    if ($counter++ == 5) {        //解除SIGUSR1信号阻塞,并立刻执行SIGUSR1处理回调函数        pcntl_sigprocmask(SIG_UNBLOCK, [SIGUSR1], $a_oldset);    }}

“PHP多进程开发面试的常见问题怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯