欢迎大家关注我的公*号:embedded_bug
这里的嵌入式Linux环境是指非标准Linux发行版环境,比如通过buildroot创建的,相比于标准的Linux发行版比如ubuntu,debian,fedora,系统比较简陋,提供的库很有限,而且系统的各种配置文件和配置方式与标准Linux发行版差别很大,总之,这里的嵌入式Linux只保证系统能够基本的启动运行起来。
上图是现在编程语言的流行度排名,别看这么多语言,真正适合在嵌入式Linux中应用的并不多。
直接说结论,个人推荐:go>C++(11)+boost>c,其他语言目前还都不太适合。
本篇文章先讨论C和C++,下面一篇文章讨论go及其他语言。
一、C语言,迫不得已的选择
C语言作为最基本的编程语言,只要是个嵌入式Linux环境肯定都是支持的,但是同样使用C的问题很多,包括:
-
语言比较老旧,很多高级语言的特性比较缺乏,程序写起来比较费时间,容易出错,代码行数也多。
-
基础C库功能太少,需要什么新功能还得去找相当的函数库,然后交叉编译,测试函数库功能怎么样,是否满足需求,费时费力。
总之,C语言的问题是库少,写起来费事,容易出错。优点是程序运行速度快,空间占用小。
二、C++(基于11标准,配合boost库),不能用go时候的选择
选择C++有两个条件,基于C++11标准编写程序,并配合boost库。如果还是用C的思想在用C++,那还不如直接用C。使用C++,一定要将C++当成一个不同于C的全新语言使用,否则发挥不出C++的优势。
C++11相比于之前的C++是巨大的进步,并且gcc4.8.5的版本已经能够完整支持C++11了。C++11的特性包括自动类型推导,自动指针,lamba表达式等等。代码写起来更流畅,更精简了。唯一的缺点就是太复杂了,否则也就没go什么事了。
boost库为什么是一个必选项呢,因为boost库实在太强大了,嵌入式开发百分之九十以上的需求都可以通过boost库实现,完全不需要借助第三方库,包括串口操作boost库中都包含了。并且boost库大部分是头文件,交叉编译出来的so文件也并不大,占用不了太多空间。
https://www.boost.org/doc/libs/1_76_0/
看下boost的文档,你就知道boost库的功能有多强大。
C++11+boost库的缺点就是太复杂,学习起来比较困难,不过真要用熟了,绝对就成了C++大师了,跳槽升职加薪迎娶白富美走向人生巅峰不是梦!
下面列举几个C++ boost库的代码示例
用boost库解析命令行参数:
using namespace std;
namespace bpo = boost::program_options;
namespace blog = boost::log;
namespace baio = boost::asio;
// 解析命令行参数
int initFlag(int argc, char* argv[], string &configDir, string &logDir, int &logLevel)
{
//步骤一: 构造选项描述器和选项存储器
//选项描述器,其参数为该描述器的名字
bpo::options_description opts("all options");
//选项存储器,继承自map容器
bpo::variables_map vm;
//步骤二: 为选项描述器增加选项
//其参数依次为: key, value的类型,该选项的描述
opts.add_options()
("configdir", bpo::value<string>()->default_value("./"), "set config file dir")
("logdir", bpo::value<string>()->default_value("./"), "set log file dir")
("loglevel", bpo::value<int>()->default_value(4), "set log level 0-6 panic fatal error warn info debug trace")
("help,h", "this is a log test program");
//步骤三: 先对命令行输入的参数做解析,而后将其存入选项存储器
//如果输入了未定义的选项,程序会抛出异常,所以对解析代码要用try-catch块包围
try
{
//parse_command_line()对输入的选项做解析
//store()将解析后的结果存入选项存储器
store(parse_command_line(argc, argv, opts), vm);
}
catch(...)
{
cout << "command line param is not correct" << endl;
cout << opts << endl;
return -1;
}
//步骤四: 参数解析完毕,处理实际信息
if(vm.empty()) //这里不会为空,因为设置了默认值
{
}
//count()检测该选项是否被输入
if(vm.count("help") )
{//若参数中有help选项
//options_description对象支持流输出, 会自动打印所有的选项信息
cout << opts << endl;
return 1;
}
//variables_map(选项存储器)是std::map的派生类,可以像关联容器一样使用,
//通过operator[]来取出其中的元素.但其内部的元素类型value_type是boost::any,
//用来存储不确定类型的参数值,必须通过模板成员函数as<type>()做类型转换后,
//才能获取其具体值.
if(vm.count("configdir") )
{
configDir = vm["configdir"].as<string>();
}
if(vm.count("logdir") )
{
logDir = vm["logdir"].as<string>();
}
if(vm.count("loglevel") )
{
logLevel = vm["loglevel"].as<int>();
}
return 0;
}
用boost库进行日志记录,同时记录至终端和文件中,并且文件支持自动分割,自动删除:
using namespace std;
namespace bpo = boost::program_options;
namespace blog = boost::log;
namespace baio = boost::asio;
// 初始化日志,日志级别0-6 panic fatal error warn info debug trace
int initLog(string logPre, string logdir, int logLevel)
{
if ((logdir.length() > 0) && (*(--logdir.end()) == ':'))
{
logdir += "/";
}
// 设置日志同时输出至文件和标准输出
auto console_sink = blog::add_console_log(
std::cout,
blog::keywords::format = (blog::expressions::stream
<< blog::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< " - [" << blog::trivial::severity
<< "] : " << blog::expressions::smessage),
blog::keywords::auto_flush = true
);
auto file_sink = blog::add_file_log(
blog::keywords::file_name = logPre + ".log",
// blog::keywords::target_file_name = logPre + "_%Y%m%d_%3N.log",
blog::keywords::enable_final_rotation = false,
blog::keywords::rotation_size = 1 * 1024 * 1024,
// blog::keywords::time_based_rotation = blog::sinks::file::rotation_at_time_point(0, 0, 0),
blog::keywords::format = (blog::expressions::stream
<< blog::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< " - [" << blog::trivial::severity
<< "] : " << blog::expressions::smessage),
blog::keywords::auto_flush = true,
blog::keywords::open_mode = ios_base::app
);
file_sink->locked_backend()->set_file_collector(blog::sinks::file::make_collector(
blog::keywords::target = logdir, //folder name.
blog::keywords::max_size = 3 * 1024 * 1024 //The maximum amount of space of the folder.
// blog::keywords::min_free_space = 100 * 1024 * 1024 //Reserved disk space minimum.
));
file_sink->locked_backend()->scan_for_files();
// 设置日志输出级别
blog::trivial::severity_level logsevirity[]{ blog::trivial::fatal, blog::trivial::fatal, blog::trivial::error,
blog::trivial::warning, blog::trivial::info, blog::trivial::debug, blog::trivial::trace};
// console_sink->set_filter(blog::trivial::severity >= logsevirity[LogLevel]);
file_sink->set_filter(blog::trivial::severity >= logsevirity[logLevel]); // 只有文件过滤日志
blog::add_common_attributes();
return 0;
}
void logFunc(int Id)
{
BOOST_LOG_TRIVIAL(trace) << Id << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << Id << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << Id << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << Id << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << Id << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << Id << "A fatal severity message";
}
用boost库进行串口读写
using namespace std;
namespace bpo = boost::program_options;
namespace blog = boost::log;
namespace baio = boost::asio;
void serialFunc(void)
{
try
{
baio::io_service io_context;
baio::serial_port sp(io_context);
sp.open("/dev/ttyS2");
//设置串口参数
sp.set_option(baio::serial_port::baud_rate(9600));
sp.set_option(baio::serial_port::flow_control());
sp.set_option(baio::serial_port::parity());
sp.set_option(baio::serial_port::stop_bits());
sp.set_option(baio::serial_port::character_size(8));
boost::system::error_code err;
while(true)
{
boost::array<char, 128> buf;
boost::system::error_code error;
size_t len = sp.read_some(baio::buffer(buf), error);
if(error)
{
throw boost::system::system_error(error); // Some other error.
}
BOOST_LOG_TRIVIAL(info) << "Serial recvd: " << buf.data();
sp.write_some(baio::buffer(buf), error);
if(err)
{
throw boost::system::system_error(error); // Some other error.
}
}
}
catch(const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Serial error: " << e.what();
return;
}
}
用boost库进行网络通信
using namespace std;
namespace bpo = boost::program_options;
namespace blog = boost::log;
namespace baio = boost::asio;
void tcpClientFunc(void)
{
try
{
baio::io_context io_context;
baio::ip::tcp::tcp::resolver resolver(io_context);
baio::ip::tcp::tcp::resolver::results_type endpoints =
resolver.resolve("192.168.205.137", "60000");
baio::ip::tcp::tcp::socket socket(io_context);
baio::connect(socket, endpoints);
BOOST_LOG_TRIVIAL(info) << "Tcp Client connect ok.";
for (;;)
{
boost::array<char, 128> buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
{
BOOST_LOG_TRIVIAL(warning) << "Tcp Client connect closed.";
break; // Connection closed cleanly by peer.
}
else if (error)
{
throw boost::system::system_error(error); // Some other error.
}
BOOST_LOG_TRIVIAL(info) << "Tcp Client recvd: " << buf.data();
// std::cout.write(buf.data(), len);
}
}
catch(const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Tcp Client connect error: " << e.what();
return;
}
}
void tcpServerChildFunc(baio::ip::tcp::tcp::socket sT)
{
time_t now = time(0);
string times = ctime(&now);
for(;;)
{
boost::system::error_code ignored_error;
baio::write(sT, baio::buffer(times), ignored_error);
if (ignored_error == boost::asio::error::eof)
{
BOOST_LOG_TRIVIAL(warning) << "Tcp Server connect closed.";
return;
}
else if (ignored_error)
{
BOOST_LOG_TRIVIAL(error) << "Tcp Server connect error: " << boost::system::system_error(ignored_error).what();
return;
}
this_thread::sleep_for(chrono::seconds(5));
}
}
void tcpServerFunc(void)
{
baio::io_context io_context;
baio::ip::tcp::tcp::acceptor acceptor(io_context, baio::ip::tcp::tcp::endpoint(baio::ip::tcp::tcp::v4(), 60000));
for (;;)
{
try
{
baio::ip::tcp::tcp::socket socket(io_context);
acceptor.accept(socket);
auto t = thread(tcpServerChildFunc, move(socket));
t.detach();
}
catch (std::exception &e)
{
BOOST_LOG_TRIVIAL(error) << "Tcp Server accept error: " << e.what();
}
}
}