服务器之家

服务器之家 > 正文

PHP 多进程与信号中断实现多任务常驻内存管理实例方法

时间:2021-08-26 14:28     来源/作者:藏色散人

本文章基于 pcntl 扩展做的多进程测试。

进程调度策略

父子进程的调度由操作系统来负责,具体先调度子进程还是父进程由系统的调度算法决定,当然可以在父进程加上延时或是调用进程回收函数 pcntl_wait 可以先让子进程先运行,进程回收的目的是释放进程创建时占用的内存空间,防止变成僵尸进程。

信号:

信号称为软中断系统或是叫软中断,功能是向进程发送异步事件通知。

信号编号: 【源码基于 SIGINT,SIGTERM,SIGUSR1 信号,含义请自行查看 kill 命令手册,不在描述】

linux 支持 64 个,有一半为实时信号,一半为非时实信号,这些信号都有自己的编号和对应的整数值。每个信号的编号含义读者可以参阅 linux 相关手册【man 手册看看就知道了】

信号处理函数:

信号一般会绑定相应的功能,有的是默认动作如 SIGKILL,SIGTERM,SIGINT 操作默认操作就是干掉进程,当然我们可以重写覆盖掉,就是通过 pcntl_signal 来覆盖掉。

信号的概念:与硬件中断一个道理,请读者自行参考本人前面撸过的文章或是查看芯片硬件中断原理。

信号的发送:

kill 信号编号 进程 或是按键产品的中断信号或是在源码里可以使用 posix_kill 等函数。

进程是相互隔离的,拥有自己的堆栈空间,除了一些公用的正文【代码区】,同时也有自己的可执行代码,进程运行时,将占用 cpu 的资源,其它进程将无权运行,此时其它进程将为阻塞状态【比如前面撸过的 tcp 服务】,当进程运行结束后【运行到代码的最后一句或是遇到 return 或是遇到 exit 退出进程函数或是遇到信号事件时将会退出】让出权限并释放掉内存,其它进程就有机会运行了。

进程拥有的自己进程描述符,其中比较常用的是进程号 PID,进程运行时会在系统 /proc/PID 下生成相应的进程文件,用户可以自行查看。

每个进程都拥有所属的进程组【进程的集合】,多个进程组集合则是一个会话,创建一个会话是通过一个进程进行创建的,并且此进程不可以为组长进程,此进程将成为会话期的会话首进程,也会成为进程组的进程组长,同时将会脱离控制终端,即使之前的进程绑定了控制终端也会脱离【守护进程的创建】。

文件描述权限掩码【权限屏蔽字】:

umask () 你可以在 linux 运行这个命令,然后创建文件,并查看它的权限【如果你跑完啥也没有发现,说明你还是训练不够 ^_^】

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
<?php
 
/**
 
 * Created by PhpStorm.
 
 * User: 1655664358@qq.com
 
 * Date: 2018/3/26
 
 * Time: 14:19
 
 */
 
namespace Chen\Worker;
 
class Server
 
{
 
 public $workerPids = [];
 
 public $workerJob = [];
 
 public $master_pid_file = "master_pid";
 
 public $state_file = "state_file.txt";
 
 function run()
 
 {
 
  $this->daemon();
 
  $this->worker();
 
  $this->setMasterPid();
 
  $this->installSignal();
 
  $this->showState();
 
  $this->wait();
 
 }
 
 function wait()
 
 {
 
  while (1){
 
   pcntl_signal_dispatch();
 
   $pid = pcntl_wait($status);
 
   if ($pid>0){
 
    unset($this->workerPids[$pid]);
 
   }else{
 
    if (count($this->workerPids)==0){
 
     exit();
 
    }
 
   }
 
   usleep(100000);
 
  }
 
 }
 
 function showState()
 
 {
 
  $state = "\nMaster 信息\n";
 
  $state.=str_pad("master pid",25);
 
  $state.=str_pad("worker num",25);
 
  $state.=str_pad("job pid list",10)."\n";
 
  $state.=str_pad($this->getMasterPid(),25);
 
  $state.=str_pad(count($this->workerPids),25);
 
  $state.=str_pad(implode(",",array_keys($this->workerPids)),10);
 
  echo $state.PHP_EOL;
 
 }
 
 function getMasterPid()
 
 {
 
  if (file_exists($this->master_pid_file)){
 
   return file_get_contents($this->master_pid_file);
 
  }else{
 
   exit("服务未运行\n");
 
  }
 
 }
 
 function setMasterPid()
 
 {
 
  $fp = fopen($this->master_pid_file,"w");
 
  @fwrite($fp,posix_getpid());
 
  @fclose($fp);
 
 }
 
 function daemon()
 
