PHP프로그램을 웹서버를 통해 실행 시키는 것이 아니라 리눅스 쉘에서 실행 시킬때 현재의 프로세스ID(pid)를 저장해 두었다가 다시 실행 할때 해당 프로세스 ID를 무조건 kill 시킨 다음 진행하는 프로그램 입니다. 간혹 종료 되지 않고 남아 있는 경우가 있을 경우 유용합니다.
$_NOW_PRG = basename(__FILE__);
$_NOW_PRG = str_replace('.php', '', $_NOW_PRG);
$pid_fname = "$_PID_/" . "{$_NOW_PRG}_$MODE.pid";
// echo $pid_fname . "\n"; exit;
// 시작은 무조건 restart로 한다.
$STATUS = 'restart';
if( ($STATUS == 'stop' || $STATUS == 'restart' ) && is_file($pid_fname) )
{
$pid_no = file_get_contents($pid_fname);
if ( is_numeric($pid_no) ) {
$rExec = shell_exec("/bin/kill $pid_no");
$rUnlink = unlink($pid_fname);
}
echo date('Y-m-d H:i:s ') . "STATUS: $STATUS -> $rExec / $rUnlink: $pid_fname \n";
}
# stop를 쉽게 할 수 있도록 로그를 남긴다.
if( $STATUS == 'restart' )
{
$mypid = getmypid();
$wrtSize = file_put_contents($pid_fname, $mypid);
echo date('Y-m-d H:i:s ') . "STATUS: $STATUS -> $mypid / $wrtSize:$pid_fname / HOST_NAME:$HOST_NAME \n";
}
if( $STATUS == 'stop' ) {
echo date('Y-m-d H:i:s ') . "end ... \n";
exit;
}
# 유저를 변경해 줘야 되는 경우
// chown($pid_fname, 'users');
// chgrp($pid_fname, 'users');
위는 start 없이 무조건 restart 하는것으로 했는데요. 쉘에서 프로그램 실행 할 때 프로세스(pid) 저장 후 재시작(restart) 할 때 kill(종료) 시키는 상황에 따라 프로그램을 약간 수정하여 사용하면 됩니다.
/**
* phpRestart
*
* @param string $MODE='' $argv[1]
* @param string $STATUS='' $argv[2]
*
* @return string
*
*/
function phpRestart( string $MODE = '', string $STATUS = '' ) : string
{
$_NOW_PRG = basename(__FILE__);
$_NOW_PRG = str_replace('.php', '', $_NOW_PRG);
$pid_fname = "$_PID_/" . "{$_NOW_PRG}_$MODE.pid";
$mypid = getmypid();
// echo $pid_fname . "\n"; exit;
// 시작은 무조건 restart로 한다.
if ( strlen($STATUS) < 1 ) $STATUS = 'restart';
if( ( $STATUS == 'stop' || $STATUS == 'restart' ) && is_file($pid_fname) )
{
$pid_no = file_get_contents($pid_fname);
if ( is_numeric($pid_no) ) {
$rExec = shell_exec("/bin/kill $pid_no");
$rUnlink = unlink($pid_fname);
}
echo date('Y-m-d H:i:s ') . "STATUS: $STATUS -> $rExec / $rUnlink: $pid_fname \n";
}
# stop를 쉽게 할 수 있도록 로그를 남긴다.
if( $STATUS == 'start' || $STATUS == 'restart' )
{
$wrtSize = file_put_contents($pid_fname, $mypid);
echo date('Y-m-d H:i:s ') . "STATUS: $STATUS -> $mypid / $wrtSize:$pid_fname / HOST_NAME:$HOST_NAME \n";
}
if( $STATUS == 'stop' ) {
echo date('Y-m-d H:i:s ') . "end ... \n";
exit;
}
return $mypid;
}
간단한 것은 잘 만들면 함수가 간단하며 좋고, 여러가지 기능을 묶어서 사용하면 클래스도 무척 좋습니다.
단, 클래스는 특성상 코딩의 양이 많아 지긴 합니다.
함수로 모두 만들다 보면 겹치지 말아야 되어 함수명이 길어지게 됩니다.
어느 한쪽이 무조건 좋은것은 아니기에 뭐든 적절하게 그때그때 유용한것을 선택하면 됩니다.
아래는 위와 비슷한데 클래스로 만들어진 것입니다.
class MyProgram
{
private static $pidFile = '/tmp/my_process.pid';
private static $hostname = '';
public static function setPidFile( string $pidFile='' ) {
global $argv;
// 'a': This is the default. Contains all modes in the sequence "s n r v m".
// 's': Operating system name. eg. FreeBSD.
// 'n': Host name. eg. localhost.example.com.
// 'r': Release name. eg. 5.1.2-RELEASE.
// 'v': Version information. Varies a lot between operating systems.
// 'm': Machine type. eg. i386.
$hostname = php_uname('n');
self::$hostname = $hostname;
if ( strlen($pidFile) < 1 ) {
$basename = basename($argv[0]);
$pidFile = str_replace('.php', '.pid', $basename);
$pidFile = __DIR__ . '/' . $hostname . '_' . $pidFile;
}
self::$pidFile = $pidFile;
return $pidFile;
}
public static function start() {
$pidFile = self::$pidFile;
if(file_exists($pidFile)) {
echo "프로그램이 실행중이다: $pidFile\n";
return;
}
$pid = getmygid();
$wrtSize = file_put_contents(self::$pidFile, $pid);
if($wrtSize) {
echo "pid: $pid 저장 성공: $pidFile\n";
return $pid;
}
}
public static function stop() {
if(!file_exists(self::$pidFile)) {
echo "실행중인 프로그램 없음.\n";
return;
}
$pid = file_get_contents(self::$pidFile);
if( strlen($pid) > 0 && is_numeric($pid) && posix_kill($pid, 0)) {
// SIGKILL:9 SIGTERM:15
if(posix_kill($pid, SIGKILL))
{
$rUnlink = unlink(self::$pidFile);
echo "$pid 프로그램 종료.\n";
return $rUnlink;
}
else {
echo "$pid 프로그램 멈추는데 실패.\n";
}
}
else {
// 필요하지 않기 때문에 파일은 삭제해 준다.
$rUnlink = unlink(self::$pidFile);
echo "pid: $pid 없습니다. - stop 실패!!" . PHP_EOL;
}
}
public static function restart() {
$rstop = self::stop();
sleep(1);
$pid = self::start();
return $pid;
}
}
#
$pidFile = MyProgram::setPidFile();
$pid = MyProgram::restart();
echo 'pidFile: ' . $pidFile . PHP_EOL;
exit;
SIGKILL과 SIGTERM은 UNIX 및 Linux 시스템에서 프로세스에 보낼 수 있는 시그널(signal)의 종류 중 두 가지입니다.
SIGTERM (시그널 번호 15)
이 시그널은 프로세스에게 "종료해주세요"라고 요청하는 것과 같습니다.
프로세스는 SIGTERM 시그널을 받으면 정리 작업을 수행한 후에 종료할 수 있습니다.
즉, 프로세스가 SIGTERM 시그널을 받으면 필요한 자원을 정리하고, 열려 있는 파일을 닫고, 네트워크 연결을 종료하고, 메모리를 해제하는 등의 작업을 수행한 후에 종료합니다. 또한, 프로세스는 SIGTERM 시그널을 잡아서 무시하거나 다르게 처리하는 것도 가능합니다.
- SIGKILL (시그널 번호 9)**
이 시그널은 프로세스에게 "지금 당장 종료해라"라고 명령하는 것과 같습니다.
SIGKILL 시그널을 받은 프로세스는 즉시 종료되며, 어떠한 정리 작업도 수행하지 않습니다.
또한, 프로세스는 SIGKILL 시그널을 잡아서 무시하거나 다르게 처리하는 것이 불가능합니다.
따라서, 일반적으로 프로세스를 종료하려면 먼저 SIGTERM 시그널을 보내서 프로세스에게 종료를 요청하고,
프로세스가 종료되지 않을 경우에만 SIGKILL 시그널을 보내서 강제로 종료하는 방식을 사용합니다. 하지만 난 죽이는게 목적이라서 보통 9를 사용한다.
● 마지막으로 간단하게 중복 실행만 방지하고자 할 때 이전것 무조건 kill
# 서버에서 동일한 PHP 스크립트가 동시에 두 번 이상 실행되는 것을 방지하고 싶을 때 사용됩니다.
# 예를 들어, 특정 스크립트가 시스템 자원을 많이 사용하거나, 같은 작업을 중복으로 수행하는 것을 방지하기 위해 사용할 수 있습니다.
# 서버 운영에 있어 중요한 원칙 중 하나인 '동일한 작업의 중복 실행 방지'를 실현하는 방법
$pidFilePath = "pid-저장할-파일경로.pid";
if (is_file($pidFilePath)) {
posix_kill(file_get_contents($pidFilePath), 9);
}
file_put_contents($pidFilePath, getmypid());
* 클래스로 만들어진것 참고
- PHP를 쉘에서 실행 시킬때(CLI 환경) restart 시키는 방법 > PHP