自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

揭開(kāi) Strace 命令捕獲系統(tǒng)調(diào)用的神秘面紗

系統(tǒng) 其他OS
strace 命令執(zhí)行的過(guò)程中,會(huì)讓目標(biāo)進(jìn)程執(zhí)行到系統(tǒng)調(diào)用時(shí)暫停運(yùn)行,從而導(dǎo)致比較頻繁的上下文切換,會(huì)增加目標(biāo)進(jìn)程 的運(yùn)行時(shí)間。所以,如果是在生產(chǎn)環(huán)境中,使用 strace 命令的時(shí)候還是要小心一點(diǎn)。更萬(wàn)萬(wàn)不可當(dāng)成一個(gè)長(zhǎng)期的線上監(jiān)控工具來(lái)使用。

在性能觀測(cè)領(lǐng)域,strace 命令是一個(gè)雖然很古老,但很常用的命令。使用它我們可以非常方便地觀察某個(gè)進(jìn)程正在執(zhí)行什么系統(tǒng)調(diào)用。

這個(gè)命令的使用方式也很簡(jiǎn)單,想觀察哪個(gè)進(jìn)程,直接將其 pid 作為參數(shù)傳給 strace 命令即可。

# strace -p {pid}
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@k\0\0\0\0\0\0"..., 832) = 832
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260A\2\0\0\0\0\0"..., 832) = 832
write(1, "anycast6   dev_snmp6\t if_inet6\ti"..., 137anycast6   dev_snmp6  if_inet6 ip6_mr_vif     ip_mr_vif        mcfilter   nf_conntrack        ptype  rt6_stats  sockstat      tcp6  unix
) = 137
......

然而我們都知道,正常來(lái)講操作系統(tǒng)中的各個(gè)進(jìn)程之間是互相隔離的。那么 strace 命令是如何做到能獲取其他進(jìn)程執(zhí)行的系統(tǒng)調(diào)用信息的呢,我們今天就來(lái)揭開(kāi)這個(gè)謎底。

一、手工實(shí)現(xiàn)一個(gè) strace

要想理解清楚 strace 命令原理,我想最有效的辦法是我們自己親手寫(xiě)一個(gè)簡(jiǎn)單程序來(lái)模擬 strace 的工作過(guò)程。

為了方便大家理解,我這里只把這個(gè)程序的核心邏輯列出來(lái)。完整的程序源碼請(qǐng)大家查看strace配套源碼 https://github.com/yanfeizhang/coder-kung-fu/blob/main/tests/cpu/test11/main.c

int main(int argc, char *argv[]) {

 // 1.attach 到 pid 指定的目標(biāo)進(jìn)程上
 ptrace(PTRACE_ATTACH, pid, NULL, NULL)

 while (1) {
  // 2.等待目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  // 2.1 指定要捕獲目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  ptrace(PTRACE_SYSCALL, pid, NULL, NULL)
  // 2.2 當(dāng)目標(biāo)進(jìn)程有 SYSCALL 發(fā)生時(shí)醒來(lái)處理
  waitpid(pid, &status, 0)

  // 3.讀取并解析系統(tǒng)調(diào)用
  // 3.1 讀取目標(biāo)進(jìn)程正在執(zhí)行的系統(tǒng)調(diào)用號(hào)
  syscall_number = ptrace(PTRACE_PEEKUSER, pid, 8 * ORIG_RAX, NULL);  、
  // 3.2 將系統(tǒng)調(diào)用號(hào)轉(zhuǎn)為系統(tǒng)調(diào)用名稱(chēng)
  switch (syscall_number) {
   case 5: syscall_name = "read"; break;
   case 6: syscall_name = "write"; break;
   case 10: syscall_name = "open"; break;
   case 11: syscall_name = "close"; break;
   ......
   default: syscall_name = "unknown"; break;
  }
  // 3.3 打印系統(tǒng)調(diào)用名稱(chēng)
  printf("Syscall: %s (number: %ld)\n", syscall_name, syscall_number);
 }
}

