转载-玩转 utrace

玩转 utrace

Linux 新的调试接口 utrace 简介

刘 明
2010 年 5 月 27 日发布

WeiboGoogle+用电子邮件发送本页面

Comments

 

6

Utrace 简介

Utrace 是 Linux 内核中最新的 trace 和 debug 基础构架。正如其名称所暗示,utrace 为运行在用户态的进程提供 trace 和 debug 支持。内核本身的 trace/debug 需求由一系列混杂不清,各自为政,却依然能共同工作的技术所支持,包括 tracepoint, ftrace, kprobe 等等。

而 Linux 对用户进程的 trace debug 支持则简单明了,在 utrace 之前 ptrace 是历史悠久的 trace/debug 接口。今后,或迟或早,应该就是 utrace 了。

然而 utrace 并不是 ptrace 完全的替代物 ( 像 CFS 替代 O(1) 调度器那样 )。utrace 是比 ptrace 更加底层的技术,ptrace 还将继续存在,他们之间的关系如下图所示。

图 1. utrace 的体系结构

图 1. utrace 的体系结构

Utrace 运行在 ptrace 下层,ptrace 通过请求 utrace 来完成用户所要求的行为。那些原先依赖于 ptrace 的应用程序,比如 gdb, 不需要做任何改变。

相对于原有的实现,采用 utrace 重写的 ptrace 结构更加合理,容易维护和扩展;

不过,utrace 的出现并不仅仅是为了重新实现 ptrace, 它提供了比 ptrace 更强大的基础功能,为未来更智能的 debugger 做好了准备。

简单说来,utrace 主要有三种功能:

Thread Event Reporting:

跟踪目标进程的各种事件,当这些事件发生时,utrace 调用对应的 callback 函数进行处理。这些事件比如:接收到信号,系统调用,进程退出等。

Core thread control:

Debugger 最基本的要求是能够控制被调试程序,比如让目标进程暂停或者单步执行。Utrace 提供这种进程控制能力。

Thread machine state access:

很多调试器都需要修改目标进程的地址空间,比如设置断点时,debugger 需要将目标进程的目标代码替换为 trap 指令。因此作为 trace/debug 基础构架,utrace 也必须提供对目标进程地址空间的访问能力。

有了这三种能力,便可以完成很多跟踪和调试的任务。

发明 utrace 的动机

凡事都有因有果,有些事情人们不必去深究其缘由,但 utrace 这件事,却很值得去研究其出现的动机和意义。

Ptrace 的问题

在 Linux 中,调试应用程序的传统接口是 ptrace。Gdb, strace 等 debugger 都是 ptrace 的具体应用。但 ptrace 有很多问题。Roland McGraph 是 Linux 社区中 ptrace 的维护者,他在长期的维护工作中总结出了很多 ptrace 的问题,也正是他开发了 utrace, 可以称他为 utrace 之父。

下面列举 Roland 所发现的问题。

从用户角度来说:

  • Changes behavior of traced processes
    • Attach/detach interrupts system calls

使用 ptrace attach 目标进程时,内核将发送 SIGSTOP 将目标进程挂起,这一动作改变了目标进程的行为。好的 trace 系统应该将对目标进程的影响降到最低。

  • Overloads signals

应用程序通过 ptrace 控制目标进程时采用了复杂的信号机制。目标进程需要通知控制进程时,须发送 SIGCHLD 信号。因此在 do_signal 中,Linux 必须重载信号机制。当目标进程收到信号需要处理时,Linux 也需要先发送 SIGCHLD。这样就改变了目标进程的行为。在某些情况下会造成困难和问题。

  • Low throughput, high latency

Ptrace 是系统调用,为了响应目标进程的事件,控制进程需要多次在内核态和用户态之间切换。从而严重影响性能。比如,目标进程调用 system call 进入内核,假如目标进程被 ptrace 所 attach, 它就会发送信号给控制进程,系统从内核态进入用户态通知控制进程。进程再次调用 ptrace 进入内核态。这种来回切换注定了 ptrace 应用程序的性能不会很好。

  • One tracer

