使用strace來查詢php的坑

weixin_33968104發表於2018-11-21

一段廢話

之前的shell分享中提到過strace這個流弊的工具,當時也是用linux C的程式碼造了一個場景來演示,而今再使用php構造一個場景來演示下如何查詢一些坑,儘管差異不是很大

問題

先看下問題,最後會描述下構造的問題,問題如下

[root@c488ca153abc xiaoju]# php7/bin/php strace.php &

執行了一個php指令碼,沒有按照預期時間內返回,目測是阻塞住了

查問題

首先查pid

[root@c488ca153abc xiaoju]# ps aux | grep strace.php
root     12052  0.0  0.0 222344 10816 pts/0    S    18:15   0:00 php7/bin/php strace.php

通過strace看下12052程式的系統呼叫的阻塞情況

[root@c488ca153abc ~]# strace -p 12052
Process 12052 attached
wait4(-1,

可以看到當前阻塞在wait4,該系統呼叫會掛起當前程式,等待指定的子程式狀態改變,其中引數是-1表示等待任一子程式的返回,如此我們去檢視下12052下面的子程式

[root@c488ca153abc ~]# pstree -p 12052
php(12052)───php(12053)

繼續使用strace看看12503為什麼死也不返回

[root@c488ca153abc ~]# strace -p 12053
Process 12053 attached
flock(3, LOCK_EX

好吧,是flock檔案鎖,還是排他鎖,檔案描述符是3,看下對應的檔名是啥
可以通過lsof看,也可以通過/proc/12053/fd目錄檢視,該檔案包含程式開啟檔案的情況

[root@c488ca153abc fd]# ll
總用量 0
lrwx------ 1 root root 64 11月 21 18:16 0 -> /dev/pts/0
lrwx------ 1 root root 64 11月 21 18:16 1 -> /dev/pts/0
lrwx------ 1 root root 64 11月 21 18:16 2 -> /dev/pts/0
lrwx------ 1 root root 64 11月 21 18:16 3 -> /home/xiaoju/lock
lrwx------ 1 root root 64 11月 21 18:16 5 -> socket:[2333709193]

倒數第三列就是檔案描述符,3 -> /home/xiaoju/lock,找到12053心心念唸的檔案了,看下是誰佔用了這個檔案

[root@c488ca153abc fd]# lsof /home/xiaoju/lock | grep -v 12053
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
php     12052 root    3uW  REG  253,6        0 17725454 /home/xiaoju/lock

至此,可以斷定是一個'死鎖'了,父程式鎖著檔案等待子程式返回,子程式一直在等待父程式釋放檔案鎖,如果還要進一步確定,可以到/proc/12052/目錄中看到更多詳細的資訊,這裡不贅述.

構造問題

構造了一個如下的問題,使用pcntl_fork建立一個父子程式,父子程式同時開啟一個檔案(核心級別會是兩個不同的控制程式碼),父程式先鎖住A檔案,然後開始pcntl_wait等待子程式的返回,最後釋放鎖,子程式以阻塞模式去獲取檔案鎖,造成一個雙等待的阻塞,如下是php的程式碼

<?php
/**
 * Created by tanlin
 * Email: jokertanlin@didichuxing.com
 * Date: 2018/11/21
 * Time: 下午4:49
 */

$pid = pcntl_fork();
$fp = fopen("./lock", "a+");

if ($pid == -1) {
    // failed to fork
    die('could not fork');
} else if ($pid) {
    // parent wait
    flock($fp, LOCK_EX);
    pcntl_wait($status);
    flock($fp, LOCK_UN);
    exit(0);
} else {
    // child
    sleep(5);
    $waitIfLocked = true;
    flock($fp, LOCK_EX, $waitIfLocked);
    exit(0);
}

相關文章