通過(guò)上面二十多行核心代碼,我們就實(shí)現(xiàn)了一個(gè)模擬 strace 命令跟蹤系統(tǒng)調(diào)用功能的簡(jiǎn)易程序。在這個(gè)程序中,主要是通過(guò)三塊邏輯來(lái)實(shí)現(xiàn):

第一,attach 到目標(biāo)進(jìn)程。在 C 標(biāo)準(zhǔn)庫(kù) 中有一個(gè) ptrace 函數(shù) , 該函數(shù)是一個(gè)系統(tǒng)調(diào)用方法。通過(guò)指定它的第一個(gè)參數(shù)為 PTRACE_ATTACH  pid,這樣就可以建立當(dāng)前程序和目標(biāo)進(jìn)程的跟蹤關(guān)系了。要注意的是,這一步必須得有 root 權(quán)限才可以。

第二,將自己注冊(cè)為目標(biāo)進(jìn)程的 syscall 調(diào)試器。這次還是使用 ptrace 函數(shù)。但第一個(gè)參數(shù)設(shè)為 PTRACE_SYSCALL,這樣就在告訴內(nèi)核要將自己注冊(cè)為目標(biāo)進(jìn)程的 syscall 調(diào)試器。每當(dāng)目標(biāo)進(jìn)程發(fā)生系統(tǒng)調(diào)用的時(shí)候,都會(huì)通知當(dāng)前程序。

第三,讀取目標(biāo)進(jìn)程系統(tǒng)調(diào)用名。這里涉及到一個(gè)基礎(chǔ)知識(shí),Linux 內(nèi)核在幫用戶進(jìn)程執(zhí)行系統(tǒng)調(diào)用的時(shí)候,會(huì)將系統(tǒng)調(diào)用號(hào)保存到 ORIG_RAX 寄存器中。

ptrace 函數(shù)第一個(gè)參數(shù)設(shè)為 PTRACE_PEEKUSER,這是在告訴內(nèi)核幫忙讀取目標(biāo)進(jìn)程的用戶區(qū)域的數(shù)據(jù)。第三個(gè)參數(shù)指定要讀取目標(biāo)進(jìn)程的 ORIG_RAX 寄存器中保存的系統(tǒng)調(diào)用號(hào)。在 /usr/include/x86_64-linux-gnu/asm/unistd_64.h 中可以查看到所有的系統(tǒng)調(diào)用號(hào)信息。將其轉(zhuǎn)為系統(tǒng)調(diào)用名后輸出即可。

整個(gè)程序是一個(gè)循環(huán),每當(dāng)目標(biāo)程序有系統(tǒng)調(diào)用發(fā)生的時(shí)候,都會(huì)通知到當(dāng)前程序。當(dāng)前程序再將其正在執(zhí)行的系統(tǒng)調(diào)用信息輸出出來(lái)。這樣就實(shí)現(xiàn)了對(duì)目標(biāo)進(jìn)程實(shí)時(shí)行為的動(dòng)態(tài)跟蹤。

圖片

接下來(lái)我們分三個(gè)部分,從內(nèi)核視角深入地探究一下底層工作原理。

二、attach 到目標(biāo)進(jìn)程

要想實(shí)現(xiàn)對(duì)目標(biāo)程序的跟蹤,首先第一步準(zhǔn)備工作便是調(diào)用 ptrace 把自己 attach 到目標(biāo)進(jìn)程上。

int main(int argc, char *argv[]) {
 // 1.attach 到 pid 指定的目標(biāo)進(jìn)程上
 ptrace(PTRACE_ATTACH, pid, NULL, NULL)
 ...
}

圖片

我們來(lái)看下這個(gè)所謂的 attach ,在 Linux 內(nèi)部究竟是干了點(diǎn)啥。找來(lái) ptrace 系統(tǒng)調(diào)用的源碼。

//file:kernel/ptrace.c
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, ...)
{ 
 // 1. 根據(jù) pid 查找目標(biāo)進(jìn)程內(nèi)核對(duì)象
 struct task_struct *child;
 child = find_get_task_by_vpid(pid);
 ......

 // 2. 執(zhí)行 ptrace_attach
 if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
  ret = ptrace_attach(child, request, addr, data);
  ...
 }
 ......
}

