基于共享内存的分布式消息学习笔记

2023-05-16

作者:深圳自研业务组  jimwu

编辑:上海业务组 alenai

 

目录:

Tbus简介

Tbus原理

Tbus配置与工具

Tbus简单应用

Tbus运维应用-python扩展

总结

Tbus简介

   Tbustsf4gTencent ServiceFramework for Game,腾讯游戏服务框架)中的基础组件之一,主要的目的是为上层业务提供统一的线程或进程间通信接口,屏蔽本地进程间通信以及远程进程通信的细节,让开发人员可以集中精力关注业务逻辑,是tsf4g重要组成部分。

 

Tbus原理

   Tbus基于共享内存构建无锁双通循环消息队列,发送的双方通过专用的读写队列完成数据收发,实现本地进程通信或者远程进程间通信。通信双方使用的两个队列称之为tbus通道(channel),每一组通讯的双方就需要有一个tbus通道。如下是一组tbus通道的图例描述。

                            

   进程AB启动后,通过tbus API绑定到对应的通道,使用通道的两个消息队列进行收发数据,而不用关注对方的部署位置。

   Tbusd

如果AB同部署于一台物理设备,与上图描述一致。如果AB部署于不同设备之上则需要tbusd服务进程完成消息中转。通信关系如下图。

进程A发送消息后,消息被存储在Host A的发送队列中,部署于Host Atbusd发现队列中存在消息,则从队列中把消息取出,通过tcp发送Host B上。Host B上的tbusd接收到消息后,把消息写入本地的接收队列,以供进程B读取。

为了能完成通信,Tbus还有以下几个特点

  1. 通信双方具备全局唯一的tbus通信地址,该地址是一个点分十进制的字符串,与IP地址类似,总长度为32bit,分为4段,每段bit位数可以自定义,总长度不得超过32bit。例如:128.1.100.15.0.200.1

  2. Tbus通道以及消息是存储在共享内存中,必须要使用相应的工具提前创建,进程才能绑定和使用。而当业务进程异常退出后,由于消息是存储在共享内存中,只要不主动清理共享内存、重启服务器或损坏共享内存,通道中的消息就不会丢失。

  3. Tbus通道为每一个消息都会分配一定时间内唯一的一个序列号,当消息需要跨物理服务器中转时,远端的tbusd会针对该序列号的消息进行确认,如果本地tbusd未收到确认,该消息则会重传。

  4. tbus通道被消息写满后(例如对端服务器死机,本地进程仍继续发送消息),则无法再往通道中写入消息,该异常需要业务自己处理。

GCIM

一台服务器可以有诸多的通信关系,每一条tbus通道就是最小的通信单元,每条通道都有自己的配置区域,保存着通道大小,通道地址以及读写指针等,一台服务器上所有配置信息汇聚起来就是全局通道信息表globalchannel information map, GCIM),一台服务器仅需要一份。管理GCIM的工具是tbusmgr。管理关系如下:

GRM

如果一台服务器不需要和其他远程服务器通信,则使用GCIM就能完成所有工作。当两台服务器需要使用tbus进行通信时,为了完成通信,除了启动tbusd服务进程之外,还需要建立一份通信配置,称之为全局路由表globalroute mapGRM),用于说明哪些通道的消息需要中转。GRM也是保存在共享内存中,每一台机器只需要一份,管理GRM的工具为trelaymgr

 

Tbus配置与工具

1.Tbusmgr GCIM

Tbusmgr是用于创建,维护GCIM的重要命令行工具。创建GCIM共享内存信息需要依赖tsf4g规范的xml配置文件。Tbusmgr可以根据xml文件的信息对GCIM进行增删改查等所有操作。Tbusmgr.xml配置文件仅包含两部分,一部分是公共参数的设定,例如通信地址模板设定,共享内存key的设定。一部分是对tbus通道(channel)的配置,每一组通信关系就需要一份。简单tbusmgr.xml说明如下:

