在一些嵌入式系统中,如果需要采集长时间的全量日志或者系统性能数据,内存将成为一个瓶颈。是否可以将嵌入式系统中的数据透传到外部机器上,这样内存和性能都没有限制。通过此想法实现了channel这个工具,主要作用就是消息透传。
项目地址:https://github.com/caibingcheng/channel.git
使用
本地使用
最基本的使用方法,在服务端运行:
1
2
| $ echo "Hello, World!" | channel -s
Hello, World!
|
那么同一ip下的客户端将接收到:
1
2
| $ channel
Hello, World!
|
透传日志
如果是将嵌入式系统中的信息传递到本地主机,那么在嵌入式系统中:
1
2
3
4
5
6
7
8
| $ echo "Hello, World!" | channel -s
Hello, World!
# or transmit top command output
$ top -n 1000 -d 1 -b | channel -s
# or transmit application output
$ <your app> 2>&1 | channel -s
|
本地主机可以得到:
1
2
3
4
5
| # just receive and write to file, because the host disk space is sufficient
$ channel -i <embedded system ip> | tee log.txt
# or receive and process by another command, such as python script
$ channel -i <embedded system ip> | <another command>
|
分享实时日志
引申出的一个应用场景是可以与其他工程师共同分析日志,比如在你的机器上:
1
2
3
4
5
| $ echo "Hello, World!" | channel -s
Hello, World!
# or transmit log from embedded system
$ channel -i <embedded system ip> | channel -s
|
在其他工程师机器上就可以实时获取你分享的内容:
1
2
| $ channel -i <your machine ip>
Hello, World!
|
安装
该项目使用了一些C++17特性,因此需要至少支持C++17的GCC。
1
2
3
| $ cd channel
$ make
$ sudo make install
|
或者交叉编译为arm版本:
1
2
3
| $ cd channel
$ make CROSS_COMPILE=aarch64-linux-gnu-
$ sudo make install
|
原理
实现该工具的基本想法就是管道 + TCP。
通过管道可以实现下面这样的用法,不会破坏原有程序运行的结构。
1
| <your app> | channel -s
|
使用TCP则是考虑透传数据完整性是第一保证,其次是性能。
Linux中的管道可以将前一个输出变为下一个的输入。在channel中使用该特性的代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| void send_message(bool is_drop) {
std::string message = "";
auto &bstream = std::cin;
while (std::getline(bstream, message) && !stop_) {
if (!message.empty()) {
message += "\n";
Log::debug("Message %lu Bytes", message.size());
Log::raw("%s", message.c_str());
Message msg;
msg.body.length = message.size();
msg.body.generate_timestamp = Message::timestamp_us();
message = std::string(reinterpret_cast<char *>(&msg), sizeof(Message)) + message;
send_message_(message, is_drop);
}
}
Log::debug("Waiting for sender to stop");
{
std::unique_lock<std::mutex> lock(messages_mutex_);
messages_condition_.wait(lock, [this] { return messages_.empty() || stop_; });
}
Log::debug("Sender stopped");
}
|
目前实现相当于直接从std::cin
中读取内容,可能存在一些兼容性问题。不过实现channel是考虑的主要应用场景就是日志透传,基本都是ASCII字符,因此一般也不会出现兼容性问题。
小结
这个工具的基本想法是很朴素的。最开始的来源是,我做了一个性能统计和分析的工具,需要采集雷达系统上的top
命令的输出,有些场景可能需要采集几十个小时,这时候数据放雷达中是不显示的,内存受限。因此可以通过sshpass + ssh
工具的组合,在本地机器上采集。但是这样需要密码认证,自动化脚本时则需要明文密码,不太适合,因此才考虑了直接通过无授权的日志透传,来将雷达中的信息传递到本地电脑上。
开始实现的版本中,我考虑了“既然实现了字符的透传,不如实现一下文件透传”,而后还是删除了文件透传的功能,文件透传必要性不高,而且一般都是用scp、rsync
工具,对其使用方法已经比较熟悉了。而字符透传的工具比较少,通过ssh协议似乎也可行,但我不太熟悉,且需要用户密码,不适合和其他同事的电脑分享。因此索性专一channel的功能,只做日志透传,不需要授权认证。