简体   繁体   English

在 Apache/PHP 中,如何运行将独立并在 linux 上重新启动的 apache

[英]In Apache/PHP how to run a process that will be independent and survive apache restart on linux

I am trying to start a php (under apache) process (by calling the apache from a browser), that will survive shutting down the apache server (sudo service apache2 stop). I am trying to start a php (under apache) process (by calling the apache from a browser), that will survive shutting down the apache server (sudo service apache2 stop). Even when I make sure the created process has no parent (parent 1), and has it's own session, still, somehow, the process is died when I stop the apache (or restart the apache)即使我确保创建的进程没有父进程(父进程 1),并且有自己的 session,但当我停止 apache(或重新启动 apache)时,进程仍然以某种方式死亡

I created a test.php file:我创建了一个 test.php 文件:

<?php
exec('setsid nohup sleep 1000 > /dev/null 2>/dev/null &');
?>

When running doing HTTP GET to this test.php, indeed we get an immediate OK response, and the process still lives.当运行 HTTP GET to this test.php 时,我们确实得到了立即的 OK 响应,并且该过程仍然存在。 But, when we do:但是,当我们这样做时:

sudo service apache2 stop

The sleep process dies.睡眠进程终止。 How can someone kill a process when the process doesn't belong to its group or session, and when the process is not a child?当进程不属于其组或 session 并且进程不是子进程时,如何杀死进程?

Apache will probably have a list of the spawned processes and kills them individually, and not as a group. Apache可能会列出所有生成的进程,并分别(而不是作为一个组)杀死它们。 In that case, all processes in the list will be kill(2) ed. 在这种情况下,列表中的所有进程都将被kill(2) ed。 But see below next paragraph for a possibility. 但可能的情况请参见下一段。

Look at the man pages for the kill(2) system call. 查看手册页中的kill(2)系统调用。 In the ERRORS section, the only possibilities to fail are: 在“错误”部分,失败的唯一可能性是:

  • EINVAL , meaning an invalid signal number has been passed. EINVAL ,表示已传递了无效的信号号。 Doesn't apply here. 在这里不适用。

  • ESRCH , the process (or process group) doesn't exist. ESRCH ,流程(或流程组)不存在。 Doesn't apply also. 也不适用。

  • EPERM , you don't have permission to send a signal. EPERM ,您无权发送信号。 This applies here, but the only processes (and this has nothing to do with process hierarchies or parental relationships) you are allowed to send signals to are the processes that run with real/saved user id equals the effective user id of the sender process. 这适用于此处,但是允许发送信号的唯一进程(与进程层次结构或父母关系无关)是使用实际/已保存用户ID运行的进程等于发送方进程的有效用户ID。 So, as Apache has a registry of all the processes it launches, it is normal that it is able to kill the process. 因此,由于Apache拥有其启动的所有进程的注册表,因此能够杀死该进程是正常的。

Anyway, have you tried to create a process, from that process create a subprocess, and execute the setsid in the grandchild subprocess? 无论如何,您是否尝试过创建一个流程,从该流程创建一个子流程,并在孙子流程中执行setsid That way, there's no chance for the Apache process to have it registered in the list of spawned processes. 这样,Apache进程就没有机会在生成的进程列表中注册它。 I have not tried that, but it could work. 我没有尝试过,但是可以。

From the FreeBSD kill(2) manual page: 从FreeBSD kill(2)手册页:

For a process to have permission to send a signal to a process designated by pid, the user must be the super-user, or the real or saved user ID of the receiving process must match the real or effective user ID of the sending process. 为了使进程有权向pid指定的进程发送信号,用户必须是超级用户,否则接收进程的真实或已保存用户ID必须与发送进程的真实或有效用户ID匹配。 A single exception is the signal SIGCONT , which may always be sent to any process with the same session ID as the sender. 信号SIGCONT是一个例外,它始终可以发送到与发送方具有相同会话ID的任何进程。

(emphasis is mine) in linux, it's almost the same, except (重点是我的)在Linux中,几乎一样,除了

... (if the sending process) have the CAP_KILL capability in the user namespace of the target process... ...(如果正在发送进程)在目标进程的用户名称空间中具有CAP_KILL功能...

but this doesn't apply here. 但这不适用于这里。

List all active processes (eg ps aux ).列出所有活动进程(例如ps aux )。 The processes, which were created by Apache run as user www-data .由 Apache 创建的进程以用户www-data身份运行。

When you stop Apache service, I suspect they will be terminated based on that or some internal list.当您停止 Apache 服务时,我怀疑它们将基于该或某些内部列表被终止。 apache2.service stop calls apachectl -k graceful-stop . apache2.service stop调用apachectl -k graceful-stop This will SIGTERM all "child processes".这将 SIGTERM 所有“子进程”。 Unfortunately, I was unable to find the exact place in the code.不幸的是,我无法在代码中找到确切的位置。 Maybe someone could do that to verify the hypothesis.也许有人可以这样做来验证这个假设。

