一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - PHP教程 - php5.2的curl-bug 服務器被php進程卡死問題排查

php5.2的curl-bug 服務器被php進程卡死問題排查

2021-03-01 18:29PHP教程網 PHP教程

這篇文章主要介紹了php5.2的curl-bug 服務器被php進程卡死問題排查,需要的朋友可以參考下

前幾天東政同學反饋說Linode服務器快卡死了,今天有時間排查了一下具體原因,最終原因稍微有點悲壯:file_get_contents沒有設置超時時間,加上我用的php5.2關于curl的代碼有個bug,于是導致PHP進程進入死循環。

今天下午又發現系統負載很高,于是上去看了一下,發現一大坨PHP進程沒有退出,占用了很多CPU,如圖:

php5.2的curl-bug 服務器被php進程卡死問題排查

問題進程:

php5.2的curl-bug 服務器被php進程卡死問題排查

后面運行的腳本是我的RSS定時更新任務,看來PHP代碼什么地方有問題,于是strace -p 14043看了一下:

?
1
2
3
4
5
6
select(5, [4], [4], [], {15, 0}) = 1 (out [4], left {14, 999996})
poll([{fd=4, events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout)
clock_gettime(CLOCK_MONOTONIC, {4582888, 760370017}) = 0
clock_gettime(CLOCK_MONOTONIC, {4582888, 760468615}) = 0
clock_gettime(CLOCK_MONOTONIC, {4582888, 760565053}) = 0
select(5, [4], [4], [], {15, 0}) = 1 (out [4], left {14, 999997})

在4號fd上面死循環了,于是看看FD是什么:ll /proc/14043/fd

lrwx—— 1 wuhaiwen wuhaiwen 64  7月 21 11:00 4 -> socket:[53176380]

再看了一下原來是在請求CSDN的一個網頁的時候死循環了,但不知道什么地方請求的,想到GDB一下php進程看看,bt顯示:

(gdb) bt
#0 0x00007f6721f8f013 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:82
#1 0×0000000000481952 in php_curl_stream_read (stream=0×2280650,
buf=0x22ea5d0 “2Fwww.laruence.com%2Ftag%2F%25e6%25ad%25a3%25e5%2588%2599%27+class%3D%27tag-link-191%27+title%3D%273+topics%27+style%3D%27font-size%3A+9.0243902439pt%3B%27%3E%E6%AD%A3%E5%88%99%3C%2Fa%3E%3C%2Ftags%3E\”"…, count=8192) at /home/wuhaiwen/install/php-env/src/php/php-5.2.8/ext/curl/streams.c:169
#2 0x00000000006738f9 in php_stream_fill_read_buffer (stream=0×2280650, size=4283) at /home/wuhaiwen/install/php-env/src/php/php-5.2.8/main/streams/streams.c:554
#3 0x0000000000673c39 in _php_stream_read (stream=0×2280650,
buf=0x2301fd5 “f='http://www.laruence.com/tag/json' class='tag-link-79′ php" id="highlighter_970485">

?
1
2
3
4
5
6
(gdb) p *op_array
$4 = {type = 2 '\002', function_name = 0x1e54278 "getContent", scope = 0x1f8e850, fn_flags = 257, prototype = 0x0, num_args = 2, required_num_args = 1, arg_info = 0x1fd5e20,
pass_rest_by_reference = 0 '\000', return_reference = 0 '\000', refcount = 0x1fd3ab8, opcodes = 0x1fddcc8, last = 28, size = 28, vars = 0x1fd3cc0, last_var = 6, size_var = 16, T = 15,
brk_cont_array = 0x0, last_brk_cont = 0, current_brk_cont = 4294967295, try_catch_array = 0x0, last_try_catch = 0, static_variables = 0x0, start_op = 0x0, backpatch_count = 0,
done_pass_two = 1 '\001', uses_this = 0 '\000', filename = 0x1fd3b58 "/home/wuhaiwen/webroot/kulvrss/libs/Myrss/Model/UrlContenter.php", line_start = 9, line_end = 30, doc_comment = 0x0,
doc_comment_len = 0, reserved = {0x0, 0x0, 0x0, 0x0}}

找到了問題代碼位置,原來是一個file_get_contents($url)調用,沒有設置超時時間,于是PHP卡死在網絡請求了。于是用stream_context_create 設置超時時間搞定。

到這里 似乎問題解決了,但是,為什么沒有設置超時時間就導致php進程占用CPU,系統負載那么高?按理說應該等待I/O才是呀?看上面CPU情況,完全是進入了死循環的節奏。

根據上面的bt堆棧,首先看倒數第二個函數的調用:

#1 0×0000000000481952 in php_curl_stream_read (stream=0×2280650,
buf=0x22ea5d0 “2Fwww.laruence.com%2Ftag%2F%25e6%25ad%25a3%25e5%2588%2599%27+class%3D%27tag-link-191%27+title%3D%273+topics%27+style%3D%27font-size%3A+9.0243902439pt%3B%27%3E%E6%AD%A3%E5%88%99%3C%2Fa%3E%3C%2Ftags%3E\”"…, count=8192) at /home/wuhaiwen/install/php-env/src/php/php-5.2.8/ext/curl/streams.c:169

看一下代碼,我用的事5.2.8版本的PHP,比較老。代碼如下:

?
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
static size_t php_curl_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
    php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
    size_t didread = 0;
    if (curlstream->readbuffer.readpos >= curlstream->readbuffer.writepos && curlstream->pending) {
//········
        do {
            /* get the descriptors from curl */
            curl_multi_fdset(curlstream->multi, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &curlstream->maxfd);
            /* if we are in blocking mode, set a timeout */
            tv.tv_usec = 0;
            tv.tv_sec = 15; /* TODO: allow this to be configured from the script */
            /* wait for data */
            switch (select(curlstream->maxfd + 1, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &tv)) {
                case -1:
                    /* error */
                    return 0;
                case 0:
                    /* no data yet: timed-out */
                    return 0;
                default:
                    /* fetch the data */
                    do {
                        curlstream->mcode = curl_multi_perform(curlstream->multi, &curlstream->pending);
                    } while (curlstream->mcode == CURLM_CALL_MULTI_PERFORM);
            }
        } while (curlstream->readbuffer.readpos >= curlstream->readbuffer.writepos && curlstream->pending > 0);
    }
//··········
    return didread;
}

GDB進去發現,代碼一直在里面的do-while里面循環了!心想curl_multi_fdset怎么不用先FD_ZERO 清空FD呢?一般做法都是會先清空的。

莫非是PHP的bug, 于是網上找了一下發現了這個Pierrick-Charron的commit,確實是一個bug, 其實curl_multi_fdset 的文檔開頭寫了的:

 

復制代碼 代碼如下:

This function extracts file descriptor information from a given multi_handle. libcurl returns its fd_set sets. The application can use these to select() on, but be sure to FD_ZERO them before calling this function as curl_multi_fdset(3) only adds its own descriptors,

 

好吧,最后用GDB驗證一下,我在上面的do下面,curl_multi_fdset調用之前,手動將fd清空,看看能否退出循環:

(gdb) print FD_ZERO(&curlstream->readfds)
No symbol “FD_ZERO” in current context.

FD_ZERO竟然沒有,不管了,其本來是個宏定義,展開就行:#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))

直接用call修改curl_muti_fdset的三個參數數組如下:

 

復制代碼 代碼如下:

(gdb) call bzero((char *)(&curlstream->readfds), sizeof(*(&curlstream->readfds)))
$5 = 17055392
(gdb) call bzero((char *)(&curlstream->writefds),sizeof(*(&curlstream->writefds)))
$6 = 17055520
(gdb) call bzero((char *)(&curlstream->excfds), sizeof(*(&curlstream->excfds)))
$7 = 17055648

 

然后GDB單步執行,如期的由于curlstream->pending變為0,從而退出了循環,回到php_stream_fill_read_buffer的大函數了

到此基本結束。有問題的PHP版本應該是5.2. 具體沒有細看,讀者可以參考下上面的這個提交改動或者直接看自己的版本代碼是否有問題。

  • bug
  • PHP
  • 進程
  • cURL
  • 卡死
  • 延伸 · 閱讀

    精彩推薦
    主站蜘蛛池模板: 国产精品99精品久久免费 | 嗯啊在线观看免费影院 | 色综合色狠狠天天久久婷婷基地 | 免费超级乱淫播放手机版 | 日本红色高清免费观看 | 国产自拍资源 | 国产婷婷高清在线观看免费 | 激情影院网站 | 亚洲第一男人网站 | 香蕉免费一区二区三区 | 高h肉爽文农民工 | kk4444了欧美 | 天天做日日做天天添天天欢公交车 | 五月色婷婷在线影院 | 日本精品vide·ssex日本 | 超级碰碰免费视频 | 国产精品青青青高清在线观看 | 男人操女人视频 | 91精品国产高清久久久久久91 | 69欧美另类xxxxx高清 | 午夜欧美精品 | 女仆色网址| 2012中文字幕中字视频 | 亚洲美女啪啪 | 久久婷婷五月综合色丁香 | 国产免费一区二区三区 | 人与善交大片免费看 | 美女被草逼 | 四色6677最新永久网站 | 日本无翼乌漫画 | 99国产精品热久久久久久夜夜嗨 | 男人插曲女人身体 | 亚洲欧美日韩特级毛片 | 九九热国产视频 | 国产高清不卡码一区二区三区 | 天天碰夜夜操 | 免费高清www动漫视频播放器 | 亚洲AV 日韩 国产 有码 | 性生大片免费看 | 色婷婷久久综合中文久久一本 | 青青网站 |