df 和 ls 命令執行夯主
作者:張首富
時間:2020-05-21
w x:y18163201
原因
今天一個朋友問我一個問題,他的描述如下:
1,ls / 執行這個命令的時候卡住,什么反應都沒有,但是 ls 其他目錄的時候就沒有事情
2,df 查看文件掛載的時候也卡住,
3,我什么都沒干啊,就做了一個 ISCSI
其實他說第二點問題的時候我就已經猜到問題所在了,那不就是遠程掛載的磁盤非正常的掉了,然后就會造成這個問題。但是他說 ISCSI 這個玩意的時候我不知道是啥,於是查了一下,有興趣的同學可以看看這是:https://zhuanlan.zhihu.com/p/60986068,看的出來他是一個網絡存儲,那么就更加堅定我的想法了,開始指揮解決問題。
解決辦法
問題原因大概猜想出來之后,就是開始解決問題了。
找到是哪個掛載點失效了
我們可以使用此命令來查找掛載點哪個地方失效了。
strace df -Th
執行卸載
通過上面命令我們能找到是哪個掛載點失效了,我們可以使用下面命令先嘗試卸載
umount -f 有問題的掛載點
然后等個1~2 分鍾看看效果,-f 是強制卸載的意思。
如果使用這個命令還是解決不了問題,那么我們先確認有哪些進程在占用這個掛載點
fuser -cu 掛載點
會得到進程 ID,和進程命令,看看此進程是否能正常殺掉,如果可以我們手工停止這個進程,然后進行卸載,或者直接使用下面命令進行卸載
fuser -ck 掛載點
strace 工具實現原理
什么是 strace
它是一個具有 Linux 傳統命令行界面的診斷、調試和指令性用戶空間的使用程序。它用於監控和篡改進程與 Linux 內核之間的交互,包括系統調用、信號傳遞和進程狀態的變化。strace 的所有操作都是通過內核的ptrace功能來實現的。項目主頁是:https://github.com/strace/strace;
關於 ptrace 相關知識可以閱讀:
https://jin-yang.github.io/post/linux-ptrace-api-introduce.html
使用 strace
最簡單的 strace 命令的用法就是: strace PROG;PROG 就是要執行的程序(linux 命令)。strace 命令執行的結果就是按照調用順序打印出所有的系統調用,包括函數名、參數列表以及返回值。
使用 strace 跟蹤一個進程的系統調用的基本流程如下圖:

(此圖片來源網絡)
從圖中可以看出strace做了以下幾件事情:
-
設置SIGCHLD 信號的處理函數,這個處理函數只要不是SIG_IGN即可。由於子進程停止后是通過SIGCHLD信號通知父進程的,所以這里要防止SIGCHLD信號被忽略。
-
創建子進程,在子進程中調用ptrace(PTRACE_TRACEME,0L, 0L, 0L)使其被父進程跟蹤,並通過execv函數執行被跟蹤的程序。
-
通過wait()等待子進程停止,並獲得子進程停止時的狀態status。
-
通過子進程的狀態查看子進程是否已正常退出,如果是,則不再跟蹤,隨后調用ptrace發送PTRACE_DETACH請求解除跟蹤關系。
-
子進程停止后,打印系統調用的函數名、參數和返回值。具體流程見圖2。
-
通過PTRACE_SYSCALL讓子進程繼續運行,由於這個請求會讓子進程在系統調用的入口處和系統調用完成時都會停止並通知父進程,這樣,父進程就可以在系統調用開始之前獲得參數,結束之后獲得返回值。
在系統調用的入口和結束時子進程停止運行時,這時父進程認為子進程是因為收到SIGTRAP信號而停止的。所以父進程在wait()后可以通過SIGTRAP來與其他信號區分開。
Strace中為每個要跟蹤的進程維護了一個TCB(Trace Control Block)結構,定義如下。它保存了當前發生的系統調用的信息。
/* Trace Control Block */
struct tcb {
int flags; /* See below for TCB_ values */
int pid; /* Process Id of this entry */
int qual_flg; /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW*/
int u_error; /* Error code */
long scno; /* System call number */
long u_arg[MAX_ARGS]; /* System call arguments */
long u_rval; /* Return value */
int curcol; /* Output column for this process */
FILE *outf; /* Output file for this process */
const char *auxstr;/*Auxiliary info from syscall (see RVAL_STR) */
const struct_sysent *s_ent;/* sysent[scno] or dummy struct for bad scno */
struct timeval stime;/*System time usage as of last process wait */
struct timeval dtime; /* Delta for system time usage */
struct timeval etime; /* Syscall entry time */
/* Support fortracing forked processes: */
long inst[2]; /* Saved clone args (badly named) */
};
上面已經提到,子進程會在系統調用前后各停止一次,所以打印系統調用信息時分為兩個階段:在系統調用開始時可以獲取系統調用號和參數,在系統調用結束時可以獲取系統調用的返回結果。通過給tcb結構的flags字段清除和添加TCB_INSYSCALL標志位來區分系統調用的開始和結束。