 {
 
  $pid = pcntl_fork();
 
  if ($pid<0){
 
   exit("fork进程失败\n");
 
  }else if ($pid >0){
 
   exit(0);
 
  }else{
 
   umask(0);
 
   $sid = posix_setsid();
 
   if ($sid<0){
 
    exit("创建会话失败\n");
 
   }
 
   $pid = pcntl_fork();
 
   if ($pid<0){
 
    exit("进程创建失败\n");
 
   }else if ($pid >0){
 
    exit(0);
 
   }
 
   //可以关闭标准输入输出错误文件描述符【守护进程不需要】
 
  }
 
 }
 
 function worker()
 
 {
 
  if (count($this->workerJob)==0)exit("没有工作任务\n");
 
  foreach($this->workerJob as $job){
 
   $pid = pcntl_fork();
 
   if ($pid<0){
 
    exit("工作进程创建失败\n");
 
   }else if ($pid==0){
 
    /***************子进程工作范围**********************/
 
    //给子进程安装信号处理程序
 
    $this->workerInstallSignal();
 
    $start_time = time();
 
    while (1){
 
     pcntl_signal_dispatch();
 
     if ((time()-$start_time)>=$job->job_run_time){
 
      break;
 
     }
 
     $job->run(posix_getpid());
 
    }
 
    exit(0);//子进程运行完成后退出
 
    /***************子进程工作范围**********************/
 
   }else{
 
    $this->workerPids[$pid] = $job;
 
   }
 
  }
 
 }
 
 function workerInstallSignal()
 
 {
 
  pcntl_signal(SIGUSR1,[__CLASS__,'workerHandleSignal'],false);
 
 }
 
 function workerHandleSignal($signal)
 
 {
 
  switch ($signal){
 
   case SIGUSR1:
 
    $state = "worker pid=".posix_getpid()."接受了父进程发来的自定义信号\n";
 
    file_put_contents($this->state_file,$state,FILE_APPEND);
 
    break;
 
  }
 
 }
 
 function installSignal()
 
 {
 
  pcntl_signal(SIGINT,[__CLASS__,'handleMasterSignal'],false);
 
  pcntl_signal(SIGTERM,[__CLASS__,'handleMasterSignal'],false);
 
  pcntl_signal(SIGUSR1,[__CLASS__,'handleMasterSignal'],false);
 
 }
 
 function handleMasterSignal($signal)
 
 {
 
  switch ($signal){
 
   case SIGINT:
 
    //主进程接受到中断信号ctrl+c
 
    foreach ($this->workerPids as $pid=>$worker){
 
     posix_kill($pid,SIGINT);//向所有的子进程发出
 
    }
 
    exit("服务平滑停止\n");
 
    break;
 
   case SIGTERM://ctrl+z
 
    foreach ($this->workerPids as $pid=>$worker){
 
     posix_kill($pid,SIGKILL);//向所有的子进程发出
 
    }
 
    exit("服务停止\n");
 
    break;
 
   case SIGUSR1://用户自定义信号
 
    if (file_exists($this->state_file)){
 
     unlink($this->state_file);
 
    }
 
    foreach ($this->workerPids as $pid=>$worker){
 
     posix_kill($pid,SIGUSR1);
 
    }
 
    $state = "master pid\n".$this->getMasterPid()."\n";
 
    while(!file_exists($this->state_file)){
 
     sleep(1);
 
    }
 
    $state.= file_get_contents($this->state_file);
 
    echo $state.PHP_EOL;
 
    break;
 
  }
 
 }
 
}
 
<?php
 
/**\
 
 * Created by PhpStorm.\ * User: 1655664358@qq.com
 
 * Date: 2018/3/26\ * Time: 14:37\ */\namespace Chen\Worker;
 
class Job
 
{
 
 public $job_run_time = 3600;
 
 function run($pid)
 
 {\sleep(3);
 
 echo "worker pid = $pid job 没事干,就在这里job\n";
 
 }
 
}
 
<?php
 
/**
 
 * Created by PhpStorm.\ * User: 1655664358@qq.com
 
 * Date: 2018/3/26\ * Time: 14:37\ */\namespace Chen\Worker;
 
class Talk
 
{
 
 public $job_run_time = 3600;
 
 function run($pid)
 
 {\sleep(3);
 
 echo "worker pid = $pid job 没事干,就在这里talk\n";
 
 }
 
}
 
<?php
 
/**
 
 * Created by PhpStorm.\ * User: 1655664358@qq.com
 
 * Date: 2018/3/26\ * Time: 15:45\ */
 
require_once 'vendor/autoload.php';
 
$process = new \Chen\Worker\Server();
 
$process->workerJob = [new \Chen\Worker\Talk(),new \Chen\Worker\Job()];
 
$process->run();

PHP 多进程与信号中断实现多任务常驻内存管理实例方法

以上就是PHP 多进程与信号中断实现多任务常驻内存管理【Master/Worker 模型】的详细内容,感谢大家的学习和对服务器之家的支持。

相关文章

热门资讯

yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
2021年耽改剧名单 2021要播出的59部耽改剧列表
2021年耽改剧名单 2021要播出的59部耽改剧列表 2021-03-05
返回顶部