<TbusGCIM>

  <!-- 十进制点分表示法表示的通信地址模版  -->

 <AddrTemplet>8.8.8.8</AddrTemplet>

   <!-- GCIM共享内存key -->

  <GCIMShmKey>1000</GCIMShmKey>

  <Channels>

    <Priority>8</Priority>

    <!-- 通道两端进程的通信地址,使用点分法表示 -->

    <Address>0.0.1.1</Address>

    <Address>0.0.3.1</Address>

     <!-- 对于出现在配置中的第一个进程而言,SendSizeRecvSize分别表示此进程相关发送,接受数据队列的大小。由于两个进程是对等的,因此第一个进程的发送队列大小(SendSize)就是第二个进程的接受队列大小(RecvSize) -->

    <SendSize>20480000</SendSize>

    <RecvSize>20480000</RecvSize>

    <Desc>tcmcenter and itstconnd</Desc>

  </Channels>

  <Channels>

    <Priority>8</Priority>

    <Address>0.0.1.1</Address>

    <Address>0.0.2.1</Address>

    <SendSize>20480000</SendSize>

    <RecvSize>20480000</RecvSize>

    <Desc>tcmcenter and itscenterd</Desc>

  </Channels>

</TbusGCIM>

上述配置描述了:

  1. 通信地址32bit分为四段,每段长度为8bit

  2. 共享内存使用key1000,转化为16进制后为0x000003e8

  3. 创建0.0.1.10.0.3.1通信的tbus通道。读写队列大小都为20480000字节,描述信息为tcmcenter and its tconnd

  4. 创建0.0.1.10.0.3.1通信的tbus通道。读写队列大小都为20480000字节,描述信息为tcmcenter and its tconnd

需要特别说明的是priority字段,该字段是通道优先级说明,当前没有使用,没有任何含义。默认值为8

GCIM创建

在命令行下,可以使用以下命令完成GCIM创建:

tbusmgr--conf-file=./tbusmgr.xml –-write

或者:tbusmgr –C tbusmgr.xml –W ,完成创建后tbusmgr会输出其创建的信息,我们也可以使用tbusmgr查看我们写入的信息:

tbusmgr –k1000或者 tbusmgr –Ctbusmgr.xml

我们可以使用ipcs查看当前在共享内存中的信息,与tbusmgr打印打出的消息是完全一致的,在key0x3e8上申请了192408字节大小作为管理区域,在共享内存上申请了40960544字节为0.0.1.10.0.3.1创建了一条通道,包含两个大小为20480000字节的消息队列,其中控制信息大小为544字节,shmid 12091401

Tbus通道查看

Tbusmgr –k1000 –-see 0.0.3.1 或者tbusmgr –C tbusmgr.xml –-see 0.0.3.1 可以查到看0.0.3.1的通道消息,可以查看该通道下消息数量,字节数等信息。--see-all参数则查看所有通道。

我们可以使用tbusmgr给通道中写入一些测试数据:

tbusmgr-k1000 –T --src=0.0.1.1 --dst=0.0.3.1 --data-len=14

0.0.1.10.0.3.1的队列中,写入14字节的测试数据。

当前通道状况如下(tbusmgr -k1000 --see 0.0.3.1--dump-binary):

MsgNum1,队列中有一个消息

Bytes72,长度为72个字节,部分为控制信息。

H0,头指针指向0

T72,尾指针指向72

Size:队列大小

HSeq:队列中第一个消息序列号

Seq:队列总共传输了多少消息

可以使用tbusmgr –k1000 –-clean 1清理第一条通道中的消息,本例子中就是清理0.0.3.10.0.1.1中的所有消息

Tbus通道删除

Tbusmgr –k1000 –-delete 1 可以清理第一条tbus通道,本例子中为0.0.3.10.0.1.1的通道。删除后该通道将被清理,共享内存也将被释放。而原来的第二条通道(0.0.2.1<=>0.0.1.1)将成为第一条通道。

如果要恢复该通道,我们可以继续使用原来的配置文件进行恢复。

tbusmgr –C tbusmgr.xml –W

