嵌入式Linux开发的编程语言选择

欢迎大家关注我的公*号: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();
        }
    }
}
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值