例如編寫一個使用printf打印“Hello world”的程序hello.c,使用strace跟蹤該程序的系統調用可以看到如下結果:
# ./strace ./hello
execve("./hello ", ["./hello "], [/* 7 vars */])= 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,-1, 0) = 0x2aaad000
stat("/etc/ld.so.cache", 0x7faf4ca8) = -1 ENOENT (No such file or directory)
open("/tmp/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/libgcc_s.so.1", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1565445, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,-1, 0) = 0x2aaae000
read(3,"\177ELF\1\2\1\0\0\0\0\0\0\0\0\0\0\3\0\10\0\0\0\1\0\0\263\200\0\0\0004"...,4096) = 4096
mmap(NULL, 241664, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0x2aabe000
mmap(0x2aabe000, 169308, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED,3, 0) = 0x2aabe000
mmap(0x2aaf8000, 2400, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED,3, 0x2a000) = 0x2aaf8000
close(3) = 0
munmap(0x2aaae000, 4096) = 0
open("/tmp/libc.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/libc.so.0", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=431732, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,-1, 0) = 0x2aaae000
read(3, "\177ELF\1\2\1\0\0\0\0\0\0\0\0\0\0\3\0\10\0\0\0\1\0\0\252\200\0\0\0004"...,4096) = 4096
mmap(NULL, 471040, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0x2aaf9000
mmap(0x2aaf9000, 380336, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED,3, 0) = 0x2aaf9000
mmap(0x2ab65000, 8088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED,3, 0x5c000) = 0x2ab65000
mmap(0x2ab67000, 19376, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x2ab67000
close(3) = 0
munmap(0x2aaae000, 4096) = 0
open("/tmp/libc.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/libc.so.0", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=431732, ...}) = 0
close(3) = 0
stat("/lib/ld-uClibc.so.0", {st_mode=S_IFREG|0755,st_size=22604, ...}) = 0
mprotect(0x2ab65000, 4096, PROT_READ) = 0
mprotect(0x2aabc000, 4096, PROT_READ) = 0
ioctl(0, TIOCNXCL, {B115200 opost isig icanon echo ...}) = 0
ioctl(1, TIOCNXCL, {B115200 opost isig icanon echo ...}) = 0
write(1, "Hello world\n", 12Hello world
) = 12
exit(0) = ?
+++ exited with 0 +++
從結果可以看出,執行該程序調用了很多系統調用,並最終通過write系統調用打印出“Hello world”。
跟蹤一個正在運行的進程,使用-p選項加上進程的pid。
跟蹤某個特定的系統調用,使用-e選項加上系統調用名。
例如,跟蹤進程727的epoll_wait系統調用:strace -e epoll_wait -p 727
參考:
https://blog.csdn.net/jasonchen_gbd/article/details/44044539
https://blog.csdn.net/shandianling/article/details/17033299
http://godorz.info/2011/02/process-tracing-using-ptrace/(值得一看)