在 ptrace 系統(tǒng)調(diào)用源碼中,第一步比較簡(jiǎn)單,根據(jù)參數(shù)中的 pid 查找目標(biāo)進(jìn)程在內(nèi)核中的 task_struct 內(nèi)核對(duì)象。第二步操作中的 ptrace_attach,是 attach 到目標(biāo)進(jìn)程的核心函數(shù)。

//file:kernel/ptrace.c
static int ptrace_attach(struct task_struct *task, long request, ...)
{
 ...
 // 1.權(quán)限檢查
 if (unlikely(task->flags & PF_KTHREAD))
  goto out;
 ...
 // 2.狀態(tài)設(shè)置
 ptrace_link(task, current);
 ...
}

在 ptrace_attach 中先要進(jìn)行一些權(quán)限檢查,例如內(nèi)核線程是不允許被 attach 的。接著調(diào)用 ptrace_link 來(lái)修改當(dāng)前進(jìn)程,以及要跟蹤的目標(biāo)進(jìn)程的內(nèi)核對(duì)象相關(guān)字段。ptrace_link 的主要實(shí)現(xiàn)是 __ptrace_link。

//file:kernel/ptrace.c
void __ptrace_link(struct task_struct *child, struct task_struct *new_parent, ...)
{
 list_add(&child->ptrace_entry, &new_parent->ptraced);
 child->parent = new_parent;
 ...
}

在 ptrace_link 函數(shù)中,先是通過(guò) list_add 函數(shù),將目標(biāo)進(jìn)程(child)的 ptrace_entry 被插入到當(dāng)前進(jìn)程(new_parent)的 ptraced 鏈表的頭部。這樣當(dāng)前進(jìn)程(new_parent) 就可以通過(guò) ptraced 鏈表來(lái)跟蹤和管理所有在跟蹤的進(jìn)程。

接著調(diào)用 child->parent = new_parent 把當(dāng)前進(jìn)程設(shè)置成了目標(biāo)進(jìn)程(child)的 parent。目的是使當(dāng)前進(jìn)程能夠通過(guò)調(diào)用 waitpid 獲取到目標(biāo)進(jìn)程的 SIGTRAP 信號(hào)。

這樣就完成了到目標(biāo)進(jìn)程的 attach,當(dāng)前進(jìn)程通過(guò) ptraced 來(lái)管理目標(biāo)進(jìn)程,目標(biāo)進(jìn)程也可以發(fā)出 SIGTRAP 信號(hào)來(lái)和當(dāng)前進(jìn)程進(jìn)行消息傳遞。

三、捕獲目標(biāo)進(jìn)程 SYSCALL

3.1 設(shè)置等待目標(biāo)進(jìn)程 SYSCALL

在完成當(dāng)前進(jìn)程和目標(biāo)進(jìn)程的 attach 關(guān)聯(lián)后。接著下一步操作是告訴 Linux 內(nèi)核,要等待和捕獲目標(biāo)進(jìn)程的系統(tǒng)調(diào)用。

int main(int argc, char *argv[]) {

 // 1.attach 到 pid 指定的目標(biāo)進(jìn)程上
 ...

 while (1) {
  // 2.等待目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  // 2.1 指定要捕獲目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  ptrace(PTRACE_SYSCALL, pid, NULL, NULL)
  // 2.2 當(dāng)目標(biāo)進(jìn)程有 SYSCALL 發(fā)生時(shí)醒來(lái)處理
  waitpid(pid, &status, 0)
  ...
 }
}

圖片

在 ptrace 系統(tǒng)調(diào)用中,由于這次傳入的第一個(gè)參數(shù)是 PTRACE_SYSCALL。所以其執(zhí)行的核心函數(shù)提煉后如下所示:

//file:kernel/ptrace.c
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
  unsigned long, data)
{ 
 struct task_struct *child;
 child = find_get_task_by_vpid(pid);
 ......
 ret = arch_ptrace(child, request, addr, data)
 ......
}

首先還是先根據(jù) pid 獲取目標(biāo)進(jìn)程的 task_struct 內(nèi)核對(duì)象。接著執(zhí)行 arch_ptrace 來(lái)為目標(biāo)進(jìn)程內(nèi)核對(duì)象添加一個(gè)標(biāo)記 SYSCALL_TRACE。具體設(shè)置是在 ptrace_resume 函數(shù)中執(zhí)行的( arch_ptrace -> ptrace_request -> ptrace_resume )。我們直接來(lái)看 ptrace_resume 源碼。

//file:kernel/ptrace.c
static int ptrace_resume(struct task_struct *child, long request,
    unsigned long data)
{
 if (request == PTRACE_SYSCALL)
  set_task_syscall_work(child, SYSCALL_TRACE);
 ...
}

set_task_syscall_work 函數(shù)就是在給目標(biāo)進(jìn)程的設(shè)置了一個(gè) SYSCALL_TRACE 標(biāo)記位。

//file:include/linux/thread_info.h
#define set_task_syscall_work(t, fl) \
 set_bit(SYSCALL_WORK_BIT_##fl, &task_thread_info(t)->syscall_work)

這樣后面當(dāng)該進(jìn)程再執(zhí)行系統(tǒng)調(diào)用的時(shí)候,通過(guò)判斷該標(biāo)記就能發(fā)現(xiàn)有進(jìn)程在跟蹤它了。

3.2 等待目標(biāo)進(jìn)程信號(hào)發(fā)生

當(dāng)前進(jìn)程在設(shè)置完要對(duì)目標(biāo)進(jìn)程的 SYSCALL 進(jìn)行觀察后,接著就調(diào)用 waitpid 進(jìn)入了睡眠狀態(tài)。

int main(int argc, char *argv[]) {

 // 1.attach 到 pid 指定的目標(biāo)進(jìn)程上
 ...
 
 while (1) {
  // 2.等待目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  // 2.1 指定要捕獲目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  ptrace(PTRACE_SYSCALL, pid, NULL, NULL)
  // 2.2 當(dāng)目標(biāo)進(jìn)程有 SYSCALL 發(fā)生時(shí)醒來(lái)處理
  waitpid(pid, &status, 0)
  ...
 }
}

waitpid 也是一個(gè)系統(tǒng)調(diào)用。

//file:kernel/exit.c
SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options)
{
 return kernel_wait4(pid, stat_addr, options, NULL);
}

在這個(gè)系統(tǒng)調(diào)用中,依次調(diào)用 kernel_wait4、do_wait 等內(nèi)核函數(shù),最后在 add_wait_queue 函數(shù)中,將當(dāng)前進(jìn)程加入到等待隊(duì)列中,更新進(jìn)程狀態(tài)為 TASK_INTERRUPTIBLE(可中斷睡眠狀態(tài)),等待子進(jìn)程信號(hào)。

// file:kernel/exit.c
static long do_wait(struct wait_opts *wo)
{
 ...
 init_waitqueue_func_entry(&wo->child_wait, child_wait_callback);
 wo->child_wait.private = current;
 add_wait_queue(¤t->signal->wait_chldexit, &wo->child_wait);
...
}

當(dāng)子進(jìn)程退出時(shí),內(nèi)核會(huì)向父進(jìn)程發(fā)送一個(gè)信號(hào),父進(jìn)程的信號(hào)處理程序會(huì)喚醒等待隊(duì)列中的進(jìn)程,使它們重新進(jìn)入可運(yùn)行狀態(tài),等待被調(diào)度器調(diào)度執(zhí)行。

四、等待并讀取目標(biāo)進(jìn)程系統(tǒng)調(diào)用

圖片

4.1 目標(biāo)進(jìn)程系統(tǒng)調(diào)用發(fā)生