在同一个时刻,一个目标进程只能被一个 tracer 所控制,换句话说,一个进程只能被 ptrace attach 一次。

  • All-or-nothing security model
  • No fun to program

Ptrace 的接口非常奇特,不容易理解和掌握。Ptrace 采用 SIGCHLD 的编程模型,即目标进程发送 SIGCHLD, 调试器调用 waitpid 等待信号。这种模型不易修改和维护。

从可维护性方面看,ptrace 存在以下缺陷。

  • fragile kernel internals, poorly documented

ptrace 在内核中没有对应的文档,并且 ptrace 本身并不是 POSIX 标准,维护人员的工作比较繁重,而且没有乐趣。

  • task parent link, reparenting on exit/detach

ptrace 会改变进程的父子关系。

  • poor separation of arch from generic

ptrace 在内核中没有很好的采用分层设计。很多代码是直接依赖 arch 中的具体实现,每个不同的 arch 都有不同的实现和特性。

  • cut'n'paste maintenance

ptrace 框架中有许多重复代码,有大量值得重构的代码。

从目前的开发状态来看,utrace 已经克服了以上所有这些 ptrace 的缺点。

Dtrace Envy

Linux 社区中会提到一个名词叫作 Dtrace Envy。Dtrace 是一种让 SUN 工程师具有优越感的技术。Linux 开发者在 Dtrace 面前总会表露些羡慕之意。

于是 Linux 社区出现了 systemTap, systemTap 利用了内核的 Kprobe 机制,完成了 Dtrace 类似的功能,IBM Developerworks 网站上曾有 文章介绍了 systemTap, 非常值得一读。要和 Dtrace 相提并论,systemTap 还必须要有能力调试用户态进程,否则只能说达到了 Dtrace 部分的功能。目前 systemTap 正是依赖 utrace 来调试用户态进程的。

Ptrace 的落后,赶超 Dtrace 的强烈渴望,我想这足以说明 utrace 出现的意义了。

Utrace 的使用简介

这一节介绍 utrace 的编程方法和概念。

utrace 编程的基础概念

如果 trace 是一篇文章,那么一定是形如“几点几分发生了什么事情”这样的一个流水帐。记录这个流水账最简便的方法就是当事情发生时,产生一个通知,当我们接到通知时便立即将事件记录下来。Utrace 的编程思想就是这样的,您告诉 utrace 当哪些事件发生时需要产生通知,然后告诉 utrace 这些事件发生时应该调用哪些 callback 函数来处理。

调试目标进程的第一件事情是 attach 它。您可以通过 utrace_attach_task()或 utrace_attach_pid()完成 attach。Attach 成功将返回一个 trace engine 数据结构,从此以后程序便通过 trace engine 对目标进程进行控制。

清单 1. trace engine 数据结构

1

2

3

4

5

struct utrace_engine {

    const struct utrace_engine_ops *ops;

    void *data;

    unsigned long flags;

};

使用 utrace 的第二步是告诉 utrace 您对哪些事件感兴趣。比如目标进程执行了 exec, 或者收到了一个信号等等。Table 1 列出了 utrace 支持的所有事件。函数 utrace_set_events_pid 用来通知 utrace 您所感兴趣的事件。

表 1. utrace 事件

