串口自动行转换
问题介绍
Linux 串口调用 write 函数发送 2 字节(0x0d 0x0a),使用逻辑分析仪和示波器看到实际发送了 3 字节(0x0d 0x0d 0x0a),也就是 \n
自动被转换为 \r\n
再发送的;
问题的解决
原来 Linux 串口默认为“规范模式”,会自动行转换;
使用如下参数后可将串口配置为“原始模式”:
1
2
3
//修改输出模式,原始数据输出
new_uart_options.c_oflag &= ~OPOST;
new_uart_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
扩展
串口还可配置等待时间和最小接收字符:
VTME 位数据设置为 150,表示串口无数据阻塞超时 15s;
1
2
3
//设置等待时间和最小接收字符
new_uart_options.c_cc[VTIME] = 150; /* 读取一个字符等待1*(1/10)s */
new_uart_options.c_cc[VMIN] = 0; /* 读取字符的最少个数为1 */
此外,linux 串口默认 LSB 方式,即 0b’00000001 发送顺序为 <起始> 1 0 0 0 0 0 0 0 <校验> <停止>
,可能是最通用的方式;
多线程下接收打印在发送打印前
问题介绍
线程1 接收帧并打印接收消息,线程 2 发送帧并打印发送消息;发送接收大量数据时,有时会先打印接收帧,然后才会打印发送帧;
1
2
3
4
5
6
7
//线程1
if(recv_frame()) //收到完整帧
printf_frame(); //打印接收帧
//线程2
send_frame(); //发送帧
printf_frame(); //打印发送帧
问题的解决
发生打印顺序颠倒时执行顺序是这样的:线程 2 发送帧 –> 线程 1 打印接收帧 –> 线程 2 打印发送帧;
代码按如下方式修改即可:
1
2
3
4
5
6
7
//线程1
if(recv_frame()) //收到完整帧
printf_frame(); //打印接收帧
//线程2
printf_frame(); //打印发送帧
send_frame(); //发送帧
链表节点被其他线程删除导致尾插节点丢失
问题介绍
线程 1 会删除被标记过时的节点,线程 2 尾插节点有时会导致节点丢失;
1
2
3
4
5
6
//线程1
del_old_node(); //删除旧节点,比如链表为 H->1->2,删掉 1 后剩下 H->2
//线程2
end_node = scan_end_node(); //查找到最后的节点
end_node->next = new_node; //尾插新节点
问题的解决
发生尾插节点丢失时执行顺序是这样的:线程 2 查找到最后的节点 e –> 线程 1 删除过时的节点 e –> 线程 2 在节点 e 后插入新节点 n;
第一条链:H->1->2->e 变为 H->1->2;
第二条链:(null) 变为 e(内容随机,地址存在)->n;
代码中操作链表的地方加锁即可:
1
2
3
4
5
6
7
8
9
10
//线程1
do_node_mutex.lock();
del_old_node(); //删除旧节点,比如链表为 H->1->2,删掉 1 后剩下 H->2
do_node_mutex.unlock();
//线程2
do_node_mutex.lock();
end_node = scan_end_node(); //查找到最后的节点
end_node->next = new_node; //尾插新节点
do_node_mutex.unlock();
扩展
互斥锁是服务于共享资源的,一般在线程中成对出现,同一时间只能有一个线程访问该资源;
信号量是服务于多线程间执行的逻辑顺序的,可在线程 1 中 wait,在线程 2 中 post,根据初值可设置同一时间最多有 n 个线程访问;
C 动态库无法被 C++ 工程链接
问题介绍
C++ 工程用到了动态库 t.so,链接时报错找不到 t.so 中的函数 tt;
1
2
3
4
5
6
7
8
9
10
11
12
//t.c(动态库 t.so 源文件)
#include "t.h"
void tt(void){...}
//t.h(动态库 t.so 头文件)
void tt(void);
//demo.cpp(C++ 工程)
#include "t.h"
void demo(){
tt();
}
问题的解决
原因是因为 C 头文件和 C++ 头文件不通用,C++ 使用 C 头文件时需要加上关键字 extern "C"{...}
;
更加建议在动态库 C 头文件中加入如下宏(C 系统库头都有类似或者其他特殊处理):
1
2
3
4
5
6
7
8
9
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
可执行文件 rpath 不合理
问题介绍
rpath 是可执行文件优先寻找依赖库的目录;
比如在 ubuntu 上交叉编译生成可执行文件时,cmake 脚本指定链接了 ../lib/a.so,但是希望可执行文件在 arm 上某些目录上查找动态库;
问题的解决
-
cmake 编译 target 时添加:
给 target 设置 BUILD_RPATH 属性;
1 2
SET_TARGET_PROPERTIES(demo PROPERTIES BUILD_RPATH ${CMAKE_BINARY_DIR})
-
不操作可执行文件的方法:
事先给环境变量 LD_LIBRARY_PATH 赋值;
1
export $LD_LIBRARY_PATH=/lib/test
-
操作可执行文件的方法:
需要 patchelf 工具,安装办法
sudo apt install binutils
;$ORIGIN/
表示可执行文件所在路径;1
patchelf --set-rpath '$ORIGIN/:/lib/:/usr/lib' demo
命令行传入密码让 sudo 提权
问题介绍
sudo xxx 执行时需要交互式输入密码,但有的时候需要脚本自动填写密码;
问题的解决
默认情况下 sudo 不接受标准输入方式填写密码;
解决办法:
1
echo your_password | sudo -S xxx
扩展,需要组合延迟输入的可以使用括号 “() | xxx”,常见的如更改 root 密码的命令: |
1
(echo abcd;sleep 1;echo abcd) | passwd
.gitignore 失效
问题介绍
修改 .gitignore 文件后,发现追踪的文件列表不符合预期;
问题的解决
更新 git 的缓存区;
解决办法:
1
2
3
4
5
6
7
8
# 在 git 项目根目录,清除缓存区
git rm -r --cached .
# 重新添加文件到 git 追踪
git add .
# 提交新的忽略文件
git commit -m "update .gitignore"
删除远程 git 仓库所有历史提交记录
问题介绍
某些情况下,希望删除远程 git 仓库的所有历史提交记录来保持仓库干净;
问题的解决
解决办法:新建分支 latest_branch,替换 master 分支;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1.Checkout
git checkout --orphan latest_branch
# 2. Add all the files
git add -A
# 3. Commit the changes
git commit -am "commit message"
# 4. Delete the branch
git branch -D master
# 5.Rename the current branch to master
git branch -m master
# 6.Finally, force update your repository
git push -f origin master
程序没有打印信息怎么跟踪调试
问题介绍
某些情况下,程序没有打印信息输出,怎么跟踪程序执行情况?
问题的解决
解决办法:使用 strace
命令;
1
strace ./xxx