目录
Linux 编程环境
vim 使用简介
1.普通模式下按下 I 键进入插入模式,再按下 Esc 键返回普通模式。
2.建立文件:“vim 文件名”
3.退出vim:普通模式下输入“: wq ” 保存并退出当前文件。“: q! ”操作可以实现不保存强制退出。
4.vim 编辑文本:
- 移动光标 h、j、k、l
- 删除字符 x、dd、:普通模式下 按下 x 删除单个字符;使用dd 命令删除一整行
- 撤销与重做 u、Ctrl+R:使用 u 命令撤销可以取消之前的删除等操作;使用 Ctrl+R 作为重做命令(反撤销)
- 复制粘贴 p、y:使用 yy 命令拷贝一整行
GCC编译器
1.使用“ g++ 文件名 ” 命令编译 cpp 文件,默认生成可执行文件 a.out 。“ ./a.out ”查看运行结果,./表示本目录下,不写则命令无效;如果希望生成指定的可执行文件名,使用选项 -o,例如“ g++ -o test hello.cpp ”。“ ./test ”查看运行结果。
2.多个文件编译:GCC 可以自动编译多个文件,不管是目标文件还是源文件,都可以使用同一个命令编译到一个可执行文件中。
- “ g++ -o test string.cpp main.c ”将两个源文件中的程序编译成一个可执行文件,test
- “ g++ -c string.cpp main.cpp ”先生成目标文件然后进行链接,“ g++ -o test string.o main.o” 生成 test
3.多文件工程的编译
- 命令行:“ g++ -o cacu add/add_int.cpp sub/sub_int.cpp main.cpp” 将当前目录下的 main.cpp 与当前目录下文件夹 add 中的 add.cpp 和文件夹 sub 中的 sub.cpp 一同编译生成可执行文件 cacu
- 多文件的 Makefile:Makefile 是进行程序编译时使用的编译配置文件,相当于提前将多条编译语句先写入一个 makefile 文件中
GDB 调试器
1.常用调试命令
1.编译可调式程序:要使用 GDB 进行调试,在编译程序的时候需要加入 -g 选项 “ g++ -o test string.cpp main.c -g ”,GCC会向程序中加入“楔子”,GDB 能够利用这些楔子与程序交互。
2.使用 GDB 调试程序:“ gdb 要调试的文件名 ”,此处文件名为可执行文件,如 test 、a.out
3.打印代码内容 list:简写“ l ”
4.设置断点 break:“ b 行数 ”;
- break 行号:程序停止在设定的行之前
- break 函数名称:程序停止在设定的函数之前
- break 函数 if 条件:条件断点设置指令,如果条件为真,程序到达指定行或函数时停止 “ b 行号 if i==2 ”
- 显示当前所设置的断点信息:“ info break ”
- 删除断点:“ delete b 断点编号 ”
- 清除所有断点:“ clean ” ;清除指定位置的断点:“ clean 行号 ”
5.运行程序“ run ”;
6.显示变量:“ display i ”:设置监测点每次代码运行至此停止时,会显示变量 i 的值
7.中断后继续运行:“ c ”
8.打印变量或者表达式的值使用 print 命令,简写为 p
- 可以用 p 打印一个结构体中各个成员的值“ p 结构体变量名 ”
- 可以使用 p 计算代码中定义的函数调用的返回值,例 “p sum(3) ”
9.查看变量类型:
- “ whatis 变量名 ”
- “ ptype 变量名 ” 可以查看结构体变量的详细定义
10.单步调试:
- 单步跟踪 next 命令:简写“ n ”
- 进入函数体 step 命令:简写“ s ”
- 退出并返回调用的函数 finish 命令:如果已经进入某个调用的函数,想退出函数的运行,返回到调用的函数中
11.调用路径 backtrace:简写“ bt ”,打印函数的调用路径,生成一个顺序列表,显示函数从最近到最远的调用过程,同时会显示调用函数的传入参数
12.多线程 thread:多线程由于执行过程中的调度随机性,所以调试时要
- 先获得线程的 ID 号
- 然后转到该线程进行调试
调试完程序后使用 q 命令退出。
2.调试多线程程序命令
2.1 多线程调试,最重要的几个命令:
info threads 查看当前进程的线程。
GDB会为每个线程分配一个ID, 后面操作线程的时候会用到这个ID.
前面有*的是当前调试的线程.
thread <ID> 切换调试的线程为指定ID的线程。
break file.c:100 thread all 在file.c文件第100行处为所有经过这里的线程设置断点。
set scheduler-locking off|on|step
在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,
怎么只让被调试程序执行呢?
通过这个命令就可以实现这个需求。
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step 在单步的时候,除了next过一个函数的情况
(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,
只有当前线程会执行。
thread apply ID1 ID2 command 让一个或者多个线程执行GDB命令command
thread apply all command 让所有被调试线程执行GDB命令command。
2.2.使用示例:
线程产生通知:在产生新的线程时, gdb会给出提示信息
(gdb) r
Starting program: /root/thread
[New Thread 1073951360 (LWP 12900)]
[New Thread 1082342592 (LWP 12907)]---以下三个为新产生的线程
[New Thread 1090731072 (LWP 12908)]
[New Thread 1099119552 (LWP 12909)]
查看线程:使用info threads可以查看运行的线程。
(gdb) info threads
4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
注意,行首为gdb分配的线程ID号,对线程进行切换时,使用该ID号码。
另外,行首的星号标识了当前活动的线程
切换线程:
使用 thread THREADNUMBER 进行切换,THREADNUMBER 为上文提到的线程ID号。
下例显示将活动线程从 1 切换至 4。
(gdb) info threads
4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb) thread 4
[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0 0xffffe002 in ?? ()
(gdb) info threads
* 4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
以上即为使用gdb提供的对多线程进行调试的一些基本命令。
另外,gdb也提供对线程的断点设置以及对指定或所有线程发布命令的命令
3.使用 core dump 文件调试
ulimit -c 查看默认是否生成 coredump 文件 若为 0 则默认不产生core dump
ulimit –c unlimited 打开生成 core dump
gdb ./a.out core.1025 用 gdb 打开 core 文件
gdb 中会打印出了出问题的代码行如 result = a/b,并给出可能的出错原因。如:“segment fault” 或 “Program terminated with signal 8, Arithmetic exception”表示应用程序是因为接收到Linux内核发出的Signal 8信号量而终止执行,Signal 8是SIGFPE,即浮点数异常。
通过 bt 可以打印函数调用栈,辅助分析出错原因。
4.Linux 下内存泄漏检测
如果编译后 gcc 提示 warning 可能有内存泄漏,
使用内存检测工具 valgrind,valgrind --leak-check = full ./a.out
打印出的 heap summary 信息中:
==6118== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==6118== at 0x4024F20: malloc (vg_replace_malloc.c:236)
==6118== by 0x8048724: GetMemory(char*, int) (in /home/netsky/workspace/a.out)
==6118== by 0x804874E: main (in /home/netsky/workspace/a.out)
是在main中调用了GetMemory导致的内存泄漏,GetMemory中是调用了malloc导致泄漏了100字节的内存。基本可以定位错误。
打印出的 leak summary 信息统计了所有的内存泄漏。
Linux 网络编程
进程的产生
1.使用 getpid() 函数返回当前进程的 ID 号,getppid() 返回当前进程的父进程的 ID 号,原型如下,pid_t 是一个 tydef 类型,定义为 unsigned int
#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void);
pid_t getppid(void);
2.进程复制 fork():以父进程为蓝本复制一个进程,返回进程 ID,失败返回 -1。特点是执行一次,返回两次,在父进程中返回的是子进程 ID 号,子进程中返回 0 。
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
3.system() 调用 shell 外部命令在当前进程中开始另一个进程,成功时返回进程状态值。
#include<stdlib.h>
#include<stdio.h>
int main()
{
int ret;
printf("系统分配的进程号是:%d\n", getpid());
ret = system("ping www.baidu.com -c 2");
printf("返回值为:%d\n", ret);
return 0;
}
4.进程执行 exec() 函数系列:使用 fork() 和 system() 的时候,系统都会建立一个新的进程,而原来的进程还会存在,直到用户显示的退出;而 exec() 函数会用新进程代替原有的进程,系统从新的进程运行,新进程的 PID 会与原来进程的 PID 值相同。
5.所有用户态进程的 初始进程 init :init 进程是所有进程的祖先,其他的进程都是由 init 进程直接或者间接 fork() 出来的。
- linux 下可使用命令 pstree 来查看系统中运行的进程之间的关系