UTRACE_EVENT_QUIESCEThread is available for examination
UTRACE_EVENT_REAPZombie reaped, no more tracing possible
UTRACE_EVENT_CLONESuccessful clone/fork/vfork just done
UTRACE_EVENT_EXECSuccessful execve just completed
UTRACE_EVENT_EXITThread exit in progress
UTRACE_EVENT_DEATHThread has died
UTRACE_EVENT_SYSCALL_ENTRYUser entered kernel for system call
UTRACE_EVENT_SYSCALL_EXITReturning to user after system call
UTRACE_EVENT_SIGNALSignal delivery will run a user handler
UTRACE_EVENT_SIGNAL_IGNNo-op signal to be delivered
UTRACE_EVENT_SIGNAL_STOPSignal delivery will suspend
UTRACE_EVENT_SIGNAL_TERMSignal delivery will terminate
UTRACE_EVENT_SIGNAL_CORESignal delivery will dump core
UTRACE_EVENT_JCTLJob control stop or continue completed

接下来,您必须告诉 utrace, 当这些事件发生时您想做什么。这是通过注册 callback 函数实现的。在调用 utrace_attach_task()时您可以传入一个 utrace_engine_ops 数据结构完成 callback 的注册。

至此,基本的准备工作就都完成了。当目标进程发生事件时,callback 便被调用。您现在可以在 callback 中做任何想做的事情。

如果我们只需静静地观察目标进程,那么上面的基本工作便足够了。但有时候我们还想干涉目标进程的执行。比如 debugger 需要控制进程,实现诸如单步执行,或者暂停目标进程等功能。这可以通过 utrace_control 完成。

Table 2 列出了您能够通过 utrace_control 完成的控制动作。

表 2. utrace control 动作

ACTION描述
UTRACE_STOPStay quiescent after callbacks
UTRACE_INTERRUPTMake report_signal() callback soon
UTRACE_REPORTMake some callback soon
UTRACE_SINGLESTEPResume in user mode for one instruction
UTRACE_BLOCKSTEPResume in user mode until next branch
UTRACE_RESUMEResume normally in user mode
UTRACE_DETACHDetach my engine

这些 action 不仅可以作为 utrace_control 的入口参数来控制目标进程,也可以出现在每个 callback 函数的返回值中。这样当 callback 返回时,utrace 便执行相应的动作。比如暂停或者单步执行等。

在 Table 2 中值得一提的 action 是 UTRACE_INTERRUPT。有些情况下,目标进程并没有发生任何我们所感兴趣的事件,但我们想做些事情,比如发送一个 fake 信号给进程,或者将目标进程暂停下来。这种时候,可以通过调用 utrace_control(UTRACE_INTERRUPT) 来打断目标进程,其结果是 signal callback 被立即触发。这时候,您可以做一些想要做的工作,一个例子是本文最后给出的 code inject 例子程序,利用能够 UTRACE_REPORT 在 module 初始化时主动触发一个 fake 的信号,从而使得 signal callback 被调用。

Utrace 的代码实例演示

对于程序员来说,说再多的也不如写几个例子程序更来劲。在这一节中,我将用 utrace 开发一些小程序,希望能使您对 utrace 有更直观的了解。

学习 ptrace 的人或许都读过 Pradeep Padala 写的 <playing with ptrace>, 如果没有那么我推荐您去读一下。在该文中,Pradeep 写了几个小程序来一步步地演示 ptrace 的使用,效果非常好 。

下面我也将开发一些程序,实现大侠 Pradeep 在 playing with ptrace 中的所有示范代码。只是这一次,我们用 utrace 来实现。

在写作本文的时候,utrace 还没有能够进入 Linux upstream, 所以首先我们必须对 kernel 打些补丁。

本文中的例子程序都是在 2.6.32+utrace patch 下调试通过的。您可以在 http://people.redhat.com/roland/utrace/下载 utrace 的补丁,在 2.6.32 下,补丁的顺序如下:

1

2

3

4

>cd /src/linux26

>patch – p1 <tracehook.patch

>patch – p1 <utrace.patch

>patch – p1 <utrace-ptrace.patch

打好补丁之后,重新编译内核,注意需要选中 utrace 选项。

获取 syscall 参数

第一个例子程序打印出目标进程在运行时所调用的系统调用信息,功能类似 strace。

