国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Linux > 正文

一篇學(xué)會Linux ptrace 的實(shí)踐

2024-08-27 23:31:42
字體:
供稿:網(wǎng)友
ptrace 是 Linux 內(nèi)核提供的非常強(qiáng)大的系統(tǒng)調(diào)用,通過 ptrace 可以實(shí)現(xiàn)進(jìn)程的單步調(diào)試和收集系統(tǒng)調(diào)用情況。比如 strace 和 gdb 都是基于 ptrace 實(shí)現(xiàn)的,strace 可以顯示進(jìn)程調(diào)用了哪些系統(tǒng)調(diào)用,gdb 可以實(shí)現(xiàn)對進(jìn)程的調(diào)試。本文介紹這些工具的底層 ptrace 是如何實(shí)現(xiàn)的。這里選用了 1.2.13 的早期版本,原理是類似的,新版內(nèi)核代碼過多,沒必要陷入過多細(xì)節(jié)中。
 
1 進(jìn)程調(diào)試
ptrace 系統(tǒng)調(diào)用的實(shí)現(xiàn)中包含了很多功能,首先來看一下單步調(diào)試的實(shí)現(xiàn)。通過 ptrace 實(shí)現(xiàn)單步調(diào)試的方式有兩種。
 
1. 父進(jìn)程執(zhí)行 fork 創(chuàng)建一個子進(jìn)程,通過 ptrace 設(shè)置子進(jìn)程為 PF_PTRACED 標(biāo)記,然后執(zhí)行 execve 加載被調(diào)試的程序。
 
2. 通過 ptrace attach 到指定的 pid 完成對進(jìn)程的調(diào)試(控制)。
 
首先看一下第一種的實(shí)現(xiàn)。
 
1.1 方式1
pid_t pid = fork();// 子進(jìn)程if (pid == 0) {
    ptrace(PTRACE_TRACEME,0,NULL,NULL);
    // 加載被調(diào)試的程序
    execve(argv[1], NULL, NULL);
}
執(zhí)行 fork 創(chuàng)建子進(jìn)程后,通過 ptrace 的 PTRACE_TRACEME 指示操作系統(tǒng)設(shè)置子進(jìn)程為被調(diào)試(設(shè)置 PF_PTRACED 標(biāo)記)。來看一下這一步操作系統(tǒng)做了什么事情。
 
asmlinkage int sys_ptrace(long request, long pid, long addr, long data){
    if (request == PTRACE_TRACEME) {
        current->flags |= PF_PTRACED;
        return 0;
    }
}
這一步非常簡單,接著看 execve 加載程序到內(nèi)存執(zhí)行時又是如何處理的。
 
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs) {
    // 加載程序
    for (fmt = formats ; fmt ; fmt = fmt->next) {
        int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
        retval = fn(&bprm, regs);
    }
}
do_execve 邏輯非常復(fù)雜,不過我們只關(guān)注需要的就好。do_execve 通過鉤子函數(shù)加載程序,我們看看 formats 是什么。
 
struct linux_binfmt {
    struct linux_binfmt * next;
    int *use_count;
    int (*load_binary)(struct linux_binprm *, struct  pt_regs * regs);
    int (*load_shlib)(int fd);
    int (*core_dump)(long signr, struct pt_regs * regs);
};
 
static struct linux_binfmt *formats = &aout_format;int register_binfmt(struct linux_binfmt * fmt){
    struct linux_binfmt ** tmp = &formats;
 
    if (!fmt)
        return -EINVAL;
    if (fmt->next)
        return -EBUSY;
    while (*tmp) {
        if (fmt == *tmp)
            return -EBUSY;
        tmp = &(*tmp)->next;
    }
    *tmp = fmt;
    return 0;    
}
可以看到 formats 是一個鏈表??梢酝ㄟ^ register_binfmt 函數(shù)注冊節(jié)點(diǎn)。那么誰調(diào)用了這個函數(shù)呢?
 
struct linux_binfmt elf_format = {
 
NULL, NULL, load_elf_binary, load_elf_library, NULL};int init_module(void) {
 
register_binfmt(&elf_format);
 
return 0;
 
}
所以最終調(diào)用了 load_elf_binary 函數(shù)加載程序。同樣我們只關(guān)注相關(guān)的邏輯。
 
if (current->flags & PF_PTRACED)
        send_sig(SIGTRAP, current, 0);
load_elf_binary 中會判斷如果進(jìn)程設(shè)置了 PF_PTRACED 標(biāo)記,那么會給當(dāng)前進(jìn)程發(fā)送一個 SIGTRAP 信號。接著看信號處理函數(shù)的相關(guān)邏輯。
 
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
    current->exit_code = signr;
    // 修改當(dāng)前進(jìn)程(被調(diào)試的進(jìn)程)為暫停狀態(tài)
    current->state = TASK_STOPPED;
    // 通知父進(jìn)程
    notify_parent(current);
    // 調(diào)度其他進(jìn)程執(zhí)行
    schedule();
}
所以程序被加載到內(nèi)存后,根本沒有機(jī)會執(zhí)行就直接被修改為暫停狀態(tài)了,接下來看看 notify_parent 通知父進(jìn)程干什么。
 
void notify_parent(struct task_struct * tsk){    
    // 給父進(jìn)程發(fā)送 SIGCHLD 信號
    if (tsk->p_pptr == task[1])
        tsk->exit_signal = SIGCHLD;
    send_sig(tsk->exit_signal, tsk->p_pptr, 1);
    wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
}
父進(jìn)程收到信號后,可以通過 sys_ptrace 控制子進(jìn)程,sys_ptrace 還提供了很多功能,比如讀取子進(jìn)程的數(shù)據(jù)。

(編輯:武林網(wǎng))

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 湖南省| 来安县| 浪卡子县| 大兴区| 嘉善县| 赤峰市| 离岛区| 宜城市| 宾川县| 无极县| 南城县| 德昌县| 习水县| 双辽市| 宁武县| 颍上县| 广昌县| 拉萨市| 克山县| 曲靖市| 湟源县| 客服| 临夏县| 屏南县| 江永县| 松潘县| 富顺县| 光山县| 新蔡县| 永顺县| 宜兴市| 甘南县| 定日县| 平湖市| 中宁县| 五大连池市| 田阳县| 宾阳县| 凯里市| 宁明县| 松阳县|