1. What is a daemon process
A daemon is a process that is separated from the terminal and runs in the background. The daemon process is separated from the terminal to prevent information during the execution of the process from being displayed on any terminal and the process will not be interrupted by terminal information generated by any terminal.
For example, apache, nginx, and mysql are all daemon processes
2. Why develop daemon process
Many programs exist in the form of services. They do not have terminal or UI interaction. They may interact with other programs in other ways, such as TCP/UDP Socket, UNIX Socket, fifo. Once the program is started, it goes into the background and starts processing tasks until the conditions are met.
3. When to use daemon process to develop applications
Take my current needs as an example. I need to run a program, then listen to a certain port, continue to receive data initiated by the server, then analyze and process the data, and then write the results to the database; I use ZeroMQ to implement the data Send and receive.
If I do not use the daemon process to develop this program, once the program is run, it will occupy the current terminal window frame, and it may be affected by the current terminal keyboard input, and the program may exit accidentally.
4. Security issues of daemon process
We hope that the program will run as a non-superuser, so that once the program is controlled by a hacker due to a vulnerability, the attacker can only inherit the running permissions but cannot obtain the superuser permissions.
We hope that the program can only run one instance, and do not run more than two programs at the same time, because problems such as port conflicts will occur.
5. How to develop a daemon process
Example 1. Daemon process example
<?php class ExampleWorker extends Worker { #public function __construct(Logging $logger) { # $this->logger = $logger; #} #protected $logger; protected static $dbh; public function __construct() { } public function run(){ $dbhost = '192.168.2.1'; // 数据库服务器 $dbport = 3306; $dbuser = 'www'; // 数据库用户名 $dbpass = 'qwer123'; // 数据库密码 $dbname = 'example'; // 数据库名 self::$dbh = new PDO("mysql:host=$dbhost;port=$dbport;dbname=$dbname", $dbuser, $dbpass, array( /* PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'', */ PDO::MYSQL_ATTR_COMPRESS => true, PDO::ATTR_PERSISTENT => true ) ); } protected function getInstance(){ return self::$dbh; } } /* the collectable class implements machinery for Pool::collect */ class Fee extends Stackable { public function __construct($msg) { $trades = explode(",", $msg); $this->data = $trades; print_r($trades); } public function run() { #$this->worker->logger->log("%s executing in Thread #%lu", __CLASS__, $this->worker->getThreadId() ); try { $dbh = $this->worker->getInstance(); $insert = "INSERT INTO fee(ticket, login, volume, `status`) VALUES(:ticket, :login, :volume,'N')"; $sth = $dbh->prepare($insert); $sth->bindValue(':ticket', $this->data[0]); $sth->bindValue(':login', $this->data[1]); $sth->bindValue(':volume', $this->data[2]); $sth->execute(); $sth = null; /* ...... */ $update = "UPDATE fee SET `status` = 'Y' WHERE ticket = :ticket and `status` = 'N'"; $sth = $dbh->prepare($update); $sth->bindValue(':ticket', $this->data[0]); $sth->execute(); //echo $sth->queryString; //$dbh = null; } catch(PDOException $e) { $error = sprintf("%s,%s\n", $mobile, $id ); file_put_contents("mobile_error.log", $error, FILE_APPEND); } } } class Example { /* config */ const LISTEN = "tcp://192.168.2.15:5555"; const MAXCONN = 100; const pidfile = __CLASS__; const uid = 80; const gid = 80; protected $pool = NULL; protected $zmq = NULL; public function __construct() { $this->pidfile = '/var/run/'.self::pidfile.'.pid'; } private function daemon(){ if (file_exists($this->pidfile)) { echo "The file $this->pidfile exists.\n"; exit(); } $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent //pcntl_wait($status); //Protect against Zombie children exit($pid); } else { // we are the child file_put_contents($this->pidfile, getmypid()); posix_setuid(self::uid); posix_setgid(self::gid); return(getmypid()); } } private function start(){ $pid = $this->daemon(); $this->pool = new Pool(self::MAXCONN, \ExampleWorker::class, []); $this->zmq = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REP); $this->zmq->bind(self::LISTEN); /* Loop receiving and echoing back */ while ($message = $this->zmq->recv()) { //print_r($message); //if($trades){ $this->pool->submit(new Fee($message)); $this->zmq->send('TRUE'); //}else{ // $this->zmq->send('FALSE'); //} } $pool->shutdown(); } private function stop(){ if (file_exists($this->pidfile)) { $pid = file_get_contents($this->pidfile); posix_kill($pid, 9); unlink($this->pidfile); } } private function help($proc){ printf("%s start | stop | help \n", $proc); } public function main($argv){ if(count($argv) < 2){ printf("please input help parameter\n"); exit(); } if($argv[1] === 'stop'){ $this->stop(); }else if($argv[1] === 'start'){ $this->start(); }else{ $this->help($argv[0]); } } } $cgse = new Example(); $cgse->main($argv);
5.1. Program startup
The following is the code to enter the background after the program is started
Judge the current process status through the process ID file. If the process ID file exists, it means that the program is running. This is achieved through the code file_exists($this->pidfile). However, if the process is killed, the file must be manually deleted before it can run.
private function daemon(){ if (file_exists($this->pidfile)) { echo "The file $this->pidfile exists.\n"; exit(); } $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent //pcntl_wait($status); //Protect against Zombie children exit($pid); } else { // we are the child file_put_contents($this->pidfile, getmypid()); posix_setuid(self::uid); posix_setgid(self::gid); return(getmypid()); } }
After the program starts, the parent process will be launched, the child process will run in the background, the child process permissions will be switched from root to the specified user, and the pid will be written to the process ID file.
5.2. Program stop
The program stops, just read the pid file, then call posix_kill($pid, 9); and finally delete the file.
private function stop(){ if (file_exists($this->pidfile)) { $pid = file_get_contents($this->pidfile); posix_kill($pid, 9); unlink($this->pidfile); } }
Very advanced ones will be involved. Because Java can do so much.
It is recommended to use ignore_user_abort for specific ideas. Private chat and long-term cooperation are also possible, but time is a bit tight