可以看到0.0.1.1<=>0.0.2.1的创建时间按没有变化,但是增加了第二条通道0.0.3.1<=>0.0.1.1tbusmgr写入GCIM时,是采用全量写入的策略,如果共享内存中已经存在该通道,则对该通道进行忽略,如果共享内存中没有存在该tbus通道,则进行创建,如果共享内存中存在多余的tbus通道,则对该tbus通道进行删除

2.trelaymgrGRM

   GRM是本地服务器上的全局路由表,保存在共享内存中,用来记录tbus通道与远端链接的对应关系。Tbusd用该对应关系完成通道消息的转发。以下是trelaymgr.xml的简单说明。

<RelayMnger>

   <AddrTemplet>8.8.8.8</AddrTemplet>

    <RelayShmKey>2688</RelayShmKey>

    <Relays>

        <Addr>0.0.3.1</Addr>

       <MConn>tcp://10.1.44.61:1027</MConn>

        <Desc>relay info for 0.0.3.1</Desc>

    </Relays>

</RelayMnger>

   基于原来的示例,完成GRM的配置文件。

AddrTemplet:与tbusmgr中的字段含义相同。

RelayShmKey:共享内存key

Relays:保存对应的tbus地址与远端链接的对应关系。写入共享内存后,tbusd会把0.0.3.1上的消息使用tcp协议发送到10.1.44.611027端口。

   写入共享内存(使用参数与tbusmgr一致):

   trelaymgr -C relaymgr.xml –W

  

   Trelaymgr –C relaymgr.xml –-delete NUM,可以删除该编号的消息转发规则。

  

Tbus简单应用

   了解tbus实际应用细节对于运维而言,不仅可以更细致把握业务的架构与部署,应用于问题排查与故障定位,也可以通过研发的角度审视tbus以及我们的业务,多角度剖析我们的工作,提升我们的专业素养。

Tbus api简介

/* 初始化tbus系统 */

int tbus_init_ex(const char* a_pszShmKey,int a_iFlag);

/* 清理tbus系统 */

void tbus_fini(void);

/* 创建tbus句柄 */

int tbus_new(int* a_piHandle);

/* 点分十进制tbus地址转TBUSADDR类型的地址 */

int tbus_addr_aton(const char* a_pszAddr, TBUSADDR*a_piAddr);

/* tbus句柄绑定本地TBUS地址 */

int tbus_bind(int a_iHandle, const TBUSADDRa_iAddr);

/* 释放tbus句柄 */

void tbus_delete(int* a_piHandle);

/* 收取一条消息 */

int tbus_recv(int a_iHandle, TBUSADDR*a_piSrc, TBUSADDR* a_piDst,void* a_ppvData, size_t* a_ptLen, int a_iFlag);

/* 发送消息 */

int tbus_send(int a_iHandle, TBUSADDR*a_piSrc, TBUSADDR* a_piDst, const void* a_pvData, size_t a_iLen, int a_iFlag);

/* 取得下一个消息的指针与长度信息 */

int tbus_peek_msg(int a_iHandle, TBUSADDR*a_piSrc, TBUSADDR* a_piDst,const char** a_ppvData, size_t* a_ptLen, inta_iFlag);

api使用一般逻辑

  1. 使用tbus_init_ex初始化tbus环境

  2. 使用tbus_new创建tbus句柄

  3. 使用tbus_addr_aton把字符串tbus地址转换为TBUSADDR结构体

  4. 使用tbus_bindtbus句柄和TBUSADDR绑定

  5. Tbus_recv/tbus_send收发消息

  6. 使用完成后,使用tbus_delete释放tbus句柄

  7. 使用tbus_fini清理tbus环境

简单示例

Tbus通道采用上面的配置,本示例通信使用0.0.1.10.0.2.1进行通信,为了简单说明流程,代码没做错误处理。

文件tbus_send.cpp如下,调整tbus地址转换中的地址即为tbus_recv.cpp

#include<iostream>

#include"pal/pal.h"

#include"tbus/tbus.h"

usingnamespace std;

 

intmain()