當(dāng)目標(biāo)進(jìn)程系統(tǒng)調(diào)用發(fā)生的時(shí)候,會(huì)檢查是否有被設(shè)置 SYSCALL_TRACE  標(biāo)記位。如果有,那就說(shuō)明有進(jìn)程正在跟蹤它。具體的檢測(cè)是在 syscall_trace_enter 內(nèi)核函數(shù)中做的

//file:arch/m68k/kernel/entry.S
ENTRY(system_call)
 ...
 jbsr syscall_trace_enter
//file:arch/m68k/kernel/ptrace.c
asmlinkage int syscall_trace_enter(void)
{
 int ret = 0;

 if (test_thread_flag(TIF_SYSCALL_TRACE))
  ret = ptrace_report_syscall_entry(task_pt_regs(current));
 return ret;
}

如果有 SYSCALL_TRACE 標(biāo)志,那就會(huì)設(shè)置一下退出碼,發(fā)出 SIGTRAP 信號(hào),喚醒正在追蹤它的進(jìn)程,最后暫停當(dāng)前程序的運(yùn)行。具體的內(nèi)核函數(shù)調(diào)用過(guò)程是經(jīng)過(guò) ptrace_report_syscall_entry -> ptrace_report_syscall -> ptrace_notify -> ptrace_do_notify 這么一條長(zhǎng)的調(diào)用鏈后,最終在 ptrace_stop 內(nèi)核函數(shù)中執(zhí)行的。我們直接來(lái)看這個(gè)最關(guān)鍵的 ptrace_stop 函數(shù)。

//file:kernel/signal.c
static int ptrace_stop(int exit_code, int why, unsigned long message,
         kernel_siginfo_t *info)
 __releases(¤t->sighand->siglock)
 __acquires(¤t->sighand->siglock)
{
 ......
 // 1.
 set_special_state(TASK_TRACED);


 // 2.設(shè)置當(dāng)前進(jìn)程的 exit_code
 current->ptrace_message = message;
 current->last_siginfo = info;
 current->exit_code = exit_code;


 // 3. 
 if (current->ptrace)
  do_notify_parent_cldstop(current, true, why);
 if (gstop_done && (!current->ptrace || ptrace_reparented(current)))
  do_notify_parent_cldstop(current, false, why);

 cgroup_enter_frozen();
 schedule();
 ......

}

在這個(gè)函數(shù)中做了這么幾件事情。

第一,調(diào)用 set_special_state(TASK_TRACED) 將當(dāng)前進(jìn)程(被跟蹤進(jìn)程)的狀態(tài)設(shè)置為 TASK_TRACED,表示進(jìn)程已被 ptrace 停止。該狀態(tài)意味著進(jìn)程將不會(huì)在下次調(diào)度時(shí)被調(diào)度執(zhí)行,因?yàn)樗F(xiàn)在處于被跟蹤狀態(tài)。

第二,設(shè)置自己的退出碼,到struct task_struct 的成員 exit_code 上。

第三,調(diào)用 do_notify_parent_cldstop()-->__wake_up_parent()喚醒跟蹤進(jìn)程(strace)

第四,調(diào)用 schedule 掛起自己讓出 CPU。

4.2 跟蹤進(jìn)程 waitpid 返回

接下來(lái)跟蹤進(jìn)程收到信號(hào),會(huì)被內(nèi)核喚醒,并中 waitpid 函數(shù)中返回。

//file:kernel/exit.c
SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options)
{
 return kernel_wait4(pid, stat_addr, options, NULL);
}

long kernel_wait4(pid_t upid, int __user *stat_addr, int options,
    struct rusage *ru)
{
 ...
 ret = do_wait(&wo);
 put_pid(pid);

 //將進(jìn)程狀態(tài)保存到用戶傳入的地址中
 if (ret > 0 && stat_addr && put_user(wo.wo_stat, stat_addr))
  ret = -EFAULT;
 return ret;
}

這時(shí),跟蹤進(jìn)程就知道目標(biāo)進(jìn)程有系統(tǒng)調(diào)用發(fā)生了。下一步就可以讀取目標(biāo)進(jìn)程正在執(zhí)行的系統(tǒng)調(diào)用信息了。

4.3 讀取目標(biāo)進(jìn)程系統(tǒng)調(diào)用號(hào)

在 Linux 內(nèi)核中,ORIG_RAX 寄存器用于保存進(jìn)程在執(zhí)行系統(tǒng)調(diào)用是的調(diào)用號(hào)。跟蹤進(jìn)程只需要訪問(wèn)下目標(biāo)進(jìn)程的 ORIG_RAX 寄存器就可以知道目標(biāo)進(jìn)程正在執(zhí)行哪個(gè)系統(tǒng)調(diào)用了。

圖片圖片

具體執(zhí)行方式是調(diào)用 ptrace 系統(tǒng)調(diào)用。第一個(gè)參數(shù)設(shè)置為 PTRACE_PEEKUSER 表示要讀取目標(biāo)進(jìn)程的一些數(shù)據(jù)。第三個(gè)參數(shù)指定為 8*ORIG_RAX 表示要讀取 ORIG_RAX 寄存器。8*ORIG_RAX 計(jì)算出 ORIG_RAX 在用戶空間的偏移地址。

int main(int argc, char *argv[]) {

 // 1.attach 到 pid 指定的目標(biāo)進(jìn)程上
 ...

 while (1) {
  // 2.等待目標(biāo)進(jìn)程的 PTRACE_SYSCALL
  ...

  // 3.讀取并解析系統(tǒng)調(diào)用
  // 3.1 讀取目標(biāo)進(jìn)程正在執(zhí)行的系統(tǒng)調(diào)用號(hào)
  syscall_number = ptrace(PTRACE_PEEKUSER, pid, 8 * ORIG_RAX, NULL);  、
  // 3.2 將系統(tǒng)調(diào)用號(hào)轉(zhuǎn)為系統(tǒng)調(diào)用名稱(chēng)
  switch (syscall_number) {
   case 5: syscall_name = "read"; break;
   case 6: syscall_name = "write"; break;
   case 10: syscall_name = "open"; break;
   case 11: syscall_name = "close"; break;
   ......
   default: syscall_name = "unknown"; break;
  }
  // 3.3 打印系統(tǒng)調(diào)用名稱(chēng)
  printf("Syscall: %s (number: %ld)\n", syscall_name, syscall_number);
 }
}

內(nèi)核執(zhí)行 ptrace 系統(tǒng)調(diào)用的時(shí)候,會(huì)執(zhí)行到 arch_ptrace 函數(shù)。

//file:arch/x86/kernel/ptrace.c
long arch_ptrace(struct task_struct *child, long request,
   unsigned long addr, unsigned long data)
{
 unsigned long __user *datap = (unsigned long __user *)data;
 ...
 switch (request) {
  ...
  case PTRACE_PEEKUSR: {
   tmp = 0;  /* Default return condition */
   if (addr < sizeof(struct user_regs_struct))
    tmp = getreg(child, addr);
   else if (addr >= offsetof(struct user, u_debugreg[0]) &&
    addr <= offsetof(struct user, u_debugreg[7])) {
    addr -= offsetof(struct user, u_debugreg[0]);
    tmp = ptrace_get_debugreg(child, addr / sizeof(data));
   }
   ret = put_user(tmp, datap);
   break;
  }
 }
}

在 arch_ptrace 判斷是 PTRACE_PEEKUSER 參數(shù),會(huì)在計(jì)算一下目標(biāo)進(jìn)程數(shù)據(jù)地址 addr,然后將其讀取出來(lái)設(shè)置到跟蹤進(jìn)程的用戶空間中。這樣,就讀取到系統(tǒng)調(diào)用號(hào)了。

接下來(lái)在 /usr/include/x86_64-linux-gnu/asm/unistd_64.h 中可以查看到所有的系統(tǒng)調(diào)用號(hào)信息。將其轉(zhuǎn)為系統(tǒng)調(diào)用名后輸出即可。

五、總結(jié)

