多執行緒中使用fork 導致分頁

2021-08-19 13:18:29 字數 2516 閱讀 6455

最近和同事一起處理了乙個 fuse 的大bug;

首先看堆疊:

core was generated by `/sf/cluster/bin/pmxcfs'.

program terminated with signal sigabrt, aborted.

#0  0x00007f2debdcc475 in raise () from /lib/x86_64-linux-gnu/libc.so.6

(gdb) bt

#0  0x00007f2debdcc475 in raise () from /lib/x86_64-linux-gnu/libc.so.6

#1  0x00007f2debdcf6f0 in abort () from /lib/x86_64-linux-gnu/libc.so.6

#2  0x00007f2debdc5621 in __assert_fail () from /lib/x86_64-linux-gnu/libc.so.6

#3  0x00007f2ded4035d2 in fuse_do_release (f=f@entry=0x16148b0, ino=ino@entry=330, path=0x7f2ddc00e040 "/acl.cfg", fi=fi@entry=0x7f2dd73f4b50) at fuse.c:3092

#4  0x00007f2ded403656 in fuse_lib_release (req=0x7f2ddc00c230, ino=330, fi=0x7f2dd73f4b50) at fuse.c:3876

#5  0x00007f2ded4094eb in do_release (req=, nodeid=, inarg=) at fuse_lowlevel.c:1345

#6  0x00007f2ded409f16 in fuse_ll_process_buf (data=0x1614ba0, buf=0x7f2dd73f4d00, ch=) at fuse_lowlevel.c:2441

#7  0x00007f2ded406c5b in fuse_do_work (data=0x7f2dcc0009e0) at fuse_loop_mt.c:117

#8  0x00007f2deca6eb50 in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0

#9  0x00007f2debe74a7d in clone () from /lib/x86_64-linux-gnu/libc.so.6

#10 0x0000000000000000 in ?? ()

(gdb) 

分析:1、這個是libfuse庫源生的bug,在網上檢視了下沒有發現有這個bug的相關資訊;

2、core分析:

(gdb) p  *(f->id_table.array[3626])

$15 = , mtime = , size = 0, locks = 0x0, is_hidden = 0, cache_valid = 0, 

treelock = 1, inline_name = "acl.cfg", '\000' }

對應**:

pthread_mutex_lock(&f->lock);

node = get_node(f, ino);

assert(node->open_count > 0);// node->open_count = 0

--node->open_count;

if (node->is_hidden && !node->open_count)

pthread_mutex_unlock(&f->lock);

====

本來想好好寫排查過程的,還有一些有效的排查方法的,但發現 csdn 好爛,都找不到預覽功能在**了;

重現方法:

1. 修改**,多個執行緒內增加呼叫system;

2. 修改**,在fuse_kern_chan_receive裡加強assert,如果read正常返回但是緩衝區沒改過,即斷言

這樣可以加大重現概率,基本一天至少可以出4、5個

問題原因:

多執行緒內fork,父子程序先是共用記憶體頁 準備copy-on-write, a執行緒等在read /dev/fuse上,a執行緒的read陷入系統呼叫 fuse.ko準備將資料寫入記憶體,此時b執行緒寫同乙個記憶體頁的記憶體觸發cow,父程序最終使用新頁表新記憶體,但read系統呼叫將資料寫入了舊記憶體

對應用層的表現是,read返回值正確,但是緩衝區資料沒有填充,應用層把緩衝區上舊命令當作新命令執行了兩次

原始不改**的情況下通常表現為兩種core,乙個是fuse_do_release裡open計數斷言,另乙個是fuse_lib_releasedir裡釋放記憶體,其他情況雖然可能命令執行錯了,但通常不會表現出core,而是上層訪問可能異常;

修改辦法:

1、命令結構體申請記憶體時,用4k對齊函式來申請(偏移量4k對齊,申請記憶體大小也要4k對齊);

2、把可能的在多執行緒中呼叫fork()的系統呼叫去掉;

多執行緒服務內應避免使用fork,如system、popen

多執行緒與fork

多執行緒程式裡盡量不使用fork 在多執行緒程式裡,在 自身以外的執行緒存在的狀態 下一使用fork的話,就可能引起各種各樣的問題.比較典型的例子就是,fork出來的子程序可能會死鎖.請不要,在不能把握問題的原委的情況下就在多執行緒程式裡fork子程序,能引起什麼問題呢?那看看例項吧.一執行下面的 ...

c 中使用多執行緒

using system using system.drawing using system.collections using system.componentmodel using system.windows.forms using system.data using system.threa...

c 中使用多執行緒

using system using system.drawing using system.collections using system.componentmodel using system.windows.forms using system.data using system.threa...