php process forking and get the child process id - php

Objective:
My script will download a remote file upon form submission, since the file might be big, I would like to fork off a process and let the user go on with his/her life.
Example of a command:
wget -q --limit-rate=1k --tries=10 "http://helios.gsfc.nasa.gov/image_euv_press.jpg" -O /web/assets/content/image/image_euv_press.jpg
Method tried:
pcntl forking,
$pid = pcntl_fork();
if ( $pid == -1 ) {
exit;
} else if ( $pid ) {
//We are the parent process, the pid is child pid right?
return $pid;
} else {
// We are the child process
exec($command.' > /dev/null');
// > /dev/null &
posix_kill(getmypid(),9);
return;
}
I do get the PID but then there is a risk that the forked process becomes a zombie and since I am using nginx -> php-fpm (tested and confirmed, upon running several times alot of defunct php-fpm processes), I would have to restart the server just to eliminate the zombies, this would leave me to PID exhaustion attack? ( I am guessing)
Background process:
exec($command . ' > /dev/null &');//background process
$proc = passthru ("ps aux | grep '$command'");//search for the command
echo var_dump($proc);
$pid = (int)(next(explode(' ',$proc)));//parse the pid (not implemented)
Question:
the background process method works but it's not clean, is there a better way to fork off a process to download and get that wget command PID so I can kill it later?.
I have tried echoing $! after doing the exec just to get the PID but exec('echo $!') doesnt return anything, i think it's because every exec is a different "space"
I added '> /dev/null 2>/dev/null &' to the end of the command on my terminal it would return something like: [3] 30751, but through php exec, there is no way to capture that returned PID.
Thank you.

While not a direct answer, the following link might help you get it done:
The Mysteries Of Asynchronous Processing With PHP - Part 3
Practical PHP: Process control
As an alternative to PHP's native pctl functions, consider using Gearman:
Gearman provides a generic application framework to farm out work to other machines or processes that are better suited to do the work. It allows you to do work in parallel, to load balance processing, and to call functions between languages. It can be used in a variety of applications, from high-availability web sites to the transport of database replication events.

Try the following command:
exec("ps -C $command -o pid=", $pids);
But I recommend you to use Zend Server Job Queue, which exists for these objectives.

Try adding the "echo $!" to the same execution flow as the launched background process.
I.e. something like this:
shell_exec("$command & echo $!");

Related

Execute 2 php-cli scripts and allow them to be stopped/started by a third script

I currently have 2 php cli scripts that run in the background on my server. I would like the ability to allow a 3rd script to kill 1 (or both) scripts and restart them again. My current method involves creating a shell script with:
#!/bin/bash
nohup php script1.php &
nohup php script2.php &
And when I need to restart them, I run ps and kill them manually and re-run the shell script.
Is there a way that I can start/restart these scripts from a third php script when needed? I would imagine it would involve recording the pid's to a file and having the 3rd script read the pid's and kill them from there, but I'm not sure how to go about it.
Figured out a solution. I created a couple of functions in my 3rd script to "start" and "restart" the other two php scripts like this:
function launchscripts() {
global $pid1, $pid2;
$cmd1 = "/usr/bin/php /home/of/the/script1.php";
$pid1 = exec("nohup $cmd1 > /dev/null 2>&1 & echo $!");
$cmd2 = "/usr/bin/php /home/of/the/script2.php";
$pid2 = exec("nohup $cmd2 > /dev/null 2>&1 & echo $!");
return true;
}
function restartscripts() {
global $pid1, $pid2;
exec("kill $pid1");
exec("kill $pid2");
launchscripts();
return true;
}
There are probably much better ways of doing this, but I'm not very good with PHP. This seems to work, so I'll stick with it.
One thing I didn't mention earlier that I'm thinking was probably important, was that my 3rd script would be constantly running when the other two are, that's why global variables will work for this.

Multi threading in PHP