strace 命令跟蹤其它進(jìn)程的系統(tǒng)調(diào)用的整個(gè)過(guò)程可以同下面一張圖來(lái)總結(jié)。

圖片圖片

首先是 strace 進(jìn)程執(zhí)行下面三步操作:

  • 調(diào)用 ptrace(ATTACH, ...) 設(shè)置關(guān)聯(lián)跟蹤進(jìn)程和目標(biāo)進(jìn)程
  • 再調(diào)用 ptrace(SYSCALL, ...) 設(shè)置要要跟蹤目標(biāo)進(jìn)程的系統(tǒng)調(diào)用
  • 接著就調(diào)用 waitpid 去等待子進(jìn)程的信號(hào)了,而先暫停執(zhí)行了

再接下來(lái)當(dāng)目標(biāo)進(jìn)程有系統(tǒng)調(diào)用發(fā)生時(shí),

  • 檢查當(dāng)前進(jìn)程是否被設(shè)置了 SYSCALL_TRACE 標(biāo)記
  • 如果有,那么設(shè)置一下當(dāng)前進(jìn)程的狀態(tài),也暫停執(zhí)行了
  • 通過(guò)信號(hào)機(jī)制喚醒跟蹤進(jìn)程

跟蹤進(jìn)程收到信號(hào)后會(huì)繼續(xù)執(zhí)行:

  • 讀取目標(biāo)進(jìn)程 ORIG_RAX 寄存器,其中保存著目標(biāo)進(jìn)程的系統(tǒng)調(diào)用號(hào)
  • 將系統(tǒng)調(diào)用號(hào)轉(zhuǎn)換成系統(tǒng)調(diào)用名輸出

再接下來(lái)再調(diào)用 wait_pid,讓目標(biāo)進(jìn)程繼續(xù)運(yùn)行。整體進(jìn)入一個(gè)不斷獲取,不斷打印的循環(huán)中。

從以上的執(zhí)行過(guò)程可以看出。strace 命令執(zhí)行的過(guò)程中,會(huì)讓目標(biāo)進(jìn)程執(zhí)行到系統(tǒng)調(diào)用時(shí)暫停運(yùn)行,從而導(dǎo)致比較頻繁的上下文切換,會(huì)增加目標(biāo)進(jìn)程 的運(yùn)行時(shí)間。所以,如果是在生產(chǎn)環(huán)境中,使用 strace 命令的時(shí)候還是要小心一點(diǎn)。更萬(wàn)萬(wàn)不可當(dāng)成一個(gè)長(zhǎng)期的線上監(jiān)控工具來(lái)使用。

不過(guò),strace 命令還是非常簡(jiǎn)單好用的,你說(shuō)呢?。?/p>

責(zé)任編輯:武曉燕 來(lái)源: 開(kāi)發(fā)內(nèi)功修煉
相關(guān)推薦

2021-05-25 09:01:21

Linux命令Bash histor

2015-08-20 13:43:17

NFV網(wǎng)絡(luò)功能虛擬化

2010-05-17 09:13:35

2014-03-12 11:11:39

Storage vMo虛擬機(jī)

2021-06-07 08:18:12

云計(jì)算云端阿里云

2010-05-26 19:12:41

SVN沖突

2009-09-15 15:34:33

Google Fast

2023-11-02 09:55:40

2016-04-06 09:27:10

runtime解密學(xué)習(xí)

2011-07-10 14:28:49

JAVAIO

2009-06-01 09:04:44

Google WaveWeb

2018-03-01 09:33:05

軟件定義存儲(chǔ)

2020-09-27 08:02:47

操作系統(tǒng)

2010-06-17 10:53:25

桌面虛擬化

2011-08-02 08:59:53

2017-10-16 05:56:00

2021-07-28 21:49:01

JVM對(duì)象內(nèi)存

2021-08-11 09:01:48

智能指針Box

2021-09-17 15:54:41

深度學(xué)習(xí)機(jī)器學(xué)習(xí)人工智能

2020-04-14 10:44:01

區(qū)塊鏈滲透測(cè)試比特幣
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)