作为演示,我将研究 ls 命令在执行过程中调用了哪些系统调用,以及这些系统调用的参数。

在本文的下载附件中,您可以找到所有在这里罗列出的源代码。

utrace 的应用一般都是内核模块。用于本例的模块由文件 syscallparam.c 实现。示例程序 testReadSysParam.c 演示了如何使用 syscallparam 模块来跟踪 ls 的系统调用。

设计思路非常简单,模块入口参数为 ls 命令的进程号,当模块初始化时,调用 utrace_attach_pid 函数 attach 到 ls 进程。然后调用 utrace_set_events_pid() 函数注册感兴趣的事件。在本例中我们只对系统调用感兴趣,因此只需要注册事件 UTRACE_EVENT(SYSCALL_ENTRY)。该事件对应的处理函数为 syacallparam_syscall_entry。

在 syacallparam_syscall_entry 中,简单地打印系统调用号(保存在 eax 寄存器中), 系统调用参数(保存在 ebx,ecx 和 edx 中), 代码如下:

清单 2. syscallparam_syscall_entry

1

2

3

4

5

6

7

8

9

static u32

syacallparam_syscall_entry(u32 action, struct utrace_engine *engine,

                   struct pt_regs *regs)

{

   if(regs->orig_ax == 4)

   printk ( KERN_INFO "syscall write with argument [%ld] [%ld] [%ld] \n",

             regs->bx, regs->cx, regs->dx);

    return UTRACE_RESUME;

}

目录 syscallparam 下的 Makefile 可以用来编译 syscallparam 模块。之后您可以用 gcc 来编译测试程序,命令如下:

1

>gcc – o test testReadSysParam.c

执行测试程序:

1

>./test

您将看到 ls 的正常运行结果。

系统调用参数是由内核模块调用 printk 打印的,因此假如您未曾配置过 printk level, 则需要用 dmesg 命令来查看结果。下面是我测试时的输出:

清单 3.syscall 测试结果

1

2

3

4

5

6

7

8

>dmesg

 attached to 3500 => 0xd70d33bc

 syscall write with argument [1] [-1220390912] [52]

 syscall write with argument [1] [-1220390912] [41]

 syscall write with argument [1] [-1220390912] [38]

 syscall write with argument [1] [-1220390912] [48]

 syscall write with argument [1] [-1220390912] [52]

通过这个例子,您可以了解 utrace 基本的 attach 和 event 处理功能,还了解到如何在 callback 函数中访问目标进程的寄存器。

接下来,我们进一步访问目标进程的地址空间,做一些更加有趣的事情。

我们要将 ls 命令显示的所有字符串进行反转。比如本来应该显示”test”文件,但我们将内存中的字符串进行反转,从而将显示变成”tset”。其原理是,在进入 write 系统调用前,将保存在 buffer 中的字符串进行反转,这样当系统调用执行后,便可以输出反转后的字符串。

源代码在 funny ls 目录下。文件 funny.c 是模块文件,pls 是本例的测试程序。Funny.c 的基本框架和前例相同,只是添加了下面的代码,用来完成字符反转:

清单 4.reverse 函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

void reverse(char *str, int len)

 {

    int i,j;

    char temp;

    for(i = 0, j = len -2; i <= j; ++i, --j)   

    {

        temp = str[i];

        str[i] = str[j];

        str[j] = temp;   

    }

 }

 

 static u32

 funny_syscall_entry(u32 action, struct utrace_engine *engine,

                    struct pt_regs *regs)

 {

    char * ptr;

    if(regs->orig_ax == 4)

    {

        ptr = regs->cx;

         reverse(ptr, regs->dx);

    }

    return UTRACE_RESUME;

 }

 

将 pls.c 编译,运行 make 生成 funny.ko。便可以看看效果了:

 

 >ls

 abc 123 pls.c funny.ko

 

 >./pls

 cba 321 c.slp ok.ynnuf