{

        int iRet = 0, iBusRecvHandle = 0;

        TBUSADDR iSrcAddr,iDstAddr;

        char szBuf[1024] = "this is atsf4g tbus test! ";

        tbus_init_ex("1000", 0); //初始化

        tbus_new(&iBusRecvHandle);  //创建tbus句柄

      //tbus地址转换,两个地址互换后保存即为tbus_recv.cpp

        tbus_addr_aton("0.0.1.1",&iSrcAddr);

        tbus_addr_aton("0.0.2.1",&iDstAddr);

      //tbus地址绑定

        tbus_bind(iBusRecvHandle, iSrcAddr);

      //发送消息 this is a tsf4g tbus test!

        tbus_send(iBusRecvHandle,&iSrcAddr, &iDstAddr, szBuf, strlen(szBuf), 0);

        size_t iRecvLen = sizeof(szBuf);

      //接收消息,打印消息或者打印No msg in tbus channel.

        iRet = tbus_recv(iBusRecvHandle,&iDstAddr, &iSrcAddr, szBuf, &iRecvLen, 0);

        if (TBUS_ERR_CHANNEL_EMPTY ==(unsigned)iRet)

        {

            cout <<"No msg in tbuschannel." << endl;

        }else {

            cout << "Recv Data: "<< szBuf << endl;

       }

      //tbus 清理

        tbus_delete(&iBusRecvHandle);

        tbus_fini();

        return 0;

}

简单编译生成tbus_sendtbus_recv可执行文件(TSF4Gtsf4g安装目录):

g++ -c-fPIC -I${TSF4G}/include/ tbus_send.cpp -o tbus_send.o

g++ -otbus_send tbus_send.o -L${TSF4G}/lib/ ${TSF4G}/lib/libtsf4g.a -Wl,-Bstatic-lscew -lexpat -Wl,-Bdynamic -lpthread

 

g++ -c-fPIC -I${TSF4G}/include/ tbus_recv.cpp -o tbus_recv.o

g++ -otbus_recv tbus_recv.o -L${TSF4G}/lib/ ${TSF4G}/lib/libtsf4g.a -Wl,-Bstatic-lscew -lexpat -Wl,-Bdynamic –lpthread

执行结果:

 

Tbus运维应用-为python扩展

   Tbus提供了快速高效的进程通信接口,让上层逻辑基本可以完全忽略消息的收发与流转,为快速搭建中小型乃至大型应用提供了强有力的支持。该收益不仅仅是针对C++,针对内部常用的python更是如此。业务应用需要对外提供服务,前端还需要搭载公共组件tconnd,运维内部系统完全对内服务,逻辑服务加上tbus就能完成非持久化存储的部分构建(同理,持久化部分可以考虑使用tormsvr进行代理),极大降低了系统构建的复杂度(采用tcm-tagent管理更能降低了后续管理的成本)。

   Tsf4g当前虽然没有为python提供可用的api,但是如今使用pythonC++构建混合系统的技术日臻完善,我们可以为python扩展tbus接口。以下是使用第三方库boost.pythonpython封装tbus接口的简单探讨。

   构建环境

   系统:Tlinux1.2

   Gcc4.4.6tlinux1.2默认版本)

   Boost.python1.49lib目录~/lib/boost1.49,库文件~/include/boost/

   Python2.7.3,默认安装目录/usr/local/

(该示例继续沿用上一个例子的tbus环境)

   Tlinux1.2 python默认版本是2.6.6,由于没有库文件,为了获取库文件,所以升级上2.7.3python版本可以在其官网(www.python.org)获取到。

Boost.pythonboost的一个子库,为pythonC++交互提供了简单易用的接口,使用前需要编译,boost库可以在其官网(www.boost.org)下载。完成python安装以及boost.python编译后,我们可以开始为python扩展tbus的动态库了:

  1. 针对tbus api进行简单类封装(以下的tbus.hpp文件);

  2. 使用boost.python1步骤中的tbus类进行python封装(以下的libtbus.cpp)。

  3. libtbus库编译,完成编译后就可以在python环境下导入tbus了。

说明:步骤1不是必须的,可以直接对tbus原始api进行python封装。这里对tbus进行简单的封装是为了简化示例。

文件Tbus.hpp

#ifndefTBUS_HPP

#defineTBUS_HPP

 

