MongoDB备份与还原
备份命令
1 | mongodump -h <ip>:<port> -d <数据库名> -u root -p pass -o <备份目录> [--gzip] |
例如
1 | TRADE_DATE=`date "+%Y%m%d"` |
其中--gzip
表示压缩备份文件,对于压缩
还原备份
还原前我们先启动一个新的MongoDB的容器,把容器的27017端口映射到宿主机的47017。
1 | docker run -dit -p 47017:27017 --name=mongo_test -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=pass mongo:3.6.2 |
执行还原命令
1 | mongorestore -h <ip>:<port> -d <数据库名> -u root -p pass <备份目录> --drop --gzip |
例如
1 | mongorestore -h 127.0.0.1:47017 -u root -p pass /var/lib/mongodb/backup/20230709 [--drop] [--gzip] |
其中--drop
的意思是如果要还原的数据库已存在,则先删除。
--gzip
表示还原的是压缩备份,如果备份时没加--gzip
参数则还原时也不加。
Linux时间同步
在NTP(Network Time Protocol)中,ntpdate
是一个命令行工具,用于将系统时钟与网络上的NTP服务器进行同步。它可以通过向NTP服务器发送请求并根据响应调整本地时钟来实现时间同步。
CentOS上通常自带ntpdate
,如果找不到ntpdate
命令可以通过下面命令安装:
1 | yum install -y ntpdate |
Ubuntu上通常没有自带ntpdate,可以通过下面命令安装:
1 | apt install ntpdate |
常用的同步命令格式
1 | ntpdate [-u] <server> |
-u
参数表示使用非特权(unprivileged)模式进行时间同步。当以非特权模式运行时,ntpdate
不需要超级用户权限来执行同步操作。这对于普通用户来说很有用,因为他们通常没有足够的权限来使用特权模式。
通过使用 -u
参数,ntpdate
会使用一个高位端口(1024以上)来发送NTP请求,而不是使用标准的NTP端口(123号端口)。在大多数操作系统中,只有特权用户才能使用低位端口,因此非特权模式是普通用户进行时间同步的常见选择。
常用的ntp服务器
1 | cn.pool.ntp.org 中国开源免费NTP服务器 |
控制台光标控制——在屏幕指定位置打印
1 | class Screen { |
使用条件变量(condition_variable)进行线程间同步
BusyWaitableCondition
是忙寻版的可等待条件
WaitableCondition
是睡眠版的可等待条件
两者实现相同功能,只是忙寻版会占用一个cpu core,而睡眠版会陷入睡眠在需要时才被唤醒继续执行。
1 |
|
上面的例子中有定义Reset
方法,但是没有演示,它的作用是将条件设置为否,以便可以再次等待,然后可以重新触发。
如果只需要等待一个一次性事件而不需要Reset
方法那么可以用std::promise
+std::future
进行更简单的实现。
1 | class WaitableEvent { |
sscanf的奇技淫巧
sscanf是C语言库函数,作用是从字符串中进行格式化解析。
例如在读文本文件时先用fgets把整行读入buffer再用sscanf进一步解析buffer的内容。
头文件
1 | #include <stdio.h> |
1 | #include <cstdio> |
函数原型
1 | int sscanf(const char *str, const char *format, ...); |
参数
- str – 以’\0’结尾的字符串,解析的数据源。
- format – 格式化字符串,描述解析的规则
- args… – 对应format参数,提供需要读取的变量的地址
返回值
返回读取到的参数的个数。
当没有读入任何数据时,可能是因为走到了字符串的结尾,也可能是因为当前将要读入的字符串无法转换为指定的格式。
如果因为读到了字符串结尾则返回EOF,即-1,如果是因为其他原因没有读入任何参数,则返回0。
样例
1 | int i; |
说明
%d
表示读取一个整数
%f
表示读取一个浮点数
%lf
表示读取一个双精度浮点
%s
表示读取一个字符串
%c
表示读取一个字符
在format中空格表示跳过任意多的空白字符(空白字符包括' '
, '\t'
,'\r'
,'\n'
)
%d %f %lf %s
这些格式自带跳过空白字符即使前面没有空格。而%c
不会自动跳过空白字符。
高级用法
指定读取字符串的长度
1 | char buf[5]; |
我们的buf只有5个字节大小,考虑到sscanf读取的字符串会在结尾加上’\0’,所以我们最多读取4个自己,可以在%
和s
中间加上希望读取的长度。
这同样适用于%d %f %lf
仍然表示最多读取的字符串长度,只是会在读取后会将字符串转换成相应类型的数据。
丢弃读取内容
在%
后面加上*
可以丢弃当前%
表示的格式读取到的内容,即后面的参数列表不需要传入接收此项的数据地址。
1 | char buf[5]; |
丢弃了4个字符,再跳过任意多空白字符,然后读入一个字符串,所以这时读入的buf里只有"o"
读取指定的字符
通过%[]
指定读取的字符,可逐个列出或通过-
指定范围,也可以指定多个范围。
1 | char buf1[128]; |
读取除指定字符外的字符
和%[...]
类似,通过%[^...]
可以反向指定需要读取的字符,即除了什么字符以外的所有字符。
非常适合读取用某种字符分割的字符串。
1 | char buf1[128]; |
获取当前读取到的位置
通过在format中需要确定的位置插入%n
获取
1 | char buf1[128]; |
n1 = 5, n2 = 11,注意%n实际上不是表示前一个%
读入了多少字符,而是表示当前已经读到的位置,但是如果有需要我们可以通过这个参数算出每个参数读入了多少字符。
综合应用
1 | const char* p = "Hello,World,123.45,, Hello world ,ttt"; |
这里演示了读入以,
分割的多字段的情况。其中有一点需要说明,while条件里的sscanf我们是期望读当前位置到下一个,
前的字符串,而当r==0时,说明当前就指在下一个,
上,此时读入了0个参数,而buf中会保留这之前的内容,所以将其手动置空,而n也同样没有被修改,所以我们也将其置0,以便后面的输出和移动可以用相同的代码统一处理。
通过这种方式sscanf完全可以替代strtok,而且避免了strtok使用了内部静态变量带来的线程安全问题。
LINK_DIRECTORIES和rpath-link的区别
在CMakeList.txt
中,LINK_DIRECTORIES和rpath-link都是用来指定链接库路径的,但是有区别,看来具体例子
1 | # 设置库路径 |
在注释(1)处的目录下面有libama.so
库和其依赖的若干.so
如果只加注释(1)处的路径而不加注释(2)处的-rpath-link
参数,则会报错找不到通过ama间接依赖的一系列.so的问题,
如果只加注释(2)处的-rpath-link
参数而不加注释(1)处的路径,则会报错找不到ama库本身的问题。
解决方案是要么两处都加上,要么只加(1)处的路径,但是需要在TARGET_LINK_LIBRARIES
中写出所有通过ama间接依赖到的库名字,这样就都会通过(1)处的路径找到它们并使它们参与到链接中。
总之,直接链接的库只会通过(1)处指定的路径查找,而间接链接到的库只会通过(2)处指定的路径进行查找。
gcc/g++生成高可读性汇编
有时我们想查看C/C++代码对应的汇编代码,可以通过gcc/g++
的编译选项-S
来生成,但这样的代码可读性较差。
可以增加-fverbose-asm
来增加可读性
1 | g++ -O0 -o temp.s temp.cpp -std=c++17 -g -S -fverbose-asm |
还有另一个方法,先生成目标文件再用objdump
进行反汇编,我发现这样生成的汇编代码可读性更好。
1 | g++ -O0 -o temp temp.cpp -std=c++17 -g && objdump -S -t -D temp > temp.s |
C++中监视线程卡死并自动崩溃退出 WatchDog
之前写过在Python中监视卡死崩溃退出并打印卡死处的调用堆栈
在此记录一下C++的版本,不过没有在代码层面实现堆栈打印,可以通过core dump和gdb来查看崩溃时的堆栈
1 | // WatchDog.h |
1 | // WatchDog.cpp |