In a apcahe server i want to run a PHP scripts as cron which starts a php file in background and exits just after starting of the file and doesn't wait for the script to complete as that script will take around 60 minutes to complete.how this can be done?
You should know that there is no threads in PHP.
But you can execute programs and detach them easily if you're running on Unix/linux system.
$command = "/usr/bin/php '/path/to/your/php/to/execute.php'";
exec("{$command} > /dev/null 2>&1 & echo -n \$!");
May do the job. Let's explain a bit :
exec($command);
Executes /usr/bin/php '/path/to/your/php/to/execute.php' : your script is launched but Apache will awaits the end of the execution before executing next code.
> /dev/null
will redirect standard output (ie. your echo, print etc) to a virtual file (all outputs written in it are lost).
2>&1
will redirect error output to standard output, writting in the same virtual and non-existing file. This avoids having logs into your apache2/error.log for example.
&
is the most important thing in your case : it will detach your execution of $command : so exec() will immediatly release your php code execution.
echo -n \$!
will give PID of your detached execution as response : it will be returned by exec() and makes you able to work with it (such as, put this pid into a database and kill it after some time to avoid zombies).
You need to use "&" symbol to run program as background proccess.
$ php -f file.php &
Thats will run this command in background.
You may wright sh script
#!/bin/bash
php -f file.php &
And run this script from crontab.
This may not be the best solution to your specific problem. But for the record, there is Threads in PHP.
https://github.com/krakjoe/pthreads
I'm assuming you know how to use threads, this is very young code that I wrote myself, but if you have experience with threads and mutex and the like you should be able to solve your problem using this extension.
This is clearly a shameless plug of my own project, and if the user doesn't have the access required to install extensions then it won't help him, but many people find stackoverflow and it will solve other problems no doubt ...

How to execute separate process in PHP

I want to run service in PHP which to be run in the background. I have tried by using exec() function in PHP but service is being run in infinite loop and control is not returning back over PHP file. I have searched more over the internet but I can't find the solution. Please give me some idea or reference to achieve this task.
This is code for reference what I want to do:-
echo"hello";
exec("raintree.frm");
echo"hello1";
raintree.frm is a service which I want to execute. Here PHP script prints "hello" over browser but that is not coming on "hello1" because control gets stuck on exec() function.
If you'd like to have your service running in a separate process, as the title states, you need to create the new process and then run the service in it. In PHP you can create a new process with pcntl_fork() and start the service in the child process. Something like this
echo "hello";
$pid = pcntl_fork();
switch($pid){
case -1: // pcntl_fork() failed
die('could not fork');
case 0: // you're in the new (child) process
exec("raintree.frm");
// controll goes further down ONLY if exec() fails
echo 'exec() failed';
default: // you're in the main (parent) process in which the script is running
echo "hello1";
}
For more clarification read the manual (the link above to pcntl_fork()) as well as look at some C/Unix tutorials on the topics (or rather syscalls) fork() and exec().
You can make use of atd for running arbitrary commands in a separate process:
shell_exec('echo code you want to run | at -m now');
For this, atd should be installed and running, of course.
The exec() function is waiting to receive the output of the externally executed command.
To prevent this from happening, you can redirect the output elsewhere:
<?php
echo 'hello1';
exec('raintree.frm > /dev/null &');
echo 'hello2';
This example will output "hello1hello2" without waiting for raintree.frm > /dev/null & to finish.
(this probably only works with Unix-like operating systems)

How to terminate a process

I am creating a process using proc_open in one PHP script.
How do i terminate this in another script . I am not able to pass the resource returned by the proc_open.
I also tried using proc_get_status() , it returns the ppid . I don't get the pid of the children .
development env : WAMP
Any inputs is appreciated .
I recommend that you re-examine your model to make certain that you actually have to kill the process from somewhere else. Your code will get increasingly difficult to debug and maintain in all but the most trivial circumstances.
To keep it encapsulated, you can signal the process you wish to terminate and gracefully exit in the process you want to kill. Otherwise, you can use normal IPC to send a message that says: "hey, buddy. shut down, please."
edit: for the 2nd paragraph, you may still end up launching a script to do this. that's fine. what you want to avoid is a kill -9 type of thing. instead, let the process exit gracefully.
To do that in pure PHP, here is the solution:
posix_kill($pid, 15); // SIGTERM = 15
You can use some methond to create process, this method usually returns the PID of the new process.
Does this works for You? :
$process = proc_open('php', $descriptorspec, $pipes, $cwd, $env);
$return_value = proc_close($process);
You're best off using something like this to launch your other process:
$pid = shell_exec("nohup $Command > /dev/null 2>&1 & echo $!");
That there would execute the process, and give you a running process ID.
exec("ps $pid", $pState);
$running = (count($pState) >= 2);
to terminate you can always use
exec("kill $pid");
However, you cant kill processes not owned by the user PHP runs at - if it runs as nobody - you'll start the new process as nobody, and only be able to kill processes running under the user nobody.