#include"pal/pal.h"

#include"tbus/tbus.h"

 

class Tbus{

 

protected:

        std::string shm_key;

        std::string send_channel;

        std::string recv_channel;

        int bus_handler ;

        TBUSADDR send_addr;

        TBUSADDR recv_addr;

 

public:

        Tbus(std::string key, std::string send,std::string recv) : shm_key(key),  send_channel(send), recv_channel(recv), bus_handler(0){}

        bool init(){

                if (TBUS_SUCCESS !=tbus_init_ex(shm_key.c_str(), 0)) {

                        std::cout <<"Tbus init failed!" << std::endl;

                        return false;

                }

                if (TBUS_SUCCESS !=tbus_new(&(this->bus_handler))) {

                        std::cout <<"Tbus create bus handler failed!" << std::endl;

                        return false;

                }

                if (TBUS_SUCCESS !=tbus_addr_aton(this->send_channel.c_str(), &send_addr)) {

                        std::cout <<"Tbus failed to convert " << this->send_channel

                                       << " to addr." << std::endl;

                        return false;

                }

                if (TBUS_SUCCESS !=tbus_addr_aton(this->recv_channel.c_str(), &recv_addr)) {

                        std::cout <<"Tbus failed to convert " << this->recv_channel

                                       << " to addr." << std::endl;

                        return false;

                }

                if (TBUS_SUCCESS !=tbus_bind(bus_handler, send_addr)){

                        std::cout <<"Tbus bind failed!" << std::endl;

                        return false;

                }

                return true;

        }

 

        int send_message(const std::string & message){

                return tbus_send(bus_handler,&send_addr, &recv_addr, message.c_str(), message.length(), 0);

        }

 

        boost::python::str recv_message(){

                char buffer[1024] = {0};

                size_t recv_len = 1024;

                int ret =tbus_recv(bus_handler, &recv_addr, &send_addr, buffer,

                                &recv_len,0);

                if (TBUS_SUCCESS == ret){

                        returnboost::python::str(buffer, recv_len);

                }

                returnboost::python::str("None");

        }

 

        void finit(){

                tbus_delete(&bus_handler);

                tbus_fini();

        }

 

        ~Tbus(){

                this->finit();

        }

 

};

 

#endif

 

代码说明:

Tbus:构造函数,参数为共享内存key,收发端进程的tbus地址;

Init:初始化函数,初始化tbus环境,完成tbus句柄以及tbus地址内部转换;

send_message:消息发送函数,为了快速完成封装,只能发送string

recv_message:消息接收函数,考虑到python的使用,直接返回python下的str类型数据;

finittbus环境清理,删除tbus句柄,退出tbus

文件libtbus.cpp

#include"boost/python.hpp"

#include"tbus.hpp"

 

BOOST_PYTHON_MODULE(libtbus){

        namespace bpy = boost::python;

       bpy::class_<Tbus>("Tbus", bpy::init<std::string,std::string, std::string>())

                        .def("init",&Tbus::init)

                       .def("send_message", &Tbus::send_message)

                       .def("recv_message", &Tbus::recv_message)

                        .def("finit",&Tbus::finit);

        bpy::def("tbus_error_string",tbus_error_string);

}

代码说明:

使用boost.python封装tbus.hpplibtbus库。封装Tbus类的五个函数:构造函数,initsend_messagerecv_message以及finit。具体boost.python语法含义可以参考官方文档(完整的说明那就是另外一篇帖子了)http://www.boost.org/doc/libs/1_54_0/libs/python/doc/index.html

 

简单Makefile

(请注意库文件目录,替换为自己实际目录)

CXXFLAGS=-I/data/home/jimwu/include/tsf4g/-I/usr/local/include/python2.7 -I/data/home/jimwu/include/

LDFLAGS=-L/data/home/jimwu/lib/boost1.49-L/usr/local/lib/python2.7 -L/data/home/jimwu/lib/tsf4g/

 