A solution:一个解法:

Run the process as another user.以另一个用户身份运行该进程。 How you go about that depends on your case.你如何 go 取决于你的情况。 You will have to define some kind of interface.您将必须定义某种接口。
For example, you could have another process listen on localhost or use a Unix domain socket.例如,您可以让另一个进程在localhost上侦听或使用 Unix 域套接字。 The same will be achieved by using gearman , as @Akshay Vanjare pointed out in the comments.正如@Akshay Vanjare 在评论中指出的那样,使用gearman也可以达到同样的效果。
Your PHP script can then call to the interface.然后您的 PHP 脚本可以调用该接口。

No solutions:没有解决方案:
  • It is a bad idea to control a daemon this way, see this answer.以这种方式控制守护程序是一个坏主意,请参阅答案。
  • As user www-data you cannot start a process as another password protected user, because su asks for pass and switching UID is not a good idea.作为用户www-data您不能以另一个受密码保护的用户身份启动进程,因为su 要求通过并且切换 UID不是一个好主意。
  • For testing, I set up a user with empty password and used su - NEW_USER -c "COMMAND" in PHP script.为了测试,我设置了一个密码为空的用户,并在 PHP 脚本中使用了su - NEW_USER -c "COMMAND" Do not do this in any system you care about.不要在您关心的任何系统中执行此操作。 It is insecure.这是不安全的。 Very insecure.非常不安全。 And you will have a new process every time the script is called.每次调用脚本时,您都会有一个新进程。 You would have to take care to kill it.你必须小心杀死它。
Further thoughts:进一步的想法:
  • I also tried a few alternatives to "nohup" the command, such as daemonizing , fork(), disown etc. They did not work for me.我还尝试了一些“nohup”命令的替代方法,例如daemonizing 、 fork() 、 disown 等。它们对我不起作用。

  • I did not try hard in the new process to ignore SIGTERM.我没有在新流程中努力忽略 SIGTERM。 maybe it is possible to solve the question that way, too.也许也可以这样解决这个问题。

  • To me, it makes sense for Apache to do (aggressive) clean up when it is stopped.对我来说,Apache 在停止时进行(积极的)清理是有意义的。 It is behavior I would expect from a web browser, which has to handle a lot of children.这是我对 web 浏览器的期望,它必须处理很多孩子。

First about my answer, it's not a good idea to control dameon from the web.首先关于我的回答,从 web 控制 dameon 不是一个好主意。

Once this said, you can have a php script that write a flag or somthing like that either on a database, a file, redis, etc.话虽如此,您可以使用 php 脚本在数据库、文件、redis 等上写入标志或类似内容。

On the other side make a PHP script that you schedule with cron for looking for the flag.另一方面,制作一个 PHP 脚本,您可以使用 cron 安排该脚本来查找标志。 If found, the script can start a PHP daemon that will detach.如果找到,该脚本可以启动一个 PHP 守护程序,该守护程序将分离。 Pay attention to running the cron script and the PHP daemon with secured user & rights.注意以安全的用户和权限运行 cron 脚本和 PHP 守护程序。

But once again be carefull with security concern.但再次小心安全问题。

I'm curious on why the apache server needs to be shut down, but im inclined to say you might try a different strategy altogether using a containerized solution.我很好奇为什么需要关闭 apache 服务器,但我倾向于说您可以使用容器化解决方案完全尝试不同的策略。 Exposing an nginx docker container that can trigger your php process would likely be more stable than using Apache, as the docker daemon always runs as root. Exposing an nginx docker container that can trigger your php process would likely be more stable than using Apache, as the docker daemon always runs as root. I think it depends on the specific needs of your use case though, so explaining why you need to shut down Apache might get you better answers.我认为这取决于您的用例的具体需求,因此解释为什么您需要关闭 Apache 可能会为您提供更好的答案。

From the PHP developer perspective I don't think there is a way to achieve that.从 PHP 开发人员的角度来看,我认为没有办法实现这一点。

Or you can implement a queue and worker to do your tasks/executions.或者您可以实现一个队列和工作人员来执行您的任务/执行。 and for that you can either use Redis or any other database.为此,您可以使用 Redis 或任何其他数据库。

https://laravel.com/docs/9.x/queues https://laravel.com/docs/9.x/queues

You can write a service that then you can control with sudo service start|stop|restart您可以编写一个服务,然后您可以使用sudo service start|stop|restart进行控制

You can use visudo to edit sudoers file to allow www-data to use sudo to run a specific sh file, without being prompted by password.您可以使用visudo编辑 sudoers 文件以允许www-data使用sudo运行特定的sh文件,而无需密码提示。 This sh file will contain the line service your_service start .此 sh 文件将包含行service your_service start