Run a ffmpeg process in the background

I am wanting to use ffmpeg to convert video to .flv in php. Currently I have this working, but it hangs the browser until the file is uploaded and is finished. I have been looking at the php docs on how to run an exec() process in the background, while updating the process using the returned PID. Here is what I found:
//Run linux command in background and return the PID created by the OS
function run_in_background($Command, $Priority = 0)
{
if($Priority)
$PID = shell_exec("nohup nice -n $Priority $Command > /dev/null & echo $!");
else
$PID = shell_exec("nohup $Command > /dev/null & echo $!");
return($PID);
}
There is also a trick which I use to track if the background task is running using the returned PID :
//Verifies if a process is running in linux
function is_process_running($PID)
{
exec("ps $PID", $ProcessState);
return(count($ProcessState) >= 2);
}
Am I suppose to create a separate .php file which then runs from the php cli to execute one of these functions? I just need a little nudge in getting this working and then I can take it from there.
Thanks!
Am I suppose to create a separate .php
file which then runs from the php cli
to execute one of these functions?
This is probably the way I would do it :
the PHP webpage adds a record in database to indicate "this file has to be processed"
and displays a message to the user ; something like "your file will be processed soon"
In CLI, have a batch process the new inserted files
first, mark a record as "processing"
do the ffmpeg thing
mark the file as "processed"
And, on the webpage, you can show to the user in which state his file is :
if it has not been processed yet
if it's being processed
or if it's been processed -- you can then give him the link to the new video file.
Here's a couple of other thoughts :
The day your application becomes bigger, you can have :
one "web server"
many "processing servers" ; in your application, it's the ffmpeg thing that will require lots of CPU, not serving web pages ; so, being able to scale that part is nice (that's another to "lock" files, indicating them as "processing" in DB : that way, you will not have several processing servers trying to process the same file)
You only use PHP from the web server to generate web pages, which is je job of a web server
Heavy / long processing is not the job of a web server !
The day you'll want to switch to something else than PHP for the "processing" part, it'll be easier.
Your "processing script" would have to be launch every couple of minutes ; you can use cron for that, if you are on a Linux-like machine.
Edit : a bit more informations, after seeing the comment
As the processing part is done from CLI, and not from Apache, you don't need anykind of "background" manipulations : you can just use shell_exec, which will return the whole ouput of the command to your PHP script when it's finished doing it's job.
For the user watching the web page saying "processing", it will seem like background processing ; and, in a way, it'll be, as the processing will be done by another processus (maybe even on another machine).
But, for you, it'll be much simpler :
one webpage (nothing "background")
one CLI script, with no background stuff either.
Your processing script could look like something like this, I suppose :
// Fetch informations from DB about one file to process
// and mark it as "processing"
// Those would be fetched / determined from the data you just fetched from DB
$in_file = 'in-file.avi';
$out_file = 'out-file.avi';
// Launch the ffmpeg processing command (will probably require more options ^^ )
// The PHP script will wait until it's finished :
// No background work
// No need for any kind of polling
$output = shell_exec('ffmpeg ' . escapeshellarg($in_file) . ' ' . escapeshellarg($out_file));
// File has been processed
// Store the "output name" to DB
// Mark the record in DB as "processed"
Really easier than what you first thought, isn't it ? ;-)
Just don't worry about the background stuff anymore : only thing important is that the processing script is launched regularly, from crontab.
Hope this helps :-)
You don't need to write a separate php script to do this (Though you may want to later if you implement some sort of queuing system).
You're almost there. The only problem is, the shell_exec() call blocks to wait for the return of the shell. You can avoid this if you redirect all output from the command in the shell to wither a file or /dev/null and background the task (with the & operator).
So your code would become:
//Run linux command in background and return the PID created by the OS
function run_in_background($Command, $Priority = 0)
{
if($Priority) {
shell_exec("nohup nice -n $Priority $Command 2> /dev/null > /dev/null &");
} else {
shell_exec("nohup $Command 2> /dev/null > /dev/null &");
}
}
I don't think there is any way to retrieve the PID, unfortunately.

Categories