libtbus.so: libtbus.o /usr/local/lib/libpython2.7.a

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于共享内存的分布式消息学习笔记 的相关文章

  • (十)嵌入式:使用TCP协议实现图传

    这段时间做了通信相关的项目 xff0c 需要用到无线图传 xff0c 因此想到了用TCP协议实现 废话不多说 xff0c 直接上代码 xff1a 服务器端 xff1a include lt stdlib h gt include lt st
  • PnP 单目相机位姿估计(一):初识PnP问题

    简介理解更多 IDE xff1a visual studio 2013 使用库 xff1a Eigen opencv2 4 9 文档版本 xff1a 1 1 简介 PnP问题是求解3D 2D点对运动的方法 他描述了当知道n个三维空间点坐标及
  • 多传感器融合中的时间同步2-论文阅读

    文章目录 前言主要内容pps对于INS时间戳校准作用原理 测试结果参考文献 前言 阅读硕士论文 GPS INS组合导航系统研究及实现 xff0c 该论文第5章为时间同步系统设计 xff0c 为GPS INS系统设计的时间同步系统部分内容非常
  • PSINS源码阅读—STIM300/GNSS组合导航

    文章目录 前言代码解读主要框架代码阅读主要脚本sinsgps函数 结果测试 前言 严老师最近在PSINS网站上上传了一组STIM300 GNSS跑车数据 xff0c 并且有光纤惯导数据作为真值参考 xff0c 网站是一组STIM300 GN
  • mpu6500-gnss组合导航代码分析

    文章目录 前言代码分析调参P矩阵陀螺仪偏置P矩阵加速度计偏置P矩阵 前言 导航数据为如下链接 xff0c 数据集使用了低成本Mems器件MPU6500和GNSS做组合导航 代码运行需要严老师psins210406组合导航函数库的支持 xff
  • Java中数组元素的删除

    这是一个LeetCode的简单题 xff0c 在二刷做过的题时突然感觉这个题真的是非常的不错 xff0c 虽然是个简单题 xff0c 没有什么技巧 xff0c 但是写代码的过程中有很多要注意的点 xff0c 感觉还是很考验基本功 xff0c
  • 【视觉里程计】对极几何,三角测量,PnP,ICP原理

    老早就想写些东西 xff0c 但是介于个人懒惰 xff0c 一直没开这个头 xff0c 前几天才发现自己以前学的东西很容易忘记 xff0c 于是决定还是将学习做个总结 xff0c 以便后续回头查看 xff0c 温故而知新嘛 此文章为对相关知
  • Java泛型--泛型应用--泛型接口、泛型方法、泛型数组、泛型嵌套

    1 泛型接口 1 1泛型接口的基本概念 1 2泛型接口实现的两种方式 定义子类 xff1a 在子类的定义上也声明泛型类型 interface Info lt T gt 在接口上定义泛型 public T getVar 定义抽象方法 xff0
  • Linux下调试段错误的方法[Segmentation Fault]--GDB

    原文 1 段错误是什么 xff1f 段错误是指访问的内存超出了系统给这个程序所设定的内存空间 xff0c 例如访问了不存在的内存地址 访问了系统保护的内存地址 访问了只读的内存地址等等情况 A segmentation fault ofte
  • linux驱动开发--copy_to_user 、copy_from_user函数实现内核空间数据与用户空间数据的相互访问

    设备读操作 如果该操作为空 xff0c 将使得read系统调用返回负EINVAL失败 xff0c 正常返回实际读取的字节数 ssize t read struct file filp char user buf size t count l
  • 函数中的形式参数和实际参数

    1 举例 xff1a 使用函数交换两个整形变量的值 运行结果 xff1a 分析 xff1a c语言中实际参数和形式参数之间采用值传递的方式来传递数据 在被调函数中 xff0c 使用的是实际参数的一个拷贝数据 我们在swap函数中交换了a和b
  • Linux 线程挂起与唤醒功能 实例

    pthread cond wait 多线程的条件变量 条件变量是利用线程间共享的 全局变量进行同步的一种机制 xff0c 主要包括两个动作 xff1a 一个线程等待 34 条件变量的条件成立 34 而挂起 xff1b 另一个线程使 34 条
  • PnP 单目相机位姿估计(二):solvePnP利用二维码求解相机世界坐标

    前言原理简介输入参数准备 1 objectPoints特征点世界坐标2 imagePoints特征点在摄像头下的像素点坐标3cameraMatrixdistCoeffs内参矩阵和畸变矩阵 相机世界坐标的求解 1求世界坐标中的点在相机坐标系下
  • Linux下socket编程,附带tcp例子

    1 网络中进程之间如何通信 xff1f 本地的进程间通信 xff08 IPC xff09 有很多种方式 xff0c 但可以总结为下面4类 xff1a 消息传递 xff08 管道 FIFO 消息队列 xff09 同步 xff08 互斥量 条件
  • 程序员加班到深夜,你经历过没?

    我看到了自己的影子啊 虽然自己非科班出身 xff0c 学历也不高吧 xff0c 但是自认为还是很努力的 xff0c 但是为什么现在的工资水平却跟应届生差不多呢 xff1f xff08 xff09 仔细想想 xff0c 自己毕业3年了 xff
  • 【C/C++学院】(16)QT版:幸运大抽奖

    程序效果 xff1a ifndef DIALOG H define DIALOG H include lt QDialog gt include lt QLabel gt include lt QPushButton gt include
  • 【Python基础】--Pickle/函数默认参数/函数的参数*args/Bytes<=>str/32-64bit/bytes对象

    Pickle gt gt gt import pickle gt gt gt my list 61 1 2 3 39 haha 39 39 and 39 39 or 39 gt gt gt pickle file 61 open 39 my
  • Windows平台python操作串口示例,可以加工下,改写成方便的测试软件

    在 windows中 xff0c 使用 Python 进行串口编程需要安装一个 Serial 模块 pyserial xff1a 下载地址 https pypi python org pypi pyserial下载完成后得到一个 pyser
  • 告别csdn一年了

    原本坚持了4年的学习 xff0c 整理笔记 xff0c 在csdn平台上进行发表 xff0c 记录 同朋友们互动 xff0c 探讨进行学习 xff0c 自己也在不断地成长 今天再次进入博客页面 xff0c 发现界面来了个大改版 xff0c
  • php视频课程

    php视频课程 xff1a 下载地址 xff1a http php itcast cn php video shtml 注 xff1a 此系列视频 xff0c 韩顺平主讲 1 php入门到精通教程 2 第二版mysql视频教程 进行中 3

