gdb调试器之"测不准原则"

2012-05-07 22:25:30|  分类: gdb源代码分析 |字号 订阅

一、测不准原则
我大学物理学的不太好,特别是高等物理,这个概念是在很多科普性的读物中都可以见到,就像”罗素悖论“、哥德尔的”不完备理论“、爱因斯坦的”相对论“等,大家都是一知半解,然后根据这个概念大家自由发挥,所以就有千奇百怪的场景和理解了,最后以讹传讹,倒也不清楚这个东西原始真正意义,这种现象在很多成语中也经常出现,例如经典的、也是考试的时候出镜率很高的”差强人意“。
作为无数民间科学家中的一员,我对这个”测不准“的理解就是当你真正观察它的时候,它和它正常的行为是不同的。这一点可能在很多其他场合也是用,例如……(此处大家可以尽情发挥一下)。或者围城中胖诗人曹元朗说的”当你以为你理解了我的时候,你就误解了我“。
二、调试器依赖的手段
当调试器调试一个任务的时候,它同样会对被调试的任务产生一些微妙的影响,这些影响在一些实时系统中表现的比较突出,特别是那些FIFO类型的实时调度任务,因为当任务被调试的时候,它的很多重要事件都要由内核代劳首先通知给调试器,在调试器发出下一个指示之前,被调试任务(线程)不能继续运行,这一点对于很多对初始化顺序有严格要求的系统来说是不能容忍的,所以调试器在很多时候并不是完成的。
对于非实时的系统,调试器同样可能会影响调度,原因同上,但是现象可能不尽相同。因为非实时系统对于任务的调度顺序没有依赖和假设,它本来就是可以以任意顺序运行的,如果需要排序可能使用各种锁来同步。
调试器主要是通过SIGSTOP来主动要求一个线程暂时冷静下来,而内核则通过ptrace_stop来强制,大家可以看一下内核在哪些地方调用了这个函数:
static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info)
{
    /* Let the debugger run.  */
    set_current_state(TASK_TRACED);该状态不可运行,并且不接收信号。
……
if (may_ptrace_stop()) {
        do_notify_parent_cldstop(current, CLD_TRAPPED);通知父进程,
        read_unlock(&tasklist_lock);
        schedule();让出调度权。
    } else {
        /*
         * By the time we got the lock, our tracer went away.
         * Don't stop here.
         */
        read_unlock(&tasklist_lock);
        set_current_state(TASK_RUNNING);
        current->exit_code = nostop_code;
    }
}
当进程附加的时候,内核也不拿自己当外人,也是毫不客气的发送了一个SIGSTOP信号过去。我们知道,很多时候,线程都是信号敏感的,也就是系统调用是可以被信号唤醒的,这明显会影响系统任务的执行,我们看一下调试器附加的代码:
sys_ptrace--->>>ptrace_attach
    force_sig_specific(SIGSTOP, task);
这里向被附加任务发送了一个SIGSTOP信号,之后将会看到,这个调用将会对被调试进程的运行产生影响。
三、附加被调试任务
1、测试代码
之前的代码已经看到,它会发送SIGSTOP给被附加线程,我们测试一下最为简单的read系统调用,测试程序为:
[tsecer@Harry TracerInter]$ cat tracersense.c
#include <fcntl.h>
#include <stdio.h>

int main()
{
    char buf[10];
    int readin = read(0,buf,sizeof buf);
    printf("readin is %d\n",readin);
}
[tsecer@Harry TracerInter]$ gcc tracersense.c -g
[tsecer@Harry TracerInter]$ sleep 1000 | ./a.out
然后到另一个终端中使用调试器附加a.out对应的进程,看它是否会被从read系统调用唤醒。
[root@Harry ~]# cat /proc/18587/status
Name:    a.out
State:    S (sleeping)
……
voluntary_ctxt_switches:    3 注意这个调度次数,在调试器附加之后,被调试线程的调度次数将会增加
nonvoluntary_ctxt_switches:    2
[root@Harry ~]# gdb -p 18587
……
(gdb) shell cat /proc/18587/status
Name:    a.out
State:    T (tracing stop)
……
voluntary_ctxt_switches:    4 调试器附加之后,被调试任务执行次数加一,说明被调试线程从read系统调用返回了,但是程序没有退出运行。
nonvoluntary_ctxt_switches:    2
(gdb) quit
A debugging session is active.

Inferior 1 [process 18587] will be detached.

Quit anyway? (y or n) y
Detaching from program: /home/tsecer/CodeTest/TracerInter/a.out, process 18587
[root@Harry ~]# cat /proc/18587/status
Name:    a.out
State:    S (sleeping)
Tgid:    18587
……
voluntary_ctxt_switches:    5调试器退出附加之后,被调试线程调度次数再次增加。但是奇怪的是被调试任务并没有从read系统调用返回到用户态空间(否则进程会直接退出)。
nonvoluntary_ctxt_switches:    2
2、管道read被唤醒
linux-2.6.21\fs\pipe.c
pipe_read(struct kiocb *iocb, const struct iovec *_iov, unsigned long nr_segs, loff_t pos)
        if (signal_pending(current)) {
            if (!ret)
                ret = -ERESTARTSYS;
            break;
        }
注意这个返回错误码。当调试器收到一个子进程上报的信号之后,如果是自己发送的SIGSTOP,那么会对被调试任务透明的取消这个信号,取消的方法就是通过ptrace的PTRACE_CONT请求实现,看其实现非常简单linux-2.6.21\arch\i386\kernel\ptrace.c
        child->exit_code = data;
然后子进程继续运行,执行信号获取函数
linux-2.6.21\kernel\signal.c:   int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie)
            /* Let the debugger run.  */
            ptrace_stop(signr, signr, info);

/* We're back.  Did the debugger cancel the sig?  */
            signr = current->exit_code;
            if (signr == 0)
                continue;
对于我们测试的例子,它刚好满足这个条件(调试器通过PTRACE_CONT清空了这个信号值),所以直接返回,然后进入信号处理函数
linux-2.6.21\arch\i386\kernel\signal.c:static void fastcall do_signal(struct pt_regs *regs)
if (signr > 0) {不满足该条件,执行下面分支。
……
        return;
    }

/* Did we come from a system call? */
    if (regs->orig_eax >= 0) {
        /* Restart the system call - no handlers present */
        switch (regs->eax) {
        case -ERESTARTNOHAND:
        case -ERESTARTSYS:
        case -ERESTARTNOINTR:
            regs->eax = regs->orig_eax;
            regs->eip -= 2;
            break;

case -ERESTART_RESTARTBLOCK:
            regs->eax = __NR_restart_syscall;
            regs->eip -= 2;这是整个机制的实现核心:将用户态指针减去两个字节,也就是386体系结构中linux下系统调用int 0x80指令占用的两个字节,这样被中断的系统调用(read)就可以再次执行而不真正对APP可见这次唤醒。
            break;
        }
    }
我看了一下另一个常用的可以测试的系统调用sys_pause,它被唤醒的时候也是设置
asmlinkage long
sys_pause(void)
{
    current->state = TASK_INTERRUPTIBLE;
    schedule();
    return -ERESTARTNOHAND;
}
所以使用pause测试应用程序可感知唤醒也不行、select也不行,所以这个现象只是作为一个理论存在,但是工程中应该比价少出现的情况,暂且不说。
四、对于不可唤醒睡眠任务的附加失败例子
[tsecer@Harry TracerInter]$ cat NonInt.c
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
    pid_t foker;
    if (0 == (foker = vfork())) 执行vfork,从而使父进程进入不可唤醒休眠。
    {
        sleep (1000);
    }
    printf("Father side This sentense should never been seen\n");
}
[tsecer@Harry TracerInter]$ gcc NonInt.c -o NonInt.c.exe -g
[tsecer@Harry TracerInter]$ ./NonInt.c.exe
另一个终端中调试器附加父进程:
[root@Harry ~]# ps aux
……
tsecer   18841  0.0  0.0   1740   272 pts/0    D+   22:17   0:00 ./NonInt.c.exe
tsecer   18842  0.0  0.0   1740   272 pts/0    S+   22:17   0:00 ./NonInt.c.exe
root     18843  1.0  0.0   4688   992 pts/6    R+   22:17   0:00 ps aux
[root@Harry ~]# cat /proc/18841/status
Name:    NonInt.c.exe
State:    D (disk sleep)
Tgid:    18841
Pid:    18841
PPid:    12127
……
[root@Harry ~]# gdb -p 18841 附加父进程,
GNU gdb (GDB) Fedora (7.0-3.fc12)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 18841

这个显示将会一直持续,也就是说调试器将会一直无法从这里返回,这说明调试器没有收到内核通知的子进程收到SIGSTOP的事件,调试器在此一直等待。
五、sum up
这里没有分析gdb的实现代码,只是结合了内核的相关接口猜测和观察了一下gdb的执行原理,纯粹是探讨性内容,可能实际意义不大。

gdb调试器之测不准原则相关推荐

  1. ECE220生存指南[02] MP7: GDB 调试Debug

    算法旅人 2021年11月12日星期五 本周的MP重点在于学习使用GDB进行调试,这里贴一个GDB的官方介绍: GNU symbolic debugger,简称「GDB 调试器」,是 Linux 平台 ...

  2. gdb调试 print打印不出变量值或者不准确

    编译选项加了 -O,即便是-O0,也不能正常显示,需要加上-gstabs+这个编译选项, -gdwarf-2这个编译选项会与-gstabs+冲突,去掉-gstabs+,只保留-gdwarf-2选项可以 ...

  3. GDB 调试 .NET 程序实录 - .NET 调用 .so 出现问题怎么解决

    注:本文重要信息使用 *** 屏蔽关键字. 最近国庆前,项目碰到一个很麻烦的问题,这个问题让我们加班到凌晨三点. 大概背景: 客户给了一些 C语言 写的 SDK 库,这些库打包成 .so 文件,然后我 ...

  4. gdb 调试命令的使用及总结

    GDB: The GNU Project Debugger:http://www.gnu.org/software/gdb/documentation/ 参考:http://www.jianshu.c ...

  5. 为DEV C++/CodeBlock配置gdb调试遇到的问题

    DEV C++和CodeBlock都只是一个IDE,不能编译调试,需要自己配置MINGW和gdb调试 1.MINGW 在这下载mingw-get-setup.exe安装即可. https://sour ...

  6. 在Linux中如何使用gdb调试C程序

    无论多么有经验的程序员,开发的任何软件都不可能完全没有 bug.因此,排查及修复 bug 成为软件开发周期中最重要的任务之一.有许多办法可以排查 bug(测试.代码自审等等),但是还有一些专用软件(称 ...

  7. STM32定时器捕获编码器模式测速和方向测不准问题

    ** STM32定时器捕获编码器模式测速和方向测不准问题 问题概述 关于STM32编码器模式电机测速的资料网上一抓一大把,却发现真的拿过来用还是有问题的,比如刚刚做了个东西,是个个头比较大的麦克纳姆轮 ...

  8. 万用表测不准简单维修

    文章目录 1. 起因 2. 故障表现 3. 组装 1. 起因 这几天装灯暖浴霸,需要用到万用表,有点坏了, 是大一大二上电工电子实验课时候买的万用表,胜利V97,也得有7-8年了,100多块钱. 2. ...

  9. linux_进程类相关学习-fork函数-getpid函数-getppid函数-getuid函数-geteuid函数-getgid函数-getegid函数-进程之间共享数据-进程gdb调试

    接上一篇:linux_环境变量-C语言代码打印环境变量-getenv函数-setenv函数-unsetenv函数 本次来分享进程类相关的学习,主要就是分享一些函数的使用,来,开始上菜: 目录 1.fo ...

最新文章

  1. 在C#中选择“.NET研究”正确的集合进行编码
  2. java类的定义的实例_Java类的定义与实例化
  3. odoo定时发送邮件
  4. 1的恢复出厂设置在哪里_无线路由器怎么恢复出厂设置
  5. tensorflow中学习率、过拟合、滑动平均的学习
  6. IOS开发之coreData
  7. 近期资料分享汇总,还不快来看看你漏了哪份没拿?
  8. jzoj5123-diyiti【统计,容斥】
  9. 使用WCF建立起Silverlight客户端与服务端的桥梁
  10. Linux ISATAP配置
  11. Spring源码分析之BeanPostProcessor接口和BeanFactoryPostProcessor接口方法不执行原因分析
  12. Windows下使用taskkill 命令批量结束进程
  13. 最近很多面试都是考研凉凉的,心塞!
  14. activex 控件 过期_ie8/9下Activex控件无法加载的两种解决方法
  15. HDU-3237-Help Bubu
  16. 开水团2023届实习笔试
  17. Stealing packets
  18. Macos 安装MacTex SublimeText3 Skim环境
  19. 淘宝电商项目落地,从零开始搭建亿级系统架构笔记
  20. 怎样删除多余的本地连接

热门文章

  1. #10038.A Horrible Poem
  2. 龙城0772信息门户网设计制作
  3. 百度新闻推荐真的在推荐新闻吗
  4. 聚类篇——(四)有序样品聚类
  5. 盛世昊通董车长2.0“后“积薄发,点爆汽车后市场
  6. 我们为什么要使用 Markdown
  7. 以软件测试的角度测试一支笔,软件测试面试:如何测试一支笔(铅笔,钢笔,中性笔)...
  8. PAT福尔摩斯的约会
  9. android 高仿美团,Android 仿美团、大众点评团购详情UI
  10. admit commit permit