Example: I wanted to be able to restart apache2 from a client side ajax request.示例:我希望能够从客户端 ajax 请求重新启动 apache2。

Context: file server with no screen/keyboard attached.上下文:没有附加屏幕/键盘的文件服务器。 I have SSH setup, but I don't want to launch a client in my laptop.我有SSH设置,但我不想在我的笔记本电脑中启动客户端。 I protect the restart page with apache password protected directory , along with other administrative pages.我使用apache 密码保护目录以及其他管理页面来保护重启页面。 For example: I have a page to restart/shutdown the whole system.例如:我有一个页面来重新启动/关闭整个系统。

I did this:我这样做了:

Register a service to run with sudo service apache_restarter_service start使用sudo service apache_restarter_service start注册要运行的服务

This service file calls a simple sh script, a two lines script: sleep 1 second and then call service apache2 restart .该服务文件调用一个简单的sh脚本,一个两行脚本: sleep 1 second and then call service apache2 restart

Use visudo to edit sudoers file.使用visudo编辑 sudoers 文件。 Allow user www-data to use sudo to run a sh script that calls service apache_restarter_service start .允许用户www-data使用sudo运行调用service apache_restarter_service start的 sh 脚本。

The service file is used only to start a process with root privileges, and it doesn't need to be long lived, so no need for Restart=always in service definition.服务文件只用于启动具有root权限的进程,不需要长期存在,所以在服务定义中不需要Restart=always This process cannot be killed by apache and apache won't even know about it.这个进程不能被 apache 杀死,apache 甚至都不知道它。 It will only know that the sh script returned 0.它只会知道sh脚本返回 0。

Example of changes to sudoers file: sudoers 文件更改示例:

www-data ALL=(ALL) NOPASSWD:/opt/apache_restarter/from_php.sh

from_php.sh only calls service apache_restarter_service start . from_php.sh只调用service apache_restarter_service start No need for sudo here, because your php script already used sudo when calling from_php.sh.这里不需要sudo ,因为您的 php 脚本在调用 from_php.sh 时已经使用了 sudo。

from_php.sh is needed here, because you don't want php to call service directly.这里需要from_php.sh ,因为你不希望 php 直接调用service Because a BUG in, or an ATTACK against, your script may do harm to your server.因为一个BUG,或者一个攻击,你的脚本可能会对你的服务器造成伤害。 This way, we only authorized from_php.sh in sudoers.这样,我们只授权了sudoers中的from_php.sh

The php script must call from_php.sh with sudo . php 脚本必须使用sudo调用from_php.sh You can use exec("sudo /opt/apache_restarter/from_php.sh") .您可以使用exec("sudo /opt/apache_restarter/from_php.sh") An & at the end is not needed, because apache won't be restarted immediately.不需要末尾的& ,因为 apache 不会立即重新启动。

Example of service definition file apache_restarter_service.service :服务定义文件apache_restarter_service.service

[Unit]
Description=Apache restarter one shot
After=multi-user.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=no
User=root
ExecStart=/opt/apache_restarter/run.sh

[Install]
WantedBy=multi-user.target

To avoid the service to run once the system is restarter.避免服务在系统重启后运行。 No harm really, but also useless.真的没有坏处,但也没用。 You can have the service always disabled and enable it in from.php script before starting it.您可以在启动之前始终禁用该服务并在from.php脚本中启用它。 Or just tune the service file to not run one time at system start.或者只是将服务文件调整为在系统启动时不运行一次。

You can tune StartLimitIntervalSec=0 to your needs.您可以根据需要调整StartLimitIntervalSec=0

You can tune User=root to a safer, less privileged user.您可以将User=root调整为更安全、特权更少的用户。 In my case I don't care but maybe you do.就我而言,我不在乎,但也许你在乎。 You may want to use www-data .您可能想使用www-data I don't know why that may not work, but I didn't tried to use www-data so far.我不知道为什么这可能行不通,但到目前为止我还没有尝试使用www-data

You can write run.sh in many ways and to do different tasks.您可以通过多种方式编写run.sh并执行不同的任务。 The simpler of which would be to simply wait 1 second and then restart apache with service apache2 restart .更简单的方法是简单地等待 1 秒,然后使用service apache2 restart启动 apache 。

While my interest is to be able to restart apache from client side, this also applies to spawn a process that won't be terminated when apache is stopped/restarted.虽然我的兴趣是能够从客户端重新启动 apache,但这也适用于生成一个不会在 apache 停止/重新启动时终止的进程。

I also tried with nohup , & , setsid and whatnot.我也试过nohup&setsid等等。 I will save people some time.我会为人们节省一些时间。 Nothing works.没有任何效果。 Apache should be terminating all child processes as already suggested. Apache 应该像已经建议的那样终止所有子进程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM