一、GDB常用命令
想用gdb调试C或C++程序,编译时需要加-g选项,编译出的文件为debug状态(如果不加则是release状态),且不可以加-O选项进行优化。
命令 | 简写 | 解释 |
set args | 设置程序传递的参数 例:./demo -v value -o a.out 传递了四个参数 | |
break | b | 设置断点 例:b 20 在20行打断点 |
run | r | 开始运行程序(遇到断点停止) |
next | n | 执行当前行语句(如果当前为函数调用,则不会进入函数内部) |
step | s | 进入函数内部执行一行语句 (如果为第三方库,进不去) |
p | 打印某个变量的值 例:p i 打印当前变量i的值 | |
continue | c | 继续执行到下一个断点 |
set var | 设置变量的值(针对已有变量) 假设程序中存在变量i set var i=100 将变量i设置为100,然后继续调试 | |
quit | q | 退出gdb |
backtrace | bt | 查看函数调用栈 |
二、GDB调试core文件
当执行一个程序时,可能会因为内存越界而崩溃。但此时没有错误信息,我们无法调试。通过生成的core文件,可以看到导致崩溃的具体代码。
默认系统是不生成core文件的,可以通过以下命令打开
ulimit -c unlimited
再运行后,即使崩溃,也会在当前目录生成core.XXX的文件。调试方式如下:
gdb core.XXX ./demo
此时在gdb中会显示崩溃行的具体代码。
三、GDB调试正在运行的程序
想调试正在运行的程序,需要通过以下命令找到程序的PID。
ps -ef | grep 运行程序名
假设运行程序名为demo,PID为8854,那么通过以下命令可以调试(程序也会暂停):
gdb -p 8854 demo
四、静态库与动态库
(注意:[ ]表示可选项,{ }表示必选项)
目的 | 选项 | 命令 |
链接.h文件 | -I | g++ main.cpp [ -I .h所在目录 ] |
编译静态库 | -c | g++ -c myKu.cpp -o libmyKu.a (注意:动态库一般以.a作为尾缀,lib作为前缀) |
编译动态库 | -fPIC -shared | g++ -fPIC -shared myDKu.cpp -o libmyDKu.so (注意:动态库一般以.so作为尾缀,lib作为前缀) |
链接静态库 | -L -l | g++ main.cpp [ -L 库所在路径 -l 库名称 ] (注意:不是完整的库名,而是去掉前缀lib和尾缀.so或.a的库名) |
链接动态库 | -L -l | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库所在目录 (注意:以上语句是将动态库所在目录包含到库路径中) g++ main.cpp [ -L 库所在路径 -l 库名称 ] (注意:不是完整的库名,而是去掉前缀lib和尾缀.so或.a的库名) |
五、tmux开启多个窗口
首先使用tmux命令打开多行窗口,然后在以下选项中选择:
1.按住Ctrl+B,再按 " 表示垂直开启另一个窗口
2.按住Ctrl+B,再按 % 表示水平开启另一个窗口
(切换窗口Ctrl+B,再按方向键)(退出则输入exit)
六、时间
函数声明 | 头文件 | 解释 |
time_t time(0) | #include<time.h> | 返回从1970年到现在的秒数 |
tm* localtime(time_t *) | #include<time.h> | 将一个time_t类型变量转换为tm结构体类型变量 (线程不安全) |
localtime_r(time_t *, tm*) | #include<time.h> | 将一个time_t类型变量转换为tm结构体类型变量 (线程安全) |
time_t mktime(tm *) | #include<time.h> | 将一个tm结构体类型变量转换为time_t类型变量 |
int gettimeofday(timeval *, 0) | #include<sys/time.h> | 一般用于计算时间间隔 注:timeval为一个结构体 有tv_sec和tv_usec两个成员 tv_sec表示秒 tv_usec表示微秒 (10^-6) |
sleep(int n) | #include<unistd.h> | 程序暂停n秒 |
usleep(int n) | #include<unistd.h> | 程序暂停n微秒 |
七、进程通信方式
7.1 管道
管道方式可以实现:通过一个读文件描述符来读取数据
通过一个写文件描述符写输入数据
int pipefd[2];// 0表示读,1表示写
int pipe(pipefd);
释放方式举例如下:
int pipefd[2];
pipe(pipefd);
//读
char buf;
read(pipefd[0], &buf, 1);
close(pipefd[0]);
//写
int val[10] = 666;
write(pipefd[1], val, 10);
close(pipefd[1]);
7.2 共享内存
创建共享内存,让两个进程同时操作其中数据。(需自己保证线程安全)
(ipcs 命令:查询共享内存)
7.2.1 shmget函数
shmget()函数用于获取/创建共享内存(如不存在,则创建)
int shmget(key_t key, size_t size, int shumflg);
//例:int shmid = shmget(0x5678, sizeof(共享内存类型), 0777 | IPC_CREAT);
返回值int:成功返回共享区的id,不成功返回-1
key:共享内存唯一标识,一般为十六进制,比如:0x5678
size:共享内存的大小,单位为字节
shmflg:共享内存的权限,八进制数,比如:0777 | IPC_CREAT
IPC_CREAT 表示 如果不存在,则创建共享内存。
(当获取时,shmflg会被忽略)
7.2.2 shmat函数
shmat()函数用于连接共享内存
共享区数据类型指针 shmat(int shmid, 0, 0);
//例:Person* p = (Person *)shmat(int shmid, 0, 0);
返回值:共享内存的指针
shmid:共享内存的id
(第二个、第三个参数一般为0)
7.2.3 shmdt函数
shmdt()函数用于分离共享内存
int shmdt(共享内存指针);
//例:shmdt(p);
返回值:0表示分离成功,-1表示分离失败
7.2.4 shmctl函数
shmctl()函数用于删除共享内存
int shmctl(共享内存地址, IPC_RMID, 0);
//例:shmctl(0x5678, IPC_RMID, 0);
返回值:0表示删除成功,-1表示删除失败
7.3 循环队列
进程间基于循环队列的通信方式,就是将多个共享内存区域放入循环队列进行管理。
7.4 信号量
包含头文件#include<semaphore.h>,在编译时,加上-pthread选项
函数 | 解释 | |
创建 | (sem_t t) sem_init(sem_t* tp, int pshared, int value) | tp为信号量指针 pshared为0表示线程共享 为1表示进程共享 value是信号量初始值 |
P操作 | sem_wait(sem_t* tp) | 信号量减1 当信号量等于0时,阻塞,直到信号量大于0 |
V操作 | sem_post(sem_t* tp) | 信号量加1 任何时候都不阻塞 |
销毁 | sem_destroy(sem_t* tp) | 使用完毕,及时销毁 |
八、同步与互斥
8.1 同步
同步是多个线程或进程按照预期顺序来依次执行。(生产者-消费者)
8.2 互斥
互斥是同一时刻,多个线程或进程只能有一个访问共享区域。(写问题)
8.3 区别
他们关注的重点不同。
同步关注的是:多个线程或进程的执行顺序。
互斥关注的是:多个线程或进程的操作彼此独立,互不影响。