适用GDB调试NS2程序

 非常详细的一篇文章,特转载收藏

gdb调试工具类似于vc中的debug工具。目前有命令行的和窗口界面的。窗口界面是DDD。我只用了命令行的。

用gdb调试ns2,要注意几点:
1,安装gdb,下载gdb包,安装了就是了。
2,改写Makefile,使之称为debug版本,具体做法:
在代码里面添加调试信息:修改Makefile(没有任何后缀的)将里面
CCOPT =     #如果是ns-allinone-2.28,这里是CCOPT = -O2
STATIC = 
LDFLAGS = $(STATIC)
LDOUT = -o $(BLANK)
改变为:
CCOPT = -g   #如果是ns-allinone-2.28,这里修改为 CCOPT = -O2 -g
STATIC = 
LDFLAGS = $(STATIC)
LDOUT = -o $(BLANK)
修改的东西是粉红色字体所示
然后重新编译 
注意:以下步骤一个都不能少
Make clean 
Make depend
Make

3,调试:
建议把我们的测试脚本写在ns-allinone-2.**/ns-2.XX/mytclscript/下面
当调试时,首先进入ns-2.XX文件夹   [tengda@localhost ~]$ cd ns-allinone-2.31/ns-2.31/
然后 [tengda@localhost ns-2.31]$ gdb ns
之后会出现
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
 
(gdb) 
然后在该平台上运行我们的脚本,可以用cd 命令进入我们脚本所在文件夹。运行我们的脚本:
(gdb) run mbs.tcl

这样就可以进行调试了。具体调试过程与命令,我转载如下:

============================================

GDB(GNU 项目调试器)可以让您了解程序在执行时“内部” 究竟在干些什么,以及在程序发生崩溃的瞬间正在做什么。

GDB 做以下 4 件主要的事情来帮助您捕获程序中的 bug:

  • 在程序启动之前指定一些可以影响程序行为的变量或条件
  • 在某个指定的地方或条件下暂停程序
  • 在程序停止时检查已经发生了什么
  • 在程序执行过程中修改程序中的变量或条件,这样就可以体验修复一个 bug 的成果,并继续了解其他 bug

 

要调试的程序可以是使用 C、C++、Pascal、Objective-C 以及其他很多语言编写的。GDB 的二进制文件名是 gdb。

gdb 中有很多命令。使用 help 命令可以列出所有的命令,以及关于如何使用这些命令的介绍。下表给出了最常用的 GDB 命令。


表 1. gdb 中最常用的命令

命令 说明 例子
help 显示命令类别 help - 显示命令类别
help breakpoints - 显示属于 breakpoints 类别的命令
help break - 显示 break 命令的解释
run 启动所调试的程序 ?
kill 终止正在调试的程序的执行 通常这会在要执行的代码行已经超过了您想要调试的代码时使用。执行 kill 会重置断点,并从头再次运行这个程序
     
cont 所调试的程序运行到一个断点、异常或单步之后,继续执行 ?
info break 显示当前的断点或观察点 ?
break 在指定的行或函数处设置断点 break 93 if i=8 - 当变量 i 等于 8 时,在第 93 行停止程序执行
Step 单步执行程序,直到它到达一个不同的源代码行。您可以使用 s 来代表 step 命令 ?
Next 与 step 命令类似,只是它不会“单步跟踪到”子例程中 ?
print 打印一个变量或表达式的值 print pointer - 打印变量指针的内容
print *pointer - 打印指针所指向的数据结构的内容
delete 删除某些断点或自动显示表达式 delete 1 - 删除断点 1。断点可以通过 info break 来显示
watch 为一个表达式设置一个观察点。当表达式的值发生变化时,这个观察点就会暂停程序的执行 ?
where 打印所有堆栈帧的栈信息 where - 不使用参数,输出当前线程的堆栈信息
where all - 输出当前线程组中所有线程的堆栈信息
where threadindex - 输出指定线程的堆栈信息
attach 开始查看一个已经运行的进程 attach <process_id> - 附加到进程 process_id 上。process_id 可以使用 ps 命令找到
info thread 显示当前正在运行的线程 ?
thread apply threadno command 对一个线程运行 gdb 命令 thread apply 3 where - 对线程 3 运行 where 命令
Thread threadno 选择一个线程作为当前线程 ?

 

如果一个程序崩溃了,并生成了一个 core 文件,您可以查看 core 文件来判断进程结束时的状态。使用下面的命令启动 gdb:

 

# gdb programname corefilename 

 

要调试一个 core 文件,您需要可执行程序、源代码文件以及 core 文件。要对一个 core 文件启动 gdb,请使用 -c 选项:

 

# gdb -c core programname 

 

gdb 会显示是哪行代码导致这个程序产生了核心转储。

默认情况下,核心转储在 Novell 的 SUSE LINUX Enterprise Server 9(SLES 9)和 Red Hat? Enterprise Linux Advanced Server(RHEL AS 4)上都是禁用的。要启用核心转储,请以 root 用户的身份在命令行中执行 ulimit –c unlimited

清单 8 中的例子阐述了如何使用 gdb 来定位程序中的 bug。清单 8 是一段包含 bug 的 C++ 代码。

清单 8 中的 C++ 程序试图构建 10 个链接在一起的数字框(number box),例如:


图 1. 一个包含 10 个链接在一起的数字框的列表

然后试图从这个列表中逐个删除数字框。

编译并运行这个程序,如下所示:


清单 9. 编译并运行这个程序

                
# g++ -g -o gdbtest1 gdbtest1.cpp
# ./gdbtest1
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Segmentation fault

 

正如您可以看到的一样,这个程序会导致段错误。调用 gdb 来看一下这个问题,如下所示:


清单 10. 调用 gdb

                
# gdb ./gdbtest1
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux"...Using host libthread_db 
library "/lib/tls/libthread_db.so.1".
(gdb)

 

您知道段错误是在数字框 "9" 被删除之后发生的。执行 run 和 where 命令来精确定位段错误发生在程序中的什么位置。


清单 11. 执行 run 和 where 命令

                
(gdb) run
Starting program: /root/test/gdbtest1 
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Program received signal SIGSEGV, Segmentation fault.
0x10000f74 in NumBox<int>::GetNext (this=0x0) at gdbtest1.cpp:14
14              NumBox<T>*GetNext() const { return Next; }
(gdb) where
#0  0x10000f74 in NumBox<int>::GetNext (this=0x0) at gdbtest1.cpp:14
#1  0x10000d10 in NumChain<int>::RemoveBox (this=0x10012008, 

item_to_remove=@0xffffe200) at gdbtest1.cpp:63 #2 0x10000978 in main (argc=1, argv=0xffffe554) at gdbtest1.cpp:94 (gdb)

 

跟踪信息显示这个程序在第 14 行 NumBox<int>::GetNext (this=0x0) 接收到一个段错误。这个数字框上 Next 指针的地址是 0x0,这对于一个数字框来说是一个无效的地址。从上面的跟踪信息可以看出,GetNext 函数是由 63 行调用的。看一下在 gdbtest1.cpp 的 63 行附近发生了什么:


清单 12. gdbtest1.cpp

                
     54                       } else {
     55                               temp->SetNext (current->GetNext());
     56                               delete temp;
     57                               temp = 0;
     58                               return 0;
     59                       }
     60               }
     61               current = 0;
     62               temp = current;
     63               current = current->GetNext();
     64       }
     65 
     66       return -1;

 

第 61 行 current=0 将这个指针设置为一个无效的地址,这正是产生段错误的根源。注释掉第 61 行,将其保存为 gdbtest2.cpp,然后编译并重新运行。


清单 13. 再次运行程序(gdbtest2.cpp)

                
# g++ -g -o gdbtest2 gdbtest2.cpp
# ./gdbtest2
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Number Box "0" deleted

 

这个程序现在可以成功完成而不会出现段错误了。然而,结果并不像我们预期的一样:程序在删除 Number Box "9"之后删除了 Number Box "0",而不像我们期望的一样删除 Number Box "8,"。使用 gdb 再次来看一下。


清单 14. 再次使用 gdb 进行查看

                
# gdb ./gdbtest2
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux"...Using host libthread_db 
library "/lib/tls/libthread_db.so.1".
(gdb) break 94 if i==8
Breakpoint 1 at 0x10000968: file gdbtest2.cpp, line 94.
(gdb) run
Starting program: /root/test/gdbtest2 
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Breakpoint 1, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
94                      list ->RemoveBox(i);

 

您可能希望找出为什么这个程序删除的是 Number Box 0,而不是 Number Box 8,因此需要在您认为程序会删除 Number Box 8 的地方停止程序。设置这个断点:break 94 if i==8,可以在 i 等于 8 时在第 94 行处停止程序。然后单步跟踪到 RemoveBox() 函数中。


清单 15. 单步跟踪到 RemoveBox() 函数中

                
(gdb) s
38                      NumBox<T> *temp = 0;  
(gdb) s
40                      while (current != 0) {
(gdb) print pointer
$1 = (NumBox<int> *) 0x100120a8
 (gdb) print *pointer
$2 = {Num = 0, Next = 0x0}
(gdb)

 

指针早已指向了 Number Box "0",因此这个 bug 可能就存在于程序删除 Number Box "9" 的地方。要在 gdb 中重新启动这个程序,请使用 kill 删除原来的断点,然后添加一个 i 等于 9 时的新断点,然后再次运行这个程序。


清单 16. 在 gdb 中重新启动程序

                
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x10000968 in main at gdbtest2.cpp:94
        stop only if i == 8
        breakpoint already hit 1 time
(gdb) delete 1
(gdb) break 94 if i==9
Breakpoint 2 at 0x10000968: file gdbtest2.cpp, line 94.
(gdb) run
Starting program: /root/test/gdbtest2 
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Breakpoint 2, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
94                      list ->RemoveBox(i);
(gdb)

 

当这一次单步跟踪 RemoveBox() 函数时,要特别注意 list->pointer 正在指向哪一个数字框,因为 bug 可能就在于 list->pointer 开始指向 Number Box "0" 的地方。请使用 display *pointer命令来查看,这会自动显示这个函数。


清单 17. 使用 display *pointer 命令进行监视

                
Breakpoint 2, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
94            list ->RemoveBox(i);
(gdb) s
NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200) 
at gdbtest2.cpp:37 37 NumBox<T> *current = pointer; (gdb) display *pointer 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 38 NumBox<T> *temp = 0; 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 40 while (current != 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 41 if (current->GetValue() == item_to_remove) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s NumBox<int>::GetValue (this=0x100120a8) at gdbtest2.cpp:16 16 const T& GetValue () const { return Num; } (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
at gdbtest2.cpp:42 42 if (temp == 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 44 if (current->GetNext() == 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s NumBox<int>::GetNext (this=0x100120a8) at gdbtest2.cpp:14 14 NumBox<T>*GetNext() const { return Next; } (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
at gdbtest2.cpp:50 50 delete current; 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s ~NumBox (this=0x100120a8) at gdbtest2.cpp:10 10 std::cout << "Number Box " <<"\"" << GetValue()
<<"\"" <<" deleted" << std::endl; (gdb) s NumBox<int>::GetValue (this=0x100120a8) at gdbtest2.cpp:16 16 const T& GetValue () const { return Num; } (gdb) s Number Box "9" deleted ~NumBox (this=0x100120a8) at gdbtest2.cpp:11 11 Next = 0; (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
at gdbtest2.cpp:51 51 current = 0; 1: *this->pointer = {Num = 0, Next = 0x0} (gdb) s 53 return 0; 1: *this->pointer = {Num = 0, Next = 0x0} (gdb) s 0x10000d1c 66 return -1; 1: *this->pointer = {Num = 0, Next = 0x0}

 

从上面的跟踪过程中,您可以看到 list->pointer 在删除 Number Box "9" 之后指向了 Number Box "0"。这个逻辑并不正确,因为在删除 Number Box "9" 之后,list->pointer 应该指向的是 Number Box "8"。现在非常显然我们应该在第 50 行之前添加一条语句 pointer = pointer->GetNext();,如下所示:


清单 18. 在第 50 行之前添加一条 pointer = pointer->GetNext(); 语句

                
     49                     } else {
     50                             pointer = pointer->GetNext();
     51                             delete current;
     52                             current = 0;
     53                      }
     54                      return 0;

 

将新修改之后的程序保存为 gdbtest3.cpp,然后编译并再次运行。


清单 19. 再次运行程序(gdbtest3.cpp)

                
# g++ -g -o gdbtest3 gdbtest3.cpp
# ./gdbtest3
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Number Box "8" deleted
Number Box "7" deleted
Number Box "6" deleted
Number Box "5" deleted
Number Box "4" deleted
Number Box "3" deleted
Number Box "2" deleted
Number Box "1" deleted
Number Box "0" deleted

 

这才是我们期望的结果。

多线程环境

在 GDB 中有一些特殊的命令可以用于多线程应用程序的调试。下面这个例子给出了一个死锁情况,并介绍了如何使用这些命令来检查多线程应用程序中的问题:


清单 20. 多线程的例子

                
#include <stdio.h>
#include "pthread.h>
pthread_mutex_t AccountA_mutex;
pthread_mutex_t AccountB_mutex;
struct BankAccount {
     char account_name[1];
     int balance;
};
struct BankAccount  accountA = {"A", 10000 };
struct BankAccount  accountB = {"B", 20000 };
void * transferAB (void* amount_ptr) {
     int amount = *((int*)amount_ptr);
     pthread_mutex_lock(&AccountA_mutex);
     if (accountA.balance < amount)   {
             printf("There is not enough memory in Account A!\n");
             pthread_mutex_unlock(&AccountA_mutex);
             pthread_exit((void *)1);
     }
     accountA.balance -=amount;
     sleep(1);
     pthread_mutex_lock(&AccountB_mutex);
     accountB.balance +=amount;
     pthread_mutex_unlock(&AccountA_mutex); 
     pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
     int amount = *((int*)amount_ptr);
     pthread_mutex_lock(&AccountB_mutex);
     if (accountB.balance < amount)   {
             printf("There is not enough memory in Account B!\n");
             pthread_mutex_unlock(&AccountB_mutex);
             pthread_exit((void *)1);
     }
     accountB.balance -=amount;
     sleep(1);
     pthread_mutex_lock(&AccountA_mutex);
     accountA.balance +=amount;
     pthread_mutex_unlock(&AccountB_mutex);
     pthread_mutex_unlock(&AccountA_mutex);
}
int main(int argc, char* argv[]) {
     int             threadid[4];
     pthread_t       pthread[4];
     int             transfer_amount[4] = {100, 200, 300, 400};
     int             final_balanceA, final_balanceB;
     final_balanceA=accountA.balance-transfer_amount[0]-
transfer_amount[1]+transfer_amount[2]+transfer_amount[3]; final_balanceB=accountB.balance+transfer_amount[0]
+transfer_amount[1]-transfer_amount[2]-transfer_amount[3]; if (threadid[0] = pthread_create(&pthread[0], NULL, transferAB,
(void*)&transfer_amount[0]) " 0) { perror("Thread #0 creation failed."); exit (1); } if (threadid[1] = pthread_create(&pthread[1], NULL, transferAB,
(void*)&transfer_amount[1]) " 0) { perror("Thread #1 creation failed."); exit (1); } if (threadid[2] = pthread_create(&pthread[2], NULL, transferBA,
(void*)&transfer_amount[2]) < 0) { perror("Thread #2 creation failed."); exit (1); } if (threadid[3] = pthread_create(&pthread[3], NULL, transferBA,
(void*)&transfer_amount[3]) < 0) { perror("Thread #3 creation failed."); exit (1); } printf("Transitions are in progress.."); while ((accountA.balance != final_balanceA) && (accountB.balance
!= final_balanceB)) { printf(".."); } printf("\nAll the money is transferred !!\n"); }

 

使用 gcc 来编译这个程序,如下所示:

# gcc -g -o gdbtest2 gdbtest2.c -L/lib/tls -lpthread

 

程序 gdbtest2 会挂起,不会返回一条 All the money is transferred !! 消息。

将 gdb 附加到正在运行的进程上,从而了解这个进程内部正在发生什么。


清单 21. 将 gdb 附加到正在运行的进程上

                
# ps -ef |grep gdbtest2
root      9510  8065  1 06:30 pts/1    00:00:00 ./gdbtest2
root      9516  9400  0 06:30 pts/4    00:00:00 grep gdbtest2
# gdb -pid 9510
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux".
Attaching to process 9510
Reading symbols from /root/test/gdbtest2...done.
Using host libthread_db library "/lib/tls/libthread_db.so.1".
Reading symbols from /lib/tls/libpthread.so.0...done.
[Thread debugging using libthread_db enabled]
[New Thread 1073991712 (LWP 9510)]
[New Thread 1090771744 (LWP 9514)]
[New Thread 1086577440 (LWP 9513)]
[New Thread 1082383136 (LWP 9512)]
[New Thread 1078188832 (LWP 9511)]
Loaded symbols for /lib/tls/libpthread.so.0
Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld.so.1...done.
Loaded symbols for /lib/ld.so.1
0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) info thread
  5 Thread 1078188832 (LWP 9511)  0x0ffe94ec in __lll_lock_wait () 
from /lib/tls/libpthread.so.0 4 Thread 1082383136 (LWP 9512) 0x0ffe94ec in __lll_lock_wait ()
from /lib/tls/libpthread.so.0 3 Thread 1086577440 (LWP 9513) 0x0ffe94ec in __lll_lock_wait ()
from /lib/tls/libpthread.so.0 2 Thread 1090771744 (LWP 9514) 0x0ffe94ec in __lll_lock_wait ()
from /lib/tls/libpthread.so.0 1 Thread 1073991712 (LWP 9510) 0x0ff4ac40 in __write_nocancel ()
from /lib/tls/libc.so.6 (gdb)

 

从 info thread 命令中,我们可以了解到除了主线程(thread #1)之外的所有线程都在等待函数 __lll_lock_wait () 完成。

使用 thread apply threadno where 命令来查看每个线程到底运行到了什么地方:


清单 22. 查看每个线程运行到了什么地方

                
(gdb) thread apply 1 where
Thread 1 (Thread 1073991712 (LWP 9510)):
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
#1  0x0ff4ac28 in __write_nocancel () from /lib/tls/libc.so.6
Previous frame identical to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 2 where
Thread 2 (Thread 1090771744 (LWP 9514)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 3 where
Thread 3 (Thread 1086577440 (LWP 9513)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 4 where
Thread 4 (Thread 1082383136 (LWP 9512)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
 (gdb) thread apply 5 where
Thread 5 (Thread 1078188832 (LWP 9511)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6

 

每个线程都试图对一个互斥体进行加锁,但是这个互斥体却是不可用的,可能是因为有另外一个线程已经对其进行加锁了。从上面的证据我们可以判断程序中一定存在死锁。您还可以看到哪个线程现在拥有这个互斥体。


清单 23. 查看哪个线程拥有互斥体

                
(gdb) print AccountA_mutex
$1 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2527, 
__m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}} (gdb) print 0x2527 $2 = 9511 (gdb) print AccountB_mutex $3 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2529,
__m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}} (gdb) print 0x2529 $4 = 9513 (gdb)

 

从上面的命令中,我们可以看出 AccontA_mutex 是被线程 5(LWP 9511)加锁(拥有)的,而 AccontB_mutex 是被线程 3(LWP 9513)加锁(拥有)的。

为了解决上面的死锁情况,可以按照相同的顺序对互斥体进行加锁,如下所示: 
清单 24. 按照相同的顺序对互斥体进行加锁

                    
.
.
void * transferAB (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountA.balance < amount)   {
                printf("There is not enough memory in Account A!\n");
                pthread_mutex_unlock(&AccountA_mutex);
                pthread_exit((void *)1);
        }
        accountA.balance -=amount;
        sleep(1);
        accountB.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountB.balance < amount)   {
                printf("There is not enough memory in Account B!\n");
                pthread_mutex_unlock(&AccountB_mutex);
                pthread_exit((void *)1);
        }
        accountB.balance -=amount;
        sleep(1);
        accountA.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_unlock(&AccountB_mutex);
}
.
.

 

或者对每个帐号单独进行加锁,如下所示: 
清单 25. 对每个帐号单独进行加锁

                    
.
.
void * transferAB (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        if (accountA.balance < amount)   {
                printf("There is not enough memory in Account A!\n");
                pthread_mutex_unlock(&AccountA_mutex);
                pthread_exit((void *)1);
        }
        accountA.balance -=amount;
        sleep(1);
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex); 
        accountB.balance +=amount;
        pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountB.balance < amount)   {
                printf("There is not enough memory in Account B!\n");
                pthread_mutex_unlock(&AccountB_mutex);
                pthread_exit((void *)1);
        }
        accountB.balance -=amount;
        sleep(1);
        pthread_mutex_unlock(&AccountB_mutex);
        pthread_mutex_lock(&AccountA_mutex); 
        accountA.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
}.

 

===========================================

Tags:

C/C++中宏的使用

C/C++中使用宏非常的方便,比如下面这一行实现两个元素的交换

cpp代码
  1. #define exch(A,B) { Item t = A; A = B; B = t;} 

但是宏有一个潜在的问题,详见《C++编程思想》关于内联函数一章的具体内容。

问题是什么列?就是宏只是简单的代码替换,并没有内联函数那样的生成临时变量的功能。今天我着实被这个问题坑的很惨。三路划分的快速排序算法结果总是不正确,搞了半个多小时,才发现问题。

如果使用上面的exch的宏,下面的代码就会出现问题

cpp代码
  1. exch(a[++p],a[i]);  
  2. exch(a[--q],a[j]); 

你会发现,被交换的元素的索引是不正确的,总是位于期望元素的后面一个。最终导致结果出错。

问题就出在宏上面。这里不用把++p、--q这样的放到下标中,必须在事前计算好,直接放变量,如a[p]、a[q]。因为宏是实现的整字替换,那么结果就是

cpp代码
  1. Item t = a[++p]; a[++p] = a[i]; a[i] = t; 

然后就悲剧了。

看书必须敲代码,不敲代码,犯一些错误,印象不深刻啊。你要是问我C++的内联函数有什么好处,以及关于内联函数的地址分配等问题,我肯定能背出书上的内容。但是背书没用啊,轮到自己敲代码的时候就傻×了。

Tags:

一个国外的广告网站readbud

readbud这个网站,晚上膘叔推荐的,说是看文章有钱拿。

看了一下,基本上就是根据你勾选的个人兴趣,推荐软文,里面做了个把连接,定向投放广告。

省去了需要搜索引擎分析页面内容,完了从广告商的佣金里面提出一部分吸引流量。

那个打分,我估计是统计软文质量用的,打分高的软文就多展示一下下。

阅读文章并打分有很少量的钱,好在是美金,paypal结算,有兴趣的可以试试。

readbud,不厚道的说,大家点击连接进入吧,就别自己输入网址了,不解释,好人一生平安,o(∩_∩)o 哈哈

Tags:

zz如何在UBUNTU下制作ISO镜像

       在Windows下有很多大型破解软件可以完成,但是在linux下就轻便多了。而且不用承受非法使用软件带来的不安(如果你感觉不安的话)。步骤如下

1 如果你是直接从cd压制iso文件的,执行

sudo umount /dev/cdrom
dd if=/dev/cdrom of=file.iso bs=1024

2 如果你要把某个文件或者目录压到iso文件中,需要使用mkisofs这个工具。这个工具非常强大,还可以压缩DVD格式的iso。你想改的参数都可以修改,而且还有-gui这个参数,用脚趾头猜猜是干什么的?使用linux一定要学会看帮助哦。最简单的用法如下:

mkisofs -r -o file.iso your_folder_name/

3 如果你想装酷生成一个MD5文件,执行

md5sum file.iso > file.iso.md5

4 如果你想把一个iso文件烧制出来更简单了。右键点这个文件,然后看到了没有?有烧录的选项。我的刻录机可能不行了,每次在windiows下刻录很容易刻坏。所以我总是到ubuntu下来刻录,又快又好。

 

Tags:

NS2无线网络仿真trace文件mac地址不正确的问题

使用长庚大学的NS2的WIMAX模块做仿真,结果trace文件的mac地址总是很奇怪,而且nam的动画显示仿真过程中有问题。

一步步的查啊查,最后发现问题在cmutrace类(trace/cmu-trace.cc)中。一下分析全部是针对老版本的trace格式,请自行修改变通。

hdr_mac802_11(mac/mac-802_11.h)与hdr_mac(mac/mac.h)使用的是同一块内存空间,而我所使用的802.16的头部,是在hdr_mac的基础上再加了一个hdr_mac802_16,也就是说,如果要取mac地址相关信息,应该是从hdr_mac中间取。

看cmutrace的format_mac函数,第一行:struct hdr_mac802_11 *mh = HDR_MAC802_11(p); 也就是说默认无线仿真是802.11的,那么用hdr_mac802_11结构体,当然取不到正确的源和目的MAC地址啦。继续往上看,format_mac_common函数的最后一个分支是对老版本的trace格式进行处理的,有下面几行:

cpp代码
  1. if (strcmp (mactype, "Mac/SMAC") == 0) {  
  2.    format_smac(p, offset);  
  3.   } else {  
  4.    format_mac(p, offset);  
  5.   } 

那么,我们自己添加一个 else if (strcmp (mactype, "Mac/802_16") == 0)的分支就可以了,再自己实现一个类似format_mac的函数,从hdr_mac中取地址,并生成trace,trace文件中MAC地址不对的问题就解决了。

我估计生成nam的类中也有相似问题,不过懒得修改了,直接看trace分析得了。

Tags:

WiMAX MAC管理消息

Type     Message name     Message description     Connection
0     UCD     Uplink Channel Descriptor     Broadcast Fragmentable Broadcast
1     DCD     Downlink Channel Descriptor     Broadcast Fragmentable Broadcast
2     DL-MAP     Downlink Access Definition     Broadcast
3     UL-MAP     Uplink Access Definition     Broadcast
4     RNG-REQ     Ranging Request     Initial Ranging or Basic
5     RNG-RSP     Ranging Response     Initial Ranging or Basic
6     REG-REQ     Registration Request     Primary Management
7     REG-RSP     Registration Response     Primary Management
8     —     Reserved     —
9     PKM-REQ     Privacy Key Management Request     Primary Management
10     PKM-RSP     Privacy Key Management Response     Primary Management or Broadcast a
11     DSA-REQ     Dynamic Service Addition Request     Primary Management
12     DSA-RSP     Dynamic Service Addition Response     Primary Management
13     DSA-ACK     Dynamic Service Addition Acknowledge     Primary Management
14     DSC-REQ     Dynamic Service Change Request     Primary Management
15     DSC-RSP     Dynamic Service Change Response     Primary Management
16     DSC-ACK     Dynamic Service Change Acknowledge     Primary Management
17     DSD-REQ     Dynamic Service Deletion Request     Primary Management
18     DSD-RSP     Dynamic Service Deletion Response     Primary Management
19     —     Reserved     —
20     —     Reserved     —
21     MCA-REQ     Multicast Assignment Request     Primary Management
22     MCA-RSP     Multicast Assignment Response     Primary Management
23     DBPC-REQ     Downlink Burst Profile Change Request     Basic
24     DBPC-RSP     Downlink Burst Profile Change Response     Basic
25     RES-CMD     Reset Command     Basic
26     SBC-REQ     SS Basic Capability Request     Basic
27     SBC-RSP     SS Basic Capability Response     Basic
28     CLK-CMP     SS network clock comparison     Broadcast
29     DREG-CMD     De/Re-register Command     Basic
30     DSX-RVD     DSx Received Message     Primary Management
31     TFTP-CPLT     Config File TFTP Complete Message     Primary Management
32     TFTP-RSP     Config File TFTP Complete Response     Primary Management
33     ARQ-Feedback     Standalone ARQ Feedback     Basic
34     ARQ-Discard     ARQ Discard message     Basic
35     ARQ-Reset     ARQ Reset message     Basic
36     REP-REQ     Channel measurement Report Request     Basic
37     REP-RSP     Channel measurement Report Response     Basic
38     FPC     Fast Power Control     Broadcast
39     MSH-NCFG     Mesh Network Configuration     Broadcast
40     MSH-NENT     Mesh Network Entry     Basic
41     MSH-DSCH     Mesh Distributed Schedule     Broadcast
42     MSH-CSCH     Mesh Centralized Schedule     Broadcast
43     MSH-CSCF     Mesh Centralized Schedule Configuration     Broadcast
44     AAS-FBCK-REQ     AAS Feedback Request     Basic
45     AAS-FBCK-RSP     AAS Feedback Response     Basic
46     AAS_Beam_Select     AAS Beam Select message     Basic
47     AAS_BEAM_REQ     AAS Beam Request message     Basic
48     AAS_BEAM_RSP     AAS Beam Response message     Basic
49     DREG-REQ     SS De-registration message     Basic
50     MOB_SLP-REQ     sleep request message     basic
51     MOB_SLP-RSP     sleep response message     basic
52     MOB_TRF-IND     traffic indication message     broadcast
53     MOB_NBR-ADV     neighbor advertisement message     broadcast, primary management
54     MOB_SCN-REQ     scanning interval allocation request     basic
55     MOB_SCN-RSP     scanning interval allocation response     basic
56     MOB_BSHO-REQ     BS HO request message     basic
57     MOB_MSHO-REQ     MS HO request message     basic
58     MOB_BSHO-RSP     BS HO response message     basic
59     MOB_HO-IND     HO indication message     basic
60     MOB_SCN-REP     Scanning result report message     primary management
61     MOB_PAG-ADV     BS broadcast paging message     broadcast
62     MBS_MAP     MBS MAP message     —
63     PMC_REQ     Power control mode change request message     Basic
64     PMC_RSP     Power control mode change response message     Basic
65     PRC-LT-CTRL     Setup/Tear-down of long-term MIMO precoding     Basic
66     MOB_ASC-REP     Association result report message     primary management
67–255         Reserved     —

Tags:

cygwin无法安装gcc和XFree86的问题

一直用debian的VPS运行NS2的,因为我都是看trace。

今天修改了一个支持WIMAX的NS 2.33,想用nam看看动画,所以装了一个cygwin,准备编译NS2。

cygwin的安装镜像可以使用这个,速度很快 http://www.cygwin.cn/pub/

安装所需的一些包:XFree86/xorg, autoconf, make, patch, perl, gcc, gcc-g++, gawk, gnuplot, tar, gzip

然后就开始install了,结果cygcheck -c gcc的时候显示没有安装gcc。仔细查了一下,发现安装的时候选软件包,有gcc,但是体积更大的,是那个gcc-core。看了一下gcc的说明,只是一个update helper,估计实际用还是用gcc-core。

于是到install脚本里面搜索gcc,把“packages_base="gcc gcc-g++ gawk tar gzip make patch perl w32api"”修改成“packages_base="gcc-core gcc-g++ gawk tar gzip make patch perl w32api"”即可。

继续运行install,这会显示没有X,也就是说XFree86和xorg都没有安装。install脚本里面搜对X的检测,发现是先检测xorg,如果没有再检查XFree86。重新运行cygwin的setup,发现XFree86只能搜到一个字体文件,其他的开发包什么的在这个版本里面都去掉了。没法只好装xorg。

搜xorg,发现没有什么东西,因为下面的那个“hide obsolete packages”勾选要去掉,现在可以看到一堆软件列表了吧。根据install脚本里面的检测说明,勾选“xorg-x11-base xorg-x11-bin xorg-x11-bin-dlls xorg-x11-devel xorg-x11-libs-data xorg-x11-etc”,鉴于7.0版本那个体积到太小,怕有问题,选低版本的6.9,几M一个,放心一点。

安装完成后,就可以正式开始install ns2了。

Tags:

NS2中从应用层访问UM-OLSR的路由表

  做一个跨层优化的仿真,在应用层Application里面要访问OLSR的路由表项,结合底层路由信息做P2P的传输策略优化。OLSR使用的是UM-OLSR。

  根据UM-OLSR的说明,如果要对单个节点上的agent进行设置,可以使用如下代码

TCL代码
  1. set ra [$mobilenode agent 255]  
  2. $ra set use_mac_ true 
  3. $ra set tc_ival_ 6 

这说明,ra取到的是对应agent的引用,可以直接操作这个路由agent了。

  那么自己的application要怎么做就简单多了,把上面的mobilenode换成自己的node对象,然后运行上面的第一句TCL即可。如下所示:

cpp代码
  1. nsaddr_t node_addr_ = agent_->addr();  
  2. int node_id_ = OLSR::node_id(node_addr_);  
  3. char command[256];  
  4. sprintf(command, "set ra [$node_(%d) agent 255]", node_id_); //id与node_数组是对应的  
  5. tcl.eval(command);  
  6. const char* ref = tcl.result();  
  7. OLSR* olsr_agent_ = (OLSR*)tcl.lookup(ref); 

  取到application所在node的OLSR Agent了,剩下的就是访问OLSR_rtable以及其中的OLSR_rt_entry了,自己改造一下OLSR_rtable吧,o(∩_∩)o

Tags: