protobuf在嵌入式linux下的移植及c语言调用

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: protobuf在嵌入式linux下的移植及c语言调用

关于什么是protobuf,网上搜搜一大堆,很多人用的都还是json,以为json是多种语言传输数据是万能的,看完了protobuf的实现,就明白了简单高效才是王道。


简单介绍:


protobuf很出名,是google开发的序列化库,很多公司都使用它作为接口的数据结构。地址:https://developers.google.com/protocol-buffers/


支持java、c++、go等多种语言,几乎所有主流语言都支持。


先在电脑上安装,随后交叉编译,


网上的教程大多是基于2.x的版本的,现在想用下最新的, 就编译了下3.6版本。编译环境Ubutu 14.04


官网下载:https://github.com/google/protobuf


https://github.com/google/protobuf/releases


protobuf-all-3.6.0.tar.gz


官网安装手册:https://github.com/google/protobuf/blob/master/src/README.md


sudo apt-get install  


autoconf automake libtool curl make g++ unzip


cd protobuf-3.6.0/
./configure --prefix=/usr/local/protobuf
make
make check
make install
protoc --version
cd /usr/local/protobuf/lib/pkgconfig
export PATH=$PATH:/usr/local/protobuf/bin/
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/
source /etc/profile
cd /etc/ld.so.conf.d
vi libprotobuf.conf  (/usr/local/protobuf/lib/pkgconfig/)
ldconfig


下面开始交叉编译:


export PATH=$PATH:/home/yang/b503/ctools/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin


先把交叉编译链添加到当前的环境变量里,


然后配置configure,


我的配置如下:


CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ ./configure --prefix=/home/linux/arm/protobuf --host=arm-linux


注意:CXX一定要配置为-g++而非-gcc


--host这项也一定要指定,才是交叉编译


最后,make, make install


编译完毕,没有报错。


protobuf很出名,是google开发的序列化库,很多公司都使用它作为接口的数据结构。地址:https://developers.google.com/protocol-buffers/


支持java、c++、go等多种语言,几乎所有主流语言都支持,但是官方没给出c语言的支持。经过一番寻找,找到了protobuf-c。在github中有很多protobuf c版本的实现,不过个人感觉还是这个protobuf-c更直接。其github地址:https://github.com/protobuf-c/protobuf-c


下面总结下protobuf-c 的编译和移植过程及遇到的问题:


首先版本要匹配,porotobuf-c 目前最新版本是1.30.需要依赖protobufcpp3,0以上的版本。以protobufcpp3.4为例。


在构建protobuf-c之前,我们需要安装一些依赖库:autoconf、automake、protobuffer、libtool,当然也需要一些基本的系统依赖(如:gcc-c++等)。autoconf、automake和libtool可以直接使用apt-get、yum、brew等安装。protobuffer虽然也可以使用libprotobuf,不过还是推荐源码安装。


构建protobuf-c


注意如果在构建protobuf-c之前没有安装protobuf的话,生成protobuf-c的configure文件肯定会报如下错误!!


configure: error: Package requirements (protobuf >= 2.6.0) were not 
met:No package ‘protobuf’ found


需要先下载protobufcpp 1.34,地址在:


https://github.com/google/protobuf/releases


先编译protobufcpp1.34。


执行./configure了,但是根目录下居然没有configure文件,却有一个autogen.sh,原来是因为protobuf的编译方式做了修改,要执行autogen.sh才会生成configure脚本。


但在执行autogen.sh时出错了,因为google.com被墙了,我的虚拟机里无法下载gtest,于是手动下载googletest-release-1.5.0.zip,解压缩后,改名为gtest放在protobuf-1.34目录下.


构建protobuf-c其实跟protobuffer几乎没任何区别,也是通过autogen.sh脚本生成configure配置脚本,然后执行make和install。但是需要注意的是protobuf-c在构建的时候不会自动把相关头文件拷贝到/usr/include下,需要手动拷贝。


https://github.com/protobuf-c/protobuf-c.git 克隆下protobuf-c源码。


git clone https://github.com/protobuf-c/protobuf-c.git


与构建protobuffer一样,分别执行:


./autogen.sh


接下来 make,make install


最后把头文件拷贝到/usr/include下


sudo cp -r /usr/local/protobuf-c/include/protobuf-c /usr/include 


按照网上教程,应该执行./configure了,但是根目录下居然没有configure文件,却有一个autogen.sh,原来是因为protobuf的编译方式做了修改,要执行autogen.sh才会生成configure脚本。


但在执行autogen.sh时出错了,因为google.com被墙了,我的虚拟机里无法下载gtest,于是手动下载googletest-release-1.5.0.zip,解压缩后,改名为gtest放在protobuf目录下


configure: error: required protobuf header file not found
export PKG_CONFIG_PATH=/home/linux/arm/protobuf/lib/pkgconfig/
export PKG_CONFIG_PATH=/usr/local/protobuf3.4/lib/pkgconfig/
./configure --prefix=/usr/local/protobuf-c
protobuf-c 1.3.0


       CC:                     gcc -std=gnu99


       CFLAGS:                 -g -O2


       CXX:                    g++


       CXXFLAGS:               -g -O2


       LDFLAGS:                


       LIBS:                  


       prefix:                 /usr/local/protobuf-c


       sysconfdir:             ${prefix}/etc


       libdir:                 ${exec_prefix}/lib


       includedir:             ${prefix}/include


       pkgconfigdir:           ${libdir}/pkgconfig


       bigendian:              no


       protobuf version:       libprotoc 3.4.0


make ,make install


root@yang-vir:/usr/local/protobuf-c/bin# ./protoc-c --version


protobuf-c 1.3.0


libprotoc 3.4.0



接下来再交叉编译:


1、打包下载protobuf-c,地址:https://github.com/protobuf-c/protobuf-c


2、解压后得到protobuf-c-master文件夹,进入该文件夹执行如下命令


3、./autogen.sh


4、CC=arm-linux-gnueabi-gcc CXX=g++ ./configure --host=arm-linux-gnueabi


5、make


6、等待完成,最终会在protobuf-c-master /protobuf-c/.libs目录下生成.so动态库


在给protobuf-c进行环境配置的时候,查找不到上一步所安装protobuf的库文件,而这些库文件又是通过pkgconfig配置进行查找的。 该配置文件此时是在/usr/local/lib/pkgconfig/下 ,也就是问题中所提示的:Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix.  。所以我们只需要将PKG_CONFIG_PATH 指定到/usr/local/lib/pkgconfig/即可


在交叉编译protobufcpp3.4时会报个错,解决办法如下:


I just send #3934 to fix the issue. You can just edit the src/Makefile.am locally and remove the "BUILT_SOURCES = ..." line. After that "make" should work.
CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ ./configure --prefix=/home/linux/arm/protobuf-c --host=arm-linux
export PKG_CONFIG_PATH=/home/linux/arm/protobuf3.41/lib/pkgconfig/


附带一个使用示例:


1、首先写一个.proto扩展名的文件test.proto,内容格式如下


message test
{
    required uint32 test_1=1;
    required string test_2=2;
    required string test_3=3;
    required string test_4=4;   
}


2、执行命令生成.c和.h文件,protoc-c可执行文件已经预先生成了。


./protoc-c test.proto


3、写示例代码main.c:


解析:protobuf的核心就是pack和unpack以及最后的free_unpack,比如客户端要给服务端发送一个msg,客户端把pack好的pBuf缓冲区数据直接发出去,服务端收到数据之后,再从pBuf里面“取出”out结构体指针指向的msg,最后调用free_unpacked释放out就可以了。


//main.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "test.pb-c.h"
int main(void)
{
    Test msg,*out;
    unsigned char *pBuf;
    unsigned int Len;
    test__init(&msg);            //init default
    msg.test_1=1;
    msg.test_2="5149013220584027";
    msg.test_3="5149013108519750";
    msg.test_4="121212121";
    Len=test__get_packed_size(&msg);
    printf("msg pack size %d\n",Len);
    pBuf=malloc(Len);
    if(pBuf)
    {
        test__pack(&msg,pBuf);        //construct msg to pBuf
        FILE *fp =fopen("protobuf.txt","wb+");        //output raw
        if(fp)
        {
            fwrite(pBuf,Len,1,fp);
            fclose(fp);
            fp=NULL;
        }
        out=test__unpack(NULL,Len,pBuf);
        if(out)
        {
            printf("out->test_1=%d\n",((Test*)out)->test_1);
            printf("out->test_2=%s\n",((Test*)out)->test_2);
            printf("out->test_3=%s\n",((Test*)out)->test_3);
            printf("out->test_4=%s\n",((Test*)out)->test_4);
            test__free_unpacked(out,NULL);
        }
    }
    printf("End\n");
}


4、编译,最终生成main可执行文件,将main放在开发板上,注意,相关的libc库和libprotobuf-c.so链接库都放在了开发板上/usr/arm-linux-gnueabi/lib目录下,否则运行是找不到.so库的


arm-linux-gnueabi-gcc main.c -o main test.pb-c.c -I./ -lprotobuf-c -L./protobuf-c-arm/lib -Wl,-rpath,/usr/arm-linux-gnueabi/lib


5、运行./main


实现了msg消息发送给out输出,同样类型的消息,Json需要91个字节,protobuf只需要49个字节,其实json里面的键值对name是完全不需要传输的,因为通讯双方都知道对应的name,只需要传输的只是value,但是每次传输数据,都要把name都传来传去,消耗了大量的带宽和数据存储空间。


export PKG_CONFIG_PATH=/home/linux/arm/protobuf3.41/lib/pkgconfig/
arm-linux-gnueabihf-gcc main.c -o main test.pb-c.c -I./ -lprotobuf-c -L./lib
root@b503_lcd:/app/city_app/opt# ls
enGPIO.sh           libprotobuf-c.so.1  restart.sh~         setconfig.sh        test.out
libprotobuf-c.so    main                rz                  setconfig.sh~
root@b503_lcd:/app/city_app/opt# ./main
msg pack size 49
out->test_1=1
out->test_2=5149013220584027
out->test_3=5149013108519750
out->test_4=121212121
End


附录:


1、经过测试,我们发现,其实protobuf的原理很简单,和两端都是C语言实现的client/server直接传输结构体变量原理是一样的,我们都知道,C语言结构体成员的存储方式都是顺序存储。所以发送和接收方都按照对应的成员排列位置进行解析,就可以实现数据的传输。


2、但是protobuf设计初衷应该是为了适应不同的语言之间数据传输,像java写的server里面就没有结构体,所以就不能传输C写的client里面的结构体变量给对方,对方是解析不了的。另外protobuf在.proto文件里面指定了具体的位置编号,否则应该就没办法生成.c和.h文件,如果后续双方通讯格式要做调整,双方都使用同一个修改后的.proto文件重新生产对应的源文件,重新编译即可。


3、为什么说用protobuf比json简单高效,原因就是你用json传输数据,每次字符串里面都传输了一堆没用的数据,比如键值对的冒号,以及键值名字和值的双引号,还有大小括号,因为通讯双方都知道对应的键值名以及怎么解析json(如果不知道键值名字,收到的数据还怎么解析?)。


双方都有的数据还每次传来传去,不是浪费是什么? 纯粹就是浪费带宽和存储,要传输的对方没有的数据,这才是有用数据。有人就说,这几个括号和键值名字能浪费多少带宽和空间,别小看这些小东西,假如数据交互量大和非常频繁,你就知道了,这里面的存储空间和带宽消耗差距是可怕惊人的。


测试结果如下:


yang@yang-vir:/mnt/hgfs/b503/new/protoc$ make
Building app...
arm-poky-linux-gnueabi-gcc  -march=armv7ve -marm -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=/opt/myir-imx-fb/4.1.15-2.0.1/sysroots/cortexa7hf-neon-poky-linux-gnueabi -c -g -Wall -I./ test.pb-c.c -o  test.pb-c.o    
arm-poky-linux-gnueabi-gcc  -march=armv7ve -marm -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=/opt/myir-imx-fb/4.1.15-2.0.1/sysroots/cortexa7hf-neon-poky-linux-gnueabi -c -g -Wall -I./ main.c -o  main.o    
Generating ...
arm-poky-linux-gnueabi-gcc  -march=armv7ve -marm -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=/opt/myir-imx-fb/4.1.15-2.0.1/sysroots/cortexa7hf-neon-poky-linux-gnueabi -o test_protoc ./test.pb-c.o ./main.o -L ./lib -g   -lprotobuf-c 
OK!
yang@yang-vir:/mnt/hgfs/b503/new/protoc$ ^C
yang@yang-vir:/mnt/hgfs/b503/new/protoc$ ls
test.pb-c.c  test.pb-c.o  lib   main.c  makefile    test_protoc
test.pb-c.h test.proto   main  main.o  protobuf-c
yang@yang-vir:/mnt/hgfs/b503/new/protoc$
arm-linux-gnueabihf-gcc main.c -o main test.pb-c.c -I./ -lprotobuf-c -L./lib
root@b503_lcd:/app/city_app/opt# ls
enGPIO.sh           libprotobuf-c.so.1  restart.sh~         setconfig.sh        test.out
libprotobuf-c.so    main                rz                  setconfig.sh~
root@b503_lcd:/app/city_app/opt# ./main
msg pack size 49
out->test_1=1
out->test_2=5149013220584027
out->test_3=5149013108519750
out->test_4=121212121
End