如果您将 utrace 版代码和 Pradeep Padala 的 ptrace 实现进行比较,便可以发现,utrace 似乎更加容易使用,因为 utrace 运行在内核态,因此访问用户进程空间非常容易。

Write 系统调用的 buffer 地址保存在 ecx 寄存器中,长度保存在 edx 寄存器。在内核模块中,这些信息很容易获得,直接将地址和长度输入 reverse 函数,便可以将用户进程本来需要打印的字符串进行反转了。

单步执行程序 single step

接下来我们演示单步执行。

程序 dummy1.s 是即将被单步的源程序。

您可能奇怪为什是汇编语言代码,因为在 utrace 这样的底层技术中,所谓单步是指机器指令的单步,而非 gdb 等调试器中的高级语言级单步。高级语言的单步需要更加复杂的符号处理技术。

单步的源代码在附件的 singleStep 目录下。首先在 module init 函数中 attach 目标进程。然后设置我们所感兴趣的事件,QUIESCE,SYSCALL_EXIT 以及 SIGNAL。当目标进程调用 write 系统调用时,single_syscall_exit 被触发,在该 callback 函数中进行判断,如果当前系统调用号为 4, 我们便让目标进程进入单步执行状态。

清单 5. 单步操作代码

1

2

3

4

5

6

if(regs->orig_ax == 4)

{

    return UTRACE_SINGLESTEP;

}

else

    return UTRACE_RESUME;

单步执行结束时,目标进程会触发 signal, 因此我们在 signal 回调函数中打印出当前的 EIP, 来模拟单步执行。Signal 回调函数如下所示:

清单 6. Signal 回调

1

2

3

4

5

6

7

8

9

10

single_signal(u32 action, struct utrace_engine *engine,

                struct pt_regs *regs,

                siginfo_t *info,

                const struct k_sigaction *orig_ka,

                struct k_sigaction *return_ka)

{

   mip = regs->ip;

   printk(KERN_INFO "running instruction at [0x%lx] \n", mip);

   return UTRACE_SINGLESTEP|UTRACE_SIGNAL_REPORT;

}

返回值为 UTRACE_SINGLESTEP, 因此目标进程将继续单步执行。

为程序设置断点

断点是每一个调试器必须实现的功能。无论哪种调试器,断点的原理都基本一样:在需要设置断点的地方,调试器将目标进程原来的代码替换为一个 trap 指令,比如 x86 上为 int 3。这样,当目标进程运行到断点时就会产生一个 trap 中断。在 Linux 中,trap 中断处理函数会向目标进程发送一个 SIGTRAP 信号。调试器在目标进程得到执行之前捕获这个信号,完成必要的工作之后,将断点处的指令替换回原来的内容,然后重新执行。

Breakpoint.c 做的就是这些事情。源代码在下载包的 breakpoint 目录中。我依旧使用 dummy1 进程作为目标进程。Bptest.c 是测试程序,它首先启动 dummy1 程序,然后调用 insmod 加载 breakpoint 模块。给定 dummy1 的进程 id, 和断点的地址。

和一个真正的 debugger 不同,breakpoint.ko 没有能力分析 ELF 文件的 debug 信息,所以需要使用者指定断点的虚拟地址。这可以通过 nm 命令来完成。对于 dummy1, 简单执行 :

>nm dummy1

就可以看到类似下面的输出:

清单 7. nm 的符号输出

1

2

3

4

5

6

7

080482a0   T    _start

080482c4   t    call_gmon_start

080495b8   b    completed.5823

08049618   W    data_start

08048350   t    frame_dummy

080495bc   d    hello

080495ca   D    main

我打算将断点设置在 dummy1 的 main 函数入口处,即 0x80495CA, 十进制为 134518218。在您的系统中,可能编译的结果不太一样,所以在做实验时可能需要修改上述地址的值。

用户通过 addr 参数指定断点的地址,breakpoint 模块就调用 set_bp 函数将 addr 处的一个字节指令替换为 int 3。

清单 8.set_bp 函数

1

2

3

4

5

6

7

static int set_bp(struct task_struct *tsk, unsigned long addr)

{

   int len;

   len = access_process_vm(tsk, addr, insn, 1, 0);

   char bp_insn = BREAKPOINT_INSTRUCTION;

   return access_process_vm(tsk, addr, &bp_insn, 1, 1);

}

直到 Linux2.6.32, 内核函数 access_process_vm 还是没有被 export 出来让其他模块调用,因此我不得不将 access_process_vm 的代码拷贝了一份。该函数能够对目标进程的地址空间进行读写操作,我用它来实现原始指令的保存和 trap 指令的插入。

指令替换完毕之后,当目标进程执行到 addr 时,SIGTRAP 产生,在目标进程返回用户空间之前,breakpoint 模块的 callback 函数 break_signal 被调用。break_signal 只是打印出当前的指令地址,而没有做任何有意义的事情,但作为演示,这应该足够了。

清单 9. break_signal 函数

1

2

3

4

5

6

7

8

9

10

11

12

static u32 break_signal(u32 action, struct utrace_engine *engine,

                struct pt_regs *regs,

                siginfo_t *info,

                const struct k_sigaction *orig_ka,

                struct k_sigaction *return_ka)

{

   mip = regs->ip;

   printk(KERN_INFO "running instruction at [0x%lx] \n", mip);

   len = restore_bp(task,vaddr);

   regs->ip =  vaddr;

   return UTRACE_RESUME|UTRACE_SIGNAL_IGN;

}

采用 utrace 实现代码注入技术 code injection

作为演示,最后一个例子是 code injection, 将一段代码注入到目标进程。这种技术并不只是被黑客所使用,类似 systemTap 等工具也需要这种技术。systemTap 允许用户编写一段代码,这段代码可以在 probe 点执行,这便需要 code injection 技术。

Code inject 的例子代码在目录 inject 下。

例子程序非常简单,是一个循环执行打印的无聊程序。他将向屏幕不断地输出”I am not injected”。我们打算用注入代码改变这个无聊的循环,让他做些其他的事情,比如打印”hello world”…当然,一旦您掌握了注入的原理,便可以做其他任何您认为更有意义的事情 .

注入的原理可以用图 2 来进一步描述。

图 2. 代码注入原理

图 2. 代码注入原理

注入代码的功能是向屏幕打印”hello world”, 这段代码已经被翻译成机器码,并存放在数组中(关于如何获得机器码,可以参考 playing with ptrace 原文)。

我们要做的事情就是将这段机器码注入到目标进程的地址空间,然后将 PC 指针指向该代码段的起始地址处。

作为一个 code injector, 我们并没有特别感兴趣的事件。在本例中,我打算在目标程序执行 write 系统调用时注入代码。这样,之后目标进程的所有 write 系统调用将被截获,转而执行我们已经注入的代码 .

这样就会产生一个比较有趣的结果,本来目标进程打算打印”I am not injected ”, 一旦被注入,就会开始打印”Hello World”。

为了实现上述的目的,我们需要告诉 utrace, 当目标进程调用 system call 时请产生一个通知。然后在相应的 callback 函数中,我们将判断系统调用号,如果是 write 调用,就将目标进程的 PC 指针修改为注入代码的地址。

在本例中,insmod 装载 inject 模块时,module 初始化函数将触发一个 UTRACE_REPORT 动作 . 这将引起 signal callback 被调用 . 在 signal callback 函数中,我们首先在目标进程的地址空间中开辟一段新的内存 ( 这可以通过 do_brk() 内核函数来实现 ), 然后调用 access_process_vm 将保存着打印”Hello world”的二进制代码拷贝到新开辟的内存中。这样就完成了注入。