随机推荐

  • pixhawk ulg转csv

    ulg是目前最新版px4固件生成的log格式 xff0c 下载最新版的flightplot即可对内部数据进行预览分析 xff0c flightplot中支持部分函数和运算符操作 xff0c 但对带 数据的操作不支持 xff0c 如需要对某些
  • 将Kinetic中的Gazebo7升级为Gazebo9

    将Kinetic中的Gazebo7升级为Gazebo9 一 查看所有gazebo7的相关包二 卸载当前已安装的gazebo相关包三 添加源四 安装新版本gazebo五 安装gazebo ros pkgs六 后记 官方教程 http gaze
  • 你真的了解串口 (Serial)吗?

    一 串口的定义 串口 xff0c 全称串行通信接口或串行通讯接口 xff0c 是一种常用于电子设备间通讯的全双工扩展接口 xff1b 串行通信 xff0c 串口通讯的技术基础 xff0c 指一位一位地按顺序传送数据 其特点是线路简单 xff
  • PnP 单目相机位姿估计(三):二维码角点检测

    解PnP问题时用二维码的好处二维码识别的流程代码最后 IDE xff1a visual studio 2013 使用库 xff1a Eigen opencv2 4 9 文档版本 xff1a 1 0 解PnP问题时 xff0c 用二维码的好处
  • 2014年计算机求职总结--面试篇

    又一年实习招聘陆续开始了 xff0c 这里分享一下我在2013年实习招聘和秋季招聘中的一些面试经历 xff0c 希望能对找工作的同学有所帮助 2013年面试过的公司有蘑菇街 网易游戏 阿里巴巴 腾讯 百度 大众点评 人人网 雅虎 xff08
  • 用位运算实现两个整数的加减乘除运算

    位运算的思想可以应用到很多地方 xff0c 这里简单的总结一下用位运算来实现整数的四则运算 1 整数加法 int Add int a int b for int i 61 1 i i lt lt 61 1 if b amp i for in
  • 深入理解C/C++数组和指针

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 7044380 C语言中数组和指针是一种很特别的关系 xff0c 首
  • 轻松搞定面试中的链表题目

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 7393134 链表是最基本的数据结构 xff0c 面试官也常常用链
  • 轻松搞定面试中的二叉树题目

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 7518888 树是一种比较重要的数据结构 xff0c 尤其是二叉树
  • 动态内存分配(malloc/free)简单实现--隐式空闲链表

    本文使用隐式空闲链表实现简单的动态内存分配 动态内存分配器维护一个大块区域 xff0c 也就是堆 xff0c 处理动态的内存分配请求 分配器将堆视为一组不同大小的块的集合来维护 xff0c 每个块要么是已分配的 xff0c 要么是空闲的 实
  • 二分查找,你真的掌握了吗?

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 8937978 二分查找 xff0c 最基本的算法之一 xff0c
  • 【谷歌面试题】求数组中两个元素的最小距离

    一个数组 xff0c 含有重复元素 xff0c 给出两个数num1和num2 xff0c 求这两个数字在数组中出现的位置的最小距离 O n 时间复杂度 xff0c O 1 空间复杂度 int minDistance int A int si
  • 进程间通信

    原作者地址不详 摘 要 随着人们对应用程序的要求越来越高 xff0c 单进程应用在许多场合已不能满足人们的要求 编写多进程 多线程程序成为现代程序设计的一个重要特点 xff0c 在多进程程序设计中 xff0c 进程间的通信是不可避免的 Mi
  • 通过能观性分析理解SLAM系统的可观维度。

    目录 1 能观性分析大体理解2 可观性定义3 可观性矩阵 1 能观性分析大体理解 什么是能观性分析 xff1f 能观性分析通过计算可观性矩阵 xff0c 分析它的零空间的秩 xff0c 来分析系统哪些状态维度可观 不可观 可观性矩阵对应系统
  • 百度2014移动研发笔试题目——1013清华版

    一 简答题 1 简述计算机的存储系统分为哪几个层次 xff0c 为什么这样的分层能够提高程序的执行效率 2 浮点数在计算中如何表示 xff0c 如何对浮点数判等 3 简述TCP与UDP协议的差别 xff0c 两者与HTTP的关系 并列举HT
  • 史上最全的ubuntu16.04安装nvidia驱动+cuda9.0+cuDnn7.0

    本文参考了 http www cnblogs com 5211314jackrose p 7081020 html https jingyan baidu com article 4853e1e55679491909f726f4 html
  • 4、ORB-SLAM闭环检测之通过词袋寻找当前关键帧和闭环候选帧之间的匹配

    目录 1 SearchByBoW 2 图解每一步实现原理 2 1 通过词袋模型寻找匹配 2 2 通过旋转直方图来筛除离群点 3 期待您加入 前面 我们已经了解到了sim3的求解流程 具体计算过程中有三步比较重要 1 寻找两关键帧之间的粗匹配
  • 基于Web服务的物联网-WoT(Web of Things)

    当我们谈到智能手机 xff0c 多样性 往往不是问题 xff0c 主流市场不是基于苹果IOS系统的就是谷歌的Android系统 xff0c 但即将到来的物联网浪潮 xff0c 智能对象是否也能这样 xff1f 这就复杂多了 xff0c 当前
  • 如何选择 catkin_make和catkin_make_isolated

    问题 在编译cartographer的代码包的时候 xff0c 会被建议使用catkin make isolated xff0c 为何不使用catkin make呢 xff1f 原因 catkin make was the first sc
  • 基于共享内存的分布式消息学习笔记

    作者 xff1a 深圳自研业务组 jimwu 编辑 xff1a 上海业务组 alenai 目录 xff1a Tbus 简介 Tbus 原理 Tbus 配置与工具 Tbus 简单应用 Tbus 运维应用 为 python 扩展 总结 Tbus