测试demo:


########################################
#makefile
########################################
#-rdynamic -pthread -lrt -lasound -lucgui -lpicc -licc -lmenures1 -LzfbAuth -lposoffline   -lhw_b503
#编译主程序
BINARY  := test_protoc
OBJ_DIR := ./
#CC= gcc
#LD= ld
CFLAGS=  -g -Wall -I./
LDSCRIPT=   -g   -lprotobuf-c
LDFLAGS= -L ./lib
SRC  = $(wildcard *.c)
DIR  = $(notdir $(SRC))
OBJS = $(patsubst %.c,$(OBJ_DIR)%.o,$(DIR))
.PHONY: clean 
all:  prebuild  $(BINARY)
prebuild:
    @echo Building app...
$(BINARY) : $(OBJS)
    @echo Generating ...
    $(CC) -o $(BINARY) $(OBJS) $(LDFLAGS) $(LDSCRIPT) 
    @echo OK!
$(OBJ_DIR)%.o : %.c
    $(CC) -c $(CFLAGS) $< -o  $@    
clean:
    rm -f $(OBJ_DIR)*.o
    @echo Removed!
相关文章
|
2月前
|
Shell Linux API
C语言在linux环境下执行终端命令
本文介绍了在Linux环境下使用C语言执行终端命令的方法。首先,文章描述了`system()`函数,其可以直接执行shell命令并返回结果。接着介绍了更强大的`popen()`函数,它允许程序与命令行命令交互,并详细说明了如何使用此函数及其配套的`pclose()`函数。此外,还讲解了`fork()`和`exec`系列函数,前者创建新进程,后者替换当前进程执行文件。最后,对比了`system()`与`exec`系列函数的区别,并针对不同场景推荐了合适的函数选择。
|
3月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
|
3月前
|
传感器 人工智能 网络协议
:嵌入式 Linux 及其用途
【8月更文挑战第24天】
169 0
|
4月前
|
Linux C语言 Windows
C语言文件编程-Linux环境下运行
本文介绍了在Linux环境下使用C语言进行文件编程时的两种主要接口:C标准库函数与Linux系统调用。C标准库提供了`fopen`, `fread`, `fwrite`, 和 `fclose`等函数,适用于普通文件操作;而Linux系统调用如`open`, `read`, `write`, 和 `close`则更适合处理设备文件,同时也可用于普通文件。这两种方法的主要区别在于前者使用文件指针,后者使用文件描述符。文章还给出了两个示例程序:一个使用C标准库函数实现文件复制,另一个则使用Linux系统调用完成相同任务。
|
3月前
|
算法 IDE 程序员
C语言与嵌入式系统:嵌入式C编程基础。
C语言与嵌入式系统:嵌入式C编程基础。
72 0
|
4月前
|
Ubuntu 算法 Linux
嵌入式Linux的学习误区
**嵌入式Linux学习误区摘要** 1. **过度聚焦桌面Linux** - 许多学习者误将大量时间用于精通桌面Linux系统(如RedHat、Fedora、Ubuntu),认为这是嵌入式Linux开发的基石。 - 实际上,桌面Linux仅作为开发工具和环境,目标不应是成为Linux服务器专家,而应专注于嵌入式开发工具和流程。 2. **盲目阅读Linux内核源码** - 初学者在不了解Linux基本知识时试图直接研读内核源码,这往往导致困惑和挫败感。 - 在具备一定嵌入式Linux开发经验后再有针对性地阅读源码,才能有效提升技能。
|
5月前
|
存储 移动开发 C语言
技术心得记录:嵌入式开发中常用到的C语言库函数
技术心得记录:嵌入式开发中常用到的C语言库函数
57 1
|
5月前
|
网络协议 Linux 编译器
【原创】EtherCAT主站IgH解析(二)-- 如何将Igh移植到Linux/Windows/RTOS等多操作系统移植指南
EtherCAT主站方案对比:商业的如Acontis、TwinCAT3和开源的igh、SOEM。SOEM易移植但功能和实时性不足,适合简单应用;igh功能强大,实时性能优秀,基于内核态,适合复杂场景。igh能移植到其他RTOS,但需克服多任务无调度的挑战。依赖操作系统服务如定时器、内存分配,适合Linux内核,但移植到裸机复杂。
234 0
|
6月前
|
存储 Linux C语言
Linux系统下C语言的文件操作
Linux系统下C语言的文件操作
71 0
|
6月前
|
Linux 测试技术 C语言
【Linux】应用编程之C语言文件操作
【Linux】应用编程之C语言文件操作