当目标进程调用 printf() 时,通过 libc, 最终会调用 write 系统调用,此时,System Call 的 callback 函数被触发,在该函数中,我们将目标进程的 EIP 寄存器设置为注入的代码地址。

最后,当注入代码执行完毕后,会执行一条 trap 指令,这将引发一个 SIGTRAP 信号,我们在 signal 的 callback 中恢复原来的 EIP 地址,从而让目标进程恢复原来的状态。就好像什么也不曾发生过一样 .

utrace 的具体应用

关于 utrace, Ingo Molnar 有一句经典的评语:“没有具体的应用,utrace 只是一堆内核中的钩子,我们为什么需要它?”。下面我们就简单介绍几个真实的 utrace 应用。

kmview

Trace 技术在虚拟化领域有一定的应用价值,UML 和 Umview 等虚拟技术被称为 system call virtual machine。以往,他们依赖 ptrace 截获系统调用,但 ptrace 的性能很低。而对于虚拟机,性能是个关键问题。

Kmview 提供了 umview 同样的功能,但采用 utrace 替代 ptrace, 因此它提供了非常好的性能。对于虚拟机开发者而言,这是非常关键的技术突破。关于 kmview 的更多信息可以参考 http://wiki.virtualsquare.org/index.php/KMview.

systemTap

关于 systemTap, 您应该已经非常熟悉了。Kernel 维护者一直对 utrace/systemTap 有一种难以理解的排斥,但在现实中,systemTap 还是得到了大量的应用。为了能达到调试用户进程的目的,systemTap 一直在使用 utrace。为了使用 systemTap, 用户要么使用 RedHat 的发行版,要么自己对内核进行 patch。希望在 2.6.34 或者更晚的版本,utrace 可以最终加入 upstream 中吧。

关于 systemTap, 您可以在网站 http://sourceware.org/systemtap上得到更详细的信息。

uprobe

Uprobe 支持对用户态进程进行动态插装,进而收集调试或者性能信息。2007 年在 Ottawa 举行的 Linux 大会上,IBM 公司的 Jim Keniston 做了题为 Ptrace, Utrace, Uprobes: Lightweight, Dynamic Tracing of User Apps的演讲。该论文是了解 uprobe 的最佳读物。

目前,多数人都认同,systemTap 加上 uprobe 便可以完成 SUN DTrace 的所有功能。

Utrace 简单而耐心的发展历史

早在 2007 年,utrace 就开始进入人们的视线,并且一直在 SystemTap 项目中被使用。然而,三年时光已过,utrace 进入内核却依然步履维艰。

2.6.25 内核发布的时候,一些将 arch 代码和 ptrace 分层的努力被加入内核 . 将处理器体系结构代码和 ptrace 的其他代码间的耦合减小。

2.6.27 tracehook 被加入内核 .

在 2.6.28 中又有一些新的体系结构改写了对 ptrace 的支持代码 .

此后,人们一直在等待 utrace-ptrace 补丁,将 ptrace 的实现完全用 utrace 来重写 . 当 2.6.32 发布后,utrace 的维护者曾试图让 utrace-ptrace 补丁进入 Stephen 的 next 开发树,但遭到了 Linus 的反对。一时之间人们对 utrace 的前景感到忧虑。目前 2.6.34 的 merge window 已经开始,希望能看到 utrace 走进内核。

通过观察 utrace 进入内核的简单历史,我们可以发现,有时候人们需要极大的耐心和毅力,还要面对权威的置疑,经过不懈努力才能使自己的工作被开源社区所接受。所以假如您也遇到了挫折,请不要气馁。

结束语

实际上,即使永远不被主流内核所接收,那也并不会妨害 utrace 的发展,广大的用户自有自己的选择。Linux 是一个开源系统,用户可以根据自己的需要来裁减和修改内核 。所以如果您觉得 utrace 挺不错,那么就不妨试试看吧 。

下载资源

相关主题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值