天天看點

df 和 ls 指令執行夯主df 和 ls 指令執行夯主

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 -lf 有問題的挂載點           

然後等個1~2 分鐘看看效果,-f 是強制解除安裝的意思。

如果使用這個指令還是解決不了問題,那麼我們先确認有哪些程序在占用這個挂載點

fuser -cu 挂載點           

會得到程序 ID,和程序指令,看看此程序是否能正常殺掉,如果可以我們手工停止這個程序,然後進行解除安裝,或者直接使用下面指令進行解除安裝

fuser -ck 挂載點           

strace 工具實作原理

什麼是 strace

它是一個具有 Linux 傳統指令行界面的診斷、調試和指令性使用者空間的使用程式。它用于監控和篡改程序與 Linux 核心之間的互動,包括系統調用、信号傳遞和程序狀态的變化。strace 的所有操作都是通過核心的

ptrace

功能來實作的。項目首頁是:https://github.com/strace/strace;

關于 ptrace 相關知識可以閱讀:

main ptrace

Linux 沙箱之 ptrace

https://jin-yang.github.io/post/linux-ptrace-api-introduce.html

使用 strace

最簡單的 strace 指令的用法就是: strace PROG;PROG 就是要執行的程式(linux 指令)。strace 指令執行的結果就是按照調用順序列印出所有的系統調用,包括函數名、參數清單以及傳回值。

使用 strace 跟蹤一個程序的系統調用的基本流程如下圖:

df 和 ls 指令執行夯主df 和 ls 指令執行夯主

(此圖檔來源網絡)

從圖中可以看出strace做了以下幾件事情:

  1. 設定SIGCHLD 信号的處理函數,這個處理函數隻要不是SIG_IGN即可。由于子程序停止後是通過SIGCHLD信号通知父程序的,是以這裡要防止SIGCHLD信号被忽略。
  2. 建立子程序,在子程序中調用ptrace(PTRACE_TRACEME,0L, 0L, 0L)使其被父程序跟蹤,并通過execv函數執行被跟蹤的程式。
  3. 通過wait()等待子程序停止,并獲得子程序停止時的狀态status。
  4. 通過子程序的狀态檢視子程序是否已正常退出,如果是,則不再跟蹤,随後調用ptrace發送PTRACE_DETACH請求解除跟蹤關系。
  5. 子程序停止後,列印系統調用的函數名、參數和傳回值。具體流程見圖2。
  6. 通過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标志位來區分系統調用的開始和結束。

df 和 ls 指令執行夯主df 和 ls 指令執行夯主

例如編寫一個使用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

參考:

繼續閱讀