docker入门教程

1. docker安装 请参考:[https://www.jianshu.com/p/665...] 2.制作docker镜像2.1 准备测试程序docker_test.cpp#include <stdio.h>#include <unistd.h>#include <string.h>int main(int argc, char** args){ FILE* pfile = fopen("docker_test.txt", "wb"); if(NULL == pfile) { return 0; } char buf[50] = "hello, welcome to learn docker"; while(1) { printf(buf); fwrite(buf, 1, strlen(buf), pfile); sleep(1); } return 0;}2.2 编写docker文件FROM centosRUN yum install -y gcc gcc-c++ make patch sudoRUN mkdir /usr/src/docker_testCOPY docker_test.cpp /usr/src/docker_testWORKDIR /usr/src/docker_testRUN g++ -o test docker_test.cppCMD ["./test"]2.3 编译$sudo docker build -t docker-test:v1 .2.4 运行docker镜像$sudo docker imagesREPOSITORY TAG IMAGE ID SIZEdocker-test v1 ba22adea3409 399MB$sudo docker run -d docker-test:v1##2.5 进入/退出docker ...

October 17, 2019 · 1 min · jiezi

qDebug-stdcout-printf性能表现

Qt君最近感觉qDebug相对于printf打印感觉有些慢,但又没有证据,于是闲着就写下qDebug,std::cout,printf的性能表现咯。注:测试数据仅供参考。0x00 测试环境环境参数CPUi5-8250U内存8G操作系统Windows@64位Qt版本Qt 5.12.1编译器MSVC2017@64位0x01 数据呈现 通过使用qDebug,std::cout,printf在1秒内打印的字符串数据。 分别各测试10次后取平均值,详细数据在文末。 debug版本(次/秒)release版本(次/秒)qDebug3831760923std::cout382890372696printf432606386663图表化显示 0x02 数据分析性能表现:printf > std::cout > qDebug;qDebug()相对于std::cout和printf差距过大(6~10倍);std::cout与printf数据基本一致;std::cout与printf的debug与release差距不大,甚至有debug比release快的现象(可能受实验环境影响)。0x03 结论qDebug比std::cout和printf慢,高频调用有可能影响系统时延;性能均衡推荐选用std::count;追求性能选用printf。0x04 测试程序#include <QDebug>#include <QElapsedTimer>#include <iostream>/* 注:单独打开某个宏测试 *///#define TEST1//#define TEST2//#define TEST3int main(int argc, char *argv[]){#ifdef TEST1 { QElapsedTimer t; qint64 it = 0; t.start(); while (t.elapsed() < 1000) { qDebug() << "Test1"; it++; } qDebug() << "Test1: " << it; }#endif#ifdef TEST2 { QElapsedTimer t; qint64 it = 0; t.start(); while (t.elapsed() < 1000) { std::cout << "Test2" << std::endl; it++; } std::cout << "Test2: " << it; }#endif#ifdef TEST3 { QElapsedTimer t; qint64 it = 0; t.start(); while (t.elapsed() < 1000) { printf("Test3\n"); it++; } printf("Test3: %lld\n", it); }#endif return 0}0x05 测试数据(各10次)debug版本qDebug: 38310 38452 39416 38420 38962 38385 39293 38814 34178 38946std::cout: 389512 397234 378168 367970 366371 364401 405547 405992 365863 387846printf: 468310 423937 480598 385025 490155 489473 373419 397995 445099 372054release版本qDebug: 60779 60710 59450 59685 63298 61044 59788 61167 61822 61495std::cout: 352541 358754 377001 380487 397576 362145 333757 413027 416352 335320printf: 468310 329729 333142 320171 333825 330411 471041 473771 468310 337921

October 17, 2019 · 1 min · jiezi

qlv转mp4格式转换器哪个好用

迅捷视频转换器是一款简单的视频转换软件,可以帮助用户在使用软件转换视频格式的时候获得更好的操作,该软件可以解决qlv转MP4格式的问题,qlv这种格式的视频不常见,普通的播放器不能兼容这种格式,导致很多用户无法查看qlv里面的内容,所以小编找到了这款腾讯视频qlv转换MP4工具,通过该软件就可以将qlv转换MP4,这样你的视频就可以在很多播放器查看了,如果你遇到不能播放qlv的情况,建议你先去试试这款迅捷视频转换器软件!好不好用你说了算。下面小编给大家简单讲解一下这款视频转换器的操作步骤。安装好这款软件之后,双击打开这款软件,就可以看到这款软件的功能列表,其中功能有视频转换、视频分割、视频美化、视频配乐等等功能,看起来功能种类繁多,不太好操作的样子,其实操作起来却非常简单。单击选择视频转换功能,点击软件中的添加文件,加入想要转换格式的视频文件文件。添加完文件之后,软件下方会出现输出格式和输出路径,格式选择MP4格式,分辨率一般是默认的,输出路径也可以选择默认的。最后进行格式转换就可以了,最后一步可以通过输出格式右边的全部转换来完成qlv和MP4的转换。操作流程大概就是这个样子,qlv转mp4格式转换器可以帮助你们解决腾讯qlv格式的困扰哦!试一试不吃亏,总要试一试才知道我说的是真是假啊!

October 17, 2019 · 1 min · jiezi

新南威尔士大学-COMP1511-Assignment1-课业解析

新南威尔士大学 COMP1511 Assignment1 课业解析题意: 使用C语言开发一款画图软件,允许用户在终端输入一串命令在画布上画画,输出一个存储像素信息的数组 解析: 用一个int型2维数组代表画布,每个元素代表电子画布的像素,二维数组初始化时值为4(4代表white,3代表light,2代表grey,1代表dark,0代表black);程序读取用户指令(一串int型数字,Ctrl+D结束输入),处理并输出数组的值。如指令draw line——画一条线段,用户输入1 5 5 5 180代表画一条线段,1代表画线段,起始点是(5,5),方向向下,长度为5个像素。 第一阶段要求实现画线段(水平和竖直方向)和正方形功能,画正方形例如2 6 6 4 0,2表示填充一个正方形,以(6,6)为起始点沿向上方向延伸4个像素为正方形对角线,填充该正方形区域;第二阶段要求实现绘制对角线方向的线段以及改变像素点颜色;第三阶段要求实现复制粘贴选定正方形区域的像素内容并粘贴到目标区域,例如4 0 0 3 135 0 10,4代表复制粘贴命令,从(0,0)开始,沿右下方延伸3个像素点,复制以此为对角线的正方形区域像素,并粘贴到以(0,10)为起始点的正方形区域上。 第三、四阶段要求实现绘制椭圆,例如0 0 0 3 3 5.5 1,0表示绘制椭圆,第一个焦点是(0,0),第二个焦点是(3,3),动点P到两焦点的距离和为2*5.5=11,绘制动点P的运动轨迹形成的椭圆,1表示填充椭圆内部所有像素,若为0则只画轮廓。 涉及知识点: 数组、类、while 更多可+讨论 唯心:g19963812037 pdf 2019/10/9 Assignment 1 - CS Painthttps://cgi.cse.unsw.edu.au/~... 1/30COMP1511 19T3 Assignment 1 - CS Paint COMP1511 19T3version: 1.1 last updated: 2019-10-09 14:00CS Paint²The year is 1985 . . . Microsoft has just released Windows 1.0 and packaged with it is a beautiful program called Paint, later referred toas MS Paint. For many people, this program is the beginning of a wonderful journey into the world of digital art.In this assignment, you will be implementing CS Paint, COMP1511's answer to the venerable drawing program. CS Paint is a programthat allows us to draw images to our terminal using a series of commands. The commands are made up of integers (and in later stages,doubles) and are typed directly into our program. Each command will make some change to a digital canvas, a space for drawing.CS Paint is already capable of setting up and drawing its canvas, it will be up to you to write code so that it can read commands andmake the correct changes in the canvas.Note: At time of release of this assignment (end of Week 3), COMP1511 has not yet covered all of the techniques and topics necessaryto complete this assignment. At the end of Week 3, the course has covered enough content to be able to read in a single command andprocess its integers, but not enough to work with two dimensional arrays like the canvas or be able to handle multiple commandsending in End-of-Input (Ctrl-D). We will be covering these topics in the lectures, tutorials and labs of Week 4.2019/10/9 Assignment 1 - CS Painthttps://cgi.cse.unsw.edu.au/~... 2/30The CanvasThe canvas is a two dimensional array (an array of arrays) of integers that represents the space we will be drawing in. We will bereferring to individual elements of these arrays as pixels on the canvas.The canvas is a fixed size and has N_ROWS rows, and N_COLS columns. Both of these are defined constants.Both the rows and columns start at 0, not at 1.The top left corner of the canvas is (0, 0) and the bottom right corner of the canvas is (N_ROWS - 1, N_COLS - 1). Note that weare using rows as the first coordinate in pairs of coordinates.For example, if we are given an input coordinate 5 10, we will use that to find a particular cell in our canvas by accessing the individualelement in the array: canvas5The integers in the pixels represent colours between black (which we call 0) and white (which we call 4). We will be starting with awhite canvas and drawing black onto it, but as we progress, we will also be using shades of grey (not 50 of them, just a few). Note thatthese colours assume you have white text on a black background.For reference, the shades are:Black (0):Dark (1): ░░Grey (2): ▒▒Light (3): ▓▓White (4): ██An empty canvas is shown below. In this documentation, we will always show you two versions of the output. In the "Output" you cansee the version that your program is expected to produce (numbers between 0 and 4).In the "Output (Stylized)" tab you can see a more readable version with the numbers converted to shades.Note that you are not expected to produce this stylized output - we have tools that will convert it for you. Your program only needs toprint the grid of numbers, as shown in the "Output" tab.2019/10/9 Assignment 1 - CS Painthttps://cgi.cse.unsw.edu.au/~... 3/30Empty Canvas ...

October 16, 2019 · 30 min · jiezi

精选-22-个-C-项目推荐新人练手首选

C/C++ 作为元老级的编程语言,任时光更迭依旧屹立不倒,哪怕如今炙手可热的AI,其底层也是用其编写。 那么作为新手该如何快速上手 C++ 呢?当然是敲代码啊!一切不写代码的学编程都是瞎搞。下面为大家精选了 22 个 C++ 项目,推荐新人练手首选! 1.C++ 实现基数树使用 C++ 实现Radix树:一种基于二进制表示的键值的查找树,尤其适合处理非常长的、可变长度的键值,Patricia 的基本思想是构建一个二叉树。 2.C++ 实现并行计算的K-Means聚类算法使用 C++ 实现一个完整的面向对象的可并行K-Means算法。 3.C++ 实现 STL 标准库和算法实现 C++ STL 的容器和算法的实现。 4.C++ 实现内存泄露检查器内存泄漏一直是 C++ 中比较令人头大的问题, 即便是很有经验的 C++ 程序员有时候也难免因为疏忽而写出导致内存泄漏的代码。本项目使用 C++ 实现一个内存泄漏检查器。 5.C++ 实现高性能内存池获得内存池所分配的内存速度高于从堆中获得分配的内存的速度,一个长期稳定运行的服务在追求极致的过程中,实现内存池是必不可少的。和标准库中的默认分配器一样,内存池本质上也是分配器,本项目设计并使用 C++实现一个高性能内存池。 6.C++ 实现高性能 RTTI 库RTTI 是运行时类型识别的英文缩写,C++ 本身提供了运行时类型检查的运算符 dynamic_cast 和 typeid,然而 dynamic_cast 的效率其实并不理想,需要牺牲一定性能。本项目将使用 C++ 手动实现一个高性能 RTTI 库。 7.C++ 实现智能指针使用C++语言实现智能指针的过程,来了解C++基本程序设计的方法,包括类的定义与使用,运算符的重载,模板类的使用方法,以及引用计数技术。 8.C++ 实现即时通信软件使用 C++ 实现一个具备服务端和客户端的即时通信聊天室,涉及网络编程,C++面向对象程序设计等知识。 9.C++实现课程管理系统使用C++ 实现一个课程管理系统,在这个过程中会介绍 C++ 11 的很多特性,同时可以熟悉 Linux下 的 C++ 。 ...

October 16, 2019 · 1 min · jiezi

C继承相关FAQ

Is it okay to convert a pointer from a derived class to its base class? ¶ Yes. An object of a derived class is a kind of the base class. Therefore the conversion from a derived class pointer to a base class pointer is perfectly safe, and happens all the time. For example, if I am pointing at a car, I am in fact pointing at a vehicle, so converting a Car to a Vehicle is perfectly safe and normal: ...

October 13, 2019 · 9 min · jiezi

如何定义两个类每一个类都把另一个类作为自己的一个成员

错误示范// bar.h#ifndef BAR_H#define BAR_H#include "foo.h"class bar {public: foo getFoo();protected: foo f;};#endif//foo.h#ifndef FOO_H#define FOO_H#include "bar.h"class foo {public: bar getBar();protected: bar b;};#endif//main.cpp#include "foo.h"#include "bar.h"intmain (int argc, char **argv){ foo myFoo; bar myBar;}编译报错,foo/bar都包含不完整的类型 $ g++ main.cppIn file included from foo.h:3, from main.cpp:1:bar.h:6: error: ‘foo’ does not name a typebar.h:8: error: ‘foo’ does not name a type解决方案用指针来指向需要包含的对象,使用前向声明来表示包含的那个类已存在指针的大小是确定的,因此类的大小也就可以确定避免了A需要B需要A这种循环引用的问题#ifndef BAR_H#define BAR_Hclass foo; // Say foo exists without defining it.class bar {public: foo* getFoo();protected: foo* f;};#endif #ifndef FOO_H#define FOO_Hclass bar; // Say bar exists without defining it.class foo {public: bar* getBar();protected: bar* f;};#endif 参考: ...

October 9, 2019 · 1 min · jiezi

NAT唯一五元组选取

使用iptable进行nat设置时,可以使用如下扩展选项: # SNAT 源地址转换,用在 POSTROUTING、INPUT 链--to-source [<ipaddr>[-<ipaddr>]][:port[-port]]--random # 映射到随机端口号,--random-fully # 映射到随机端口号(PRNG 完全随机化)--persistent # 映射到固定地址# DNAT 目的地址转换,用在 PREROUTING、OUTPUT 链--to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]--random # 映射到随机端口号--persistent # 映射到固定地址在内核中有如下几个标志与上面的选项对应: /* 指定了IP范围 */#define NF_NAT_RANGE_MAP_IPS (1 << 0)/* 指定了端口具体范围 */#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)/* 范围随机,使用secure_port函数进行源端口计算,对应于--random */#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)/* 映射到固定地址,同一个客户端使用相同的源地址,对应于--persistent */#define NF_NAT_RANGE_PERSISTENT (1 << 3)/* 完全随机,对应于--random-fully */#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)//上面几个标志有些可以组合使用//随机标志#define NF_NAT_RANGE_PROTO_RANDOM_ALL \ (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)//范围标志#define NF_NAT_RANGE_MASK \ (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \ NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \ NF_NAT_RANGE_PROTO_RANDOM_FULLY)构建nat信息 netfilter在两个地方会构建nat信息。一个是在命中nat规则后构建nat信息,另外一个是relate连接会构建nat信息,在expect函数中。构建nat信息都是使用函数nf_nat_setup_info进行构建,两者的差异在于range参数。后者由iptable规则设置,前者由help函数确定。nat会修改连接跟踪,仅仅修改应答方向。 ...

October 8, 2019 · 7 min · jiezi

NULL与nullptr二义性问题

在编程逻辑世界,有因必有果,如果一个结果含糊不定(二义性),显然是我们不想要的。C++11中引入nullptr是为了解决NULL的二义性问题。NULL二义性的体现void func(int) {}void func(int *) {}当函数调用func(NULL)时会是怎样执行?先看C++对NULL的定义: #if defined(__cplusplus) # define NULL 0 /* C++中使用0作为NULL的值 */#else # define NULL ((void *)0) /* C中使用((void *)0)作为NULL的值 */ #endif我们可以看到C++的NULL被宏定义为0,所以函数func(NULL)会因为NULL为0而导致调用func(int)函数,这是我们不想要的结果。 那怎么解决问题呢?使用nullptr(空指针常量),当函数调用func(nullptr)时则会调用func(int *)函数。

October 8, 2019 · 1 min · jiezi

使用libuv编写简单的TCP-Server

libuv使用基于事件的异步回调的方式来处理多个IO事件 因此使用一个线程就可以监控大量的文件(socket等文件类型)这种基于回调的编程风格在代码的可读性上比较差,这里梳理一下流程 创建TCP服务端的三个步骤: 创建TCP套接字, 绑定IP&&PORT,LISTEN然后运行uv_run一个新连接的建立的时候,on_new_connection被调用 如果accept成功,开始读套接字 否则处理错误 关闭资源数据读完之后,echo_read被调用,在这里我们调用写数据操作、写操作完成之后调用回调函数释放分配的堆上内存更简短的版本 接受新的套接字/有新的数据->读数据(EOF 套接字关闭,此TCP连接结束)- 写数据(回复) -> 回调释放内存 #include <stdio.h>#include <stdlib.h>#include <string.h>#include <uv.h>#define DEFAULT_PORT 7000#define DEFAULT_BACKLOG 128uv_loop_t* loop;struct sockaddr_in addr;typedef struct { uv_write_t req; uv_buf_t buf;} write_req_t;/// 释放资源的回调函数void free_write_req(uv_write_t* req) { write_req_t* wr = (write_req_t*)req; free(wr->buf.base); free(wr);}///分配空间存储接受的数据void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = (char*)malloc(suggested_size); buf->len = suggested_size;}/// 写完成后调用的函数/// 释放资源void echo_write(uv_write_t* req, int status) { if (status) { fprintf(stderr, "Write error %s\n", uv_strerror(status)); } free_write_req(req);}/// 将从socket套接字读取的数据放入request(req) 然后在写(buf->base nread 个字节)后 调用回调函数检查状态 释放req占用的内存/// 这里要注意 正确读取的时候req由回调函数处理/// 而EOF/其他错误发生的时候 需要关闭套接字 并释放buf->base所占据的内存/// EOF代表套接字已经被关闭/// 因为此时没有回调函数/// 异步回调很容易出错void echo_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { write_req_t* req = (write_req_t*)malloc(sizeof(write_req_t)); req->buf = uv_buf_init(buf->base, nread); uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write); return; } if (nread < 0) { if (nread != UV_EOF) fprintf(stderr, "Read error %s\n", uv_err_name(nread)); uv_close((uv_handle_t*)client, NULL); } free(buf->base);}/// 一个新连接的建立void on_new_connection(uv_stream_t* server, int status) { if (status < 0) { fprintf(stderr, "New connection error %s\n", uv_strerror(status)); // error! return; } uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t)); uv_tcp_init(loop, client); if (uv_accept(server, (uv_stream_t*)client) == 0) { uv_read_start((uv_stream_t*)client, alloc_buffer, echo_read); } else { uv_close((uv_handle_t*)client, NULL); }}int main() { loop = uv_default_loop(); uv_tcp_t server; uv_tcp_init(loop, &server); uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr); uv_tcp_bind(&server, (const struct sockaddr*) & addr, 0); int r = uv_listen((uv_stream_t*)&server, DEFAULT_BACKLOG, on_new_connection); if (r) { fprintf(stderr, "Listen error %s\n", uv_strerror(r)); return 1; } return uv_run(loop, UV_RUN_DEFAULT);}

October 7, 2019 · 1 min · jiezi

使用nullptr调用类的成员函数

注:这里只是为了探寻C++的内部机制,实践中解引用空指针是绝不应该出现的如果希望只通过类名调用成员函数,请声明静态成员函数 #include <iostream>using namespace std;class MyClass {private: int k;public: MyClass(int _k); ~MyClass(); int add(int i, int j); int addWithThis(int i, int j); virtual string myStrCat(string a, string b); static int static_add(int a, int b);};MyClass::MyClass(int _k = 0) : k(_k){}int MyClass::add(int i, int j){ return i + j;}int MyClass::addWithThis(int i, int j){ return i + j + this->k;}string MyClass::myStrCat(string a, string b){ return a + b;}int MyClass::static_add(int i, int j){ return i + j;}MyClass::~MyClass(){ cout << "dtor" << endl;}int main(int argc, char const* argv[]){ MyClass* p = nullptr; // 有可能会正确的执行 // 因为这个类的成员函数没有引用内部的成员(自己声明的或编译器声明的)) // 有可能编译器将这个类成员函数直接当作一个普通的函数处理 // p->add()直接跳转到此函数在进程中所处的地址处开始执行 cout << "add begin" << endl; cout << p->add(1, 2) << endl; //会引发段错误 因为这个函数内部使用了成员变量,会通过this指针访问 //而此时this指针显然是无效的 // cout << "addWithThis begin" << endl; // cout << p->addWithThis(1, 2) << endl; //会引发段错误 虚函数会通过虚函数指针去查找虚函数表,而此时虚指针显然是无效的 // cout << "myStrCat begin" << endl; // cout << p->myStrCat("must ", "fail") << endl; // 静态成员函数 可能会正常运行 类似第一个add cout << "static_add begin" << endl; cout << p->static_add(1, 2) << endl; return 0;}结论对于不涉及内部成员的函数调用(1,4),C++都把(nullptr)->操作直接翻译成了对于函数的调用. ...

October 7, 2019 · 1 min · jiezi

PixhawkPX4开发环境搭建Ubuntu-1804

本文主要记录了PX4环境在Ubuntu 18.04下的搭建过程,由于我在安装PX4环境之前已经先安装了ROS Melodic,而安装ROS的时候同时安装了gazebo,因此无法确定后面出现的问题是否由于先安装了ROS。 本文分为以下几个部分: 使用官方推荐的安装脚本进行安装解决安装过程中出现的问题下载PX4源码视频记录请注意: 以下安装过程全部在手机热点下完成,如果你是校园网用户并且下载速度过慢,请尝试使用手机热点。 1. 使用官方推荐的安装脚本进行安装打开PX4_Ubuntu安装页面,然后按照官方推荐的使用脚本进行安装,选择安装脚本ubuntu.sh。 1. 下载ubuntu.sh和requirements.txtwget https://raw.githubusercontent.com/PX4/Firmware/master/Tools/setup/ubuntu.sh wget https://raw.githubusercontent.com/PX4/Firmware/master/Tools/setup/requirements.txt2. 运行ubuntu.shsource ubuntu.sh3. 等待安装完成安装完成后,会在终端提示重启电脑。 安装完成后,你可以通过检查gcc版本来检查是否成功安装Nuttx $arm-none-eabi-gcc --version arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2017-q4-major) 7.2.1 20170904 (release) [ARM/embedded-7-branch revision 255204] Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.2. 解决安装过程中出现的问题1.python包安装过慢安装过程中,在安装python的依赖时,由于直接从国外的pip源进行获取,导致速度太慢。 ...

October 7, 2019 · 1 min · jiezi

取消宏定义

今天遇到一个问题是:重复宏定义。想到的解决方法是使用undef取消该重复宏。解决方法#ifdef xxx #undef xxx#endif或 #ifndef xxx #define xxx#endif

October 5, 2019 · 1 min · jiezi

墨尔本大学comp10002课业解析

墨尔本大学comp10002课业解析 HHappyyy简书作者2019-10-04 15:22打开App题意: 可视化网格路径,检测路径合法性,支持修复非法路径 解析: 第一阶段要求完成读取地图和分析数据功能。 上面的地图输入格式如下: 第一行代表地图行列,第二行和第三行分别表示出发点和终点,第四行至表示障碍, 表示障碍,表示障碍,标志地图信息加载完毕,$至最后一行表示路径信息。 该阶段要打印如下内容: 其中最后一行依据路径状态打印5条语句之一。 若路径起始点和地图出发点不同,打印Initial cell in the route is wrong! 若路径终点和地图终点不同,打印Goal cell in the route is wrong! 若每次移动超过两格,打印There is an illegal move in this route! 若路径上有障碍。打印There is a block on this route! 其它情况下打印The route is valid! 第二阶段要求把地图和路径可视化(使用ASCII码),以及遇到障碍重新寻路。 第三阶段展示路径修复的全过程。 涉及知识点: 动态内存、路径规划、数据结构(数组、链表等) 更多可+V讨论g19963812037

October 4, 2019 · 1 min · jiezi

时间复杂度与空间复杂度

前言衡量一个算法的效率,可以从时间复杂度T(n) 和 空间复杂度S(n) 去分析。时间复杂度衡量算法执行的时间,空间复杂度衡量算法执行消耗的内存大小,默认情况下都是分析最坏情况下的复杂度。首先引入一个辅助函数f(n),可理解为算法中代码的执行次数(也称为频度),如下面代码: 代价 次数for(int i = 0; i < n; i++){ c1 n int j = i; c2 n-1 cout << j << endl; c3 n-1 }此时f(n)=c1n + c2(n-1) + c3(n-1)。 时间复杂度1.概念 对于时间复杂度来说,代价即为当前行代码执行时间,当n趋于无穷大的时候,记T(n)=O(f(n)),被称为算法的渐进时间复杂度,又简称为时间复杂度,大O给出的是函数f(n)唯一的的渐进上界。因为n趋于无穷大,所以f(n)的常数项变化对整体影响不大,直接去掉。所以对于上述代码,T(n)=O(n)。 2.常见时间复杂度 常数阶T(n)=O(1)int i = 0; 线性阶T(n) = O(n)for(int i = 0; i < n; i++);平方阶T(n)=O(n2)for(int i = 0; i < n; i++) for(int j = 0; j < n; j++)对数阶T(n)=O(logn)int i = 2;while(i < n) i *= 2;这里说一下,设循环次数为t,2^t < n ==> t < logn。 ...

October 4, 2019 · 1 min · jiezi

算法与数据结构堆二叉堆

前言堆(二叉堆),一种动态的树型结构,一种除了底层外,完全被填满的二叉树结构。因此,堆一般是基于数组去实现的,它不会出现数组中很多空缺的现象,而造成空间浪费。如下是一个完全二叉树: 它可以用数组表示为[10,7,2,5,1],若以k表示当前数组的索引,那么: 其父节点:floor((k-1)/2)其左孩子:2k+1其又孩子:2k+2结合上图,堆的性质如下: 堆必须是完全二叉树;任一节点要么比其子树节点大,要么小;根据上面性质,堆被分为最大堆(大顶堆)和最小堆(小顶堆)。堆的主要用途: 构建优先队列;支持堆排序;快速找出集合中的最大值或最小值。堆结构的基本操作(以最大堆为例,本文均使用vector容器存储数组,假设vector容器的基本操作时间复杂度为(1)): MaxHeap:维护最大堆的性质,时间复杂度O(nlgn);BuildHeap:从无序的输入数据构造一个最大堆,时间复杂度为O(n);堆结构的基本操作1.MaxHeap: 输入一个数组A和一个下标i,使不满足最大堆性质的A[i]逐级下降,直到满足。 void MaxHeap(vector<int> &A, int i){ int max; //存储父节点和其子树节点中的最大值下标 int lef_child = 2i; //左孩子 int rig_child = 2i + 1; //右孩子 //左孩子大于父节点 if(lef_child <= A.size() && A[lef_child] > A[i]) max = lef_child; else max = i; //右孩子更大 if(rig_child <= A.size() && A[rig_child] > A[max]) max = rig_child; //将最大值上移至父节点 if(max != i){ //交换 int temp = A[i]; A[i] = A[max]; A[max] = temp; //更新了数组,需继续查看当前元素是否满足最大堆性质 MaxHeap(A, max); }}因为每一个节点的子树节点数(包括孩子的孩子)至多为2n/3(n是整个树的节点数,最坏情况即节点为根节点,且底层大于等于半满),所以该算法的时间复杂度为T(n) <= T(2n/3) + (1)通过主方法求解得T(n)=O(lgn),最后因为含n个元素的堆高为O(lgn),所以其时间复杂度又可以表示为O(h)。 ...

October 4, 2019 · 1 min · jiezi

flutter-SharedPreferences桌面插件

flutter可以构建跨平台的多端应用, 正好开发的应用需要桌面版本, 那就尝试传说中的无缝移植. 然而刚开始就遇到了大麻烦: 移动端普遍使用的SharedPreferences在桌面端只有macOS有实现! 虽然引入shared_preferences: ^0.5.3+4在编译时没有问题, 但windows和linux平台在运行时会抛出[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)的异常. 这"无缝"来的太猛, 有点措手不及...等着官方出正式版本断然不行的, 必须得自行添加在平台层的实现了. 好在桌面端可以以插件的方式与shared_preferences对接上, 结合在macOS上的实现及提供的示例程序总算给搞出来了! 以linux为例, 写一下久违的C++. 开发环境: 之前尝试最新的flutter1.9运行集成的桌面应用,但失败了, 所以开发环境在flutter1.8, 这是确定可以运行起来的 flutterSDK: v1.8.0@stable flutter Desktop: c183d46798b9642b8d908710de1e7d14a8573c86@master pubspec.yaml: dependencies: shared_preferences: ^0.5.3+4运行以下命令确保可以运行起来或者参照这篇文章 (flutterSDK安装不再另行说明): git clone https://github.com/google/flutter-desktop-embedding.git desktopcd desktop/exampleflutter run我们就是基于example应用把SharedPreferences插件开发出来. 插件结构所有的插件位于desktop仓库根目录下的plugins, 其中的flutter_plugins特指的是flutter在其它端(android/iOS/web)也可以用的插件, 其余的表示只在桌面端(macOS/linux/windows)用到的插件, 需要实现的SharedPreferences就在plugins/flutter_plugins/shared_preferences_fde下,可以看到只有macos的目录.所以开始新建linux平台上的插件: 创建目录及文件借助已经有url_launcher_fde mkdir -p plugins/flutter_plugins/shared_preferences_fde/linux && cd plugins/flutter_plugins/shared_preferences_fde/linuxcp ../../url_launcher_fde/linux/Makefile .cp ../../url_launcher_fde/linux/url_launcher_fde_plugin.{cc,h} .插件命名将Makefile中的url_launcher_fde_plugin改成shared_preferences_fde_plugin, 这是编译插件所需要的Makefile, 只需改这一个名称即可.本地cpp文件改成shared_preferences_fde_plugin.{cc,h}, 同时类名和宏也改成相应的名称, 最好用sed搜索一起替换 FLUTTER_PLUGIN_EXPORT void SharedPreferencesRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar);class SharedPreferencesPlugin : public flutter::Plugin { virtual ~SharedPreferencesPlugin();private: SharedPreferencesPlugin();}...RegisterWithRegistrar方法里有个通道注册的名称"plugins.flutter.io/shared_preferences", 这和异常抛出时的名称是一致的. ...

October 3, 2019 · 4 min · jiezi

Jsoncpp-所见即所得使用范例

jsoncpp版本: jsoncpp-1.8.4 基本操作#include <json/json.h>int main() { Json::Value root; root["key1"] = 1; root["key2"] = "good"; root["indent"]["length"] = 2; root["indent"]["use_space"] = true; Json::Value v(Json::arrayValue); v[0] = "clojure"; v[1] = "json"; v[2] = "xml"; root["key3"] = v; return 0;}遍历与类型 for (auto i = root.begin(); i != root.end(); i++) { Json::Value& obj = *i; const std::string name = i.name(); std::cout << name << "-" << i.key() << ": " << obj.isArray() << "\n"; }读入 Json::Value root; std::ifstream infile; infile.open("your.json", std::ios::in); try { infile >> root; } catch (std::exception& e) { root = Json::objectValue; } infile.close();这里用的是文件的读入流, 也可以是文本的stringstream ...

October 3, 2019 · 1 min · jiezi

string与char-小知识

在C++中,使用字符串相对于char *,我更倾向于使用string。其优点更安全,更多的易用接口。 简化的string类似于下列实现,除了比char 多几个字节的占用空间外几乎一样,但比char 省心很多,这也是C++的"++"表现之一。 struct string { char* data;; size_t length;};

October 2, 2019 · 1 min · jiezi

算法分析与设计C-4过河卒

总时间限制: 1000ms 内存限制: 128000kB 描述 棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的某一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。 输入B点的坐标(n,m)以及对方马的坐标(X,Y)输出从A点能够到达B点的路径的条数。 样例输入6 6 3 2 样例输出17 来源noip普及组2002 #include<iostream>using namespace std;long long fun(long long n,long long m, long long x, long long y){ long long a[21][21]; int dx[9] = {0, 2, 2, 1, 1, -2, -2, -1, -1}; int dy[9] = {0, 1, -1, 2, -2, 1, -1, 2, -2}; bool g1[21][21]; fill(g1[0],g1[0]+21*21,true); for (int i=0; i<=8; i++){ int xx=x+dx[i],yy=y+dy[i]; if (xx>=0 && xx<=n && yy>=0 && yy<=m){ g1[xx][yy]=false; } } // for(int g=0; g<=n; g++) // { // for(int h=0; h<=m; h++) // { // cout<<g1[g][h]<<" "; // } // cout<<endl; // } fill(a[0],a[0]+21*21,0); a[0][0]=1LL; for(int g=0; g<=n; g++) { for(int h=0;h<=m;h++) { if(g1[g][h]){ if(g==0&&h>=1){ a[0][h]=a[g][h-1]; } if(h==0&&g>=1){ a[g][0]=a[g-1][h]; } if(g>=1&&h>=1){ a[g][h] = a[g-1][h]+a[g][h-1]; } } } } // cout<<endl; // for(int g=0; g<=n; g++) // { // for(int h=0;h<=m;h++) // { // cout<<"("<<g<<","<<h<<"):"<<a[g][h]<<" "; // } // cout<<endl; // } return a[n][m];}int main(){ long long n,m,x,y; cin>>n>>m>>x>>y; cout<<fun(n,m,x,y)<<endl; return 0;}踩坑日志 ...

October 1, 2019 · 1 min · jiezi

算法分析与设计C-1猴子吃桃

总时间限制: 1000ms 单个测试点时间限制: 100ms 内存限制: 65535kB描述猴子第一天摘下若干个桃子,当即吃了一半,好不过瘾,又多吃了一个。第二天早上又吃了剩下的桃子的一半,又多吃了一个。以后每天都吃了前一天剩下的一半零一个,到第10 天早上想再吃的时候,就剩下一个桃子。求第一天共摘多少个桃子。输入无输出第一天摘的桃子数样例输入无样例输出1534**#include<iostream>using namespace std;int main(){ //f(n+1)=f(n)/2-1; int m = 1; for(int i=0;i<10;i++){ m = 2*(m+1); } cout<<m; return 0;}

September 30, 2019 · 1 min · jiezi

算法分析与设计C-2递归爬楼梯

总时间限制: 2000ms 单个测试点时间限制: 1000ms 内存限制: 512kB描述小明爬楼梯,他可以每次走1级或者2级,输入楼梯的级数,求不同的走法数。 例如:楼梯一共有3级,他可以每次都走一级,或者第一次走一级,第二次走两级;也可以第一次走两级,第二次走一级,一共3种方法。 输入输入包含若干行正整数,第一行正整数K代表数据组数;后面K行,每行包含一个正整数N,代表楼梯级数,1 <= N <= 30输出不同的走法数,每一行输入对应一行输出 样例输入35810样例输出83489来源Tiger Zhang#include<iostream>using namespace std;int f(int n){ // if(n==1){ // return 1; // }else if(n==2){ // return 2; // }else{ // return f(n-1)+f(n-2); // } int tmp[n+1]; tmp[1] = 1; tmp[2] = 2; for(int i=3;i<=n;i++){ tmp[i] = tmp[i-1]+tmp[i-2]; } return tmp[n];}int main(){ int K; cin>>K; // f(n) = f(n-1) + f(n-2) for(int i=0; i<K; i++){ int n; cin>>n; cout<<f(n)<<endl; } return 0;}

September 30, 2019 · 1 min · jiezi

算法分析与设计C汉诺塔实现

递归算法三:汉诺塔问题描述移动规则:每次只能移动一个圆盘;圆盘可以插在A、 B和C中的任何一个塔座上;任何时刻都不能将一个较大的圆盘压在较小的圆盘之上。 分析边界条件只有一个圆环时,只需将圆环从第一座塔移到第三座塔递归条件1、从第一座塔把n-1个圆环移到第二座塔,用第三座塔做辅助2、从第一座塔把第n个圆环移到第三座塔3、从第二座塔把n-1个圆环移到第三座塔,用第一座塔做辅助 代码 简单汉诺塔递归实现#include<iostream>using namespace std;void move(char from, char to){ cout<<"Move"<<from<<"to"<<to<<endl;}void hanoi(int n, char first, char second, char third){ if(n==1){ move(first, third); }else{ hanoi(n-1, first, third, second); move(first, third); hanoi(n-1, second, first, third); }}int main(){ int m; cout<<"the number of diskes:"; cin>>m; cout<<"move "<<m<<" diskes:\n"; hanoi(m,'A','B','C'); return 0;}汉诺塔递推实现#include<iostream>using namespace std;int main(){ int m; cin>>m; long long p = 0; for(int i=0; i<m; i++){ p=2*p+1; } cout<<2*p<<endl; return 0;}递推和递归都可以实现汉诺塔 但无法完美通过openjudge上的问题,可能是因为当数据很大时,数据溢出,可能需要通过自己编写大整数运算的算法来解决问题。这个下一篇文章单独写出。

September 30, 2019 · 1 min · jiezi

Binder驱动之死亡通知

在Binder通信建立后,Client端可能需要知道Server端的存活状态。当Server端挂掉时,Client端需要清理与通信相关的数据和行为,这个清理过程就是通过Binder死亡通知机制实现的。 注册死亡通知应用层通过调用BpBinder::linkToDeath()来注册死亡通知。Native Binder通信可以直接调用这个接口,Java通信需要通过Jni来调用。 frameworks/base/core/jni/android_util_Binder.cppstatic void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj, jobject recipient, jint flags) // throws RemoteException{ ...... // 获取BpBinder对象 IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject); ...... // 只有远程传输需要注册死亡通知 if (!target->localBinder()) { // 获取死亡通知队列 DeathRecipientList* list = (DeathRecipientList*) env->GetLongField(obj, gBinderProxyOffsets.mOrgue); // 创建JavaDeathRecipient对象,将其加入到死亡回收队列 sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list); // 调用BpBinder::linkToDeath()来建立死亡通知 status_t err = target->linkToDeath(jdr, NULL, flags); ...... } }处理死亡通知的是JavaDeathRecipient,它继承IBinder::DeathRecipient,接收到死亡通知时会回调binderDied()。 frameworks/base/core/jni/android_util_Binder.cppclass JavaDeathRecipient : public IBinder::DeathRecipient{public: JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list) : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), mObjectWeak(NULL), mList(list) { ...... // 加入到回收队列 list->add(this); android_atomic_inc(&gNumDeathRefs); // 增加对象引用计数,当创建对象个数达到200时强行出发GC incRefsCreated(env); } void binderDied(const wp<IBinder>& who) { if (mObject != NULL) { JNIEnv* env = javavm_to_jnienv(mVM); // 调用Java的sendDeathNotice方法 env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice, mObject); ...... // 释放全局引用,增加弱引用,以便GC回收 mObjectWeak = env->NewWeakGlobalRef(mObject); env->DeleteGlobalRef(mObject); mObject = NULL; } }Java的注册过程最终也是指向到BpBinder中。死亡通知的注销也同样在这里,一起看一下。 ...

September 30, 2019 · 4 min · jiezi

一篇入门算法与数据结构

算法与数据结构开篇你真的会数据结构吗?公司开发一个客服电话系统,小菜需要完成客户排队模块的开发,经过三次修改:第一次:小菜使用了数据库设计了一张客户排队表,并且设置了一个自动增长的整型id字段,来一个用户,就在这张表的末尾插入一条数据,等客服系统一空闲,就将表中最前的的客户提交,然后删除这条记录。 实时排队模块,在内存中实现即可,无序用数据库第二次:小菜用数组变量重新实现了这个功能,害怕数组不够大,选择远大于实际情况的100作为数组长度 数组虽然可以满足一定需求,但是需要考虑溢出问题,以及新增和删除后的数据移动,显然不是很方便第三次:小菜使用了数据结构中的 “队列结构” 终于满足了需求 说明:此例子归纳参考自《大话数据结构》为什么你的程序比别人的慢?(算法问题)来看一个问题: 公元前五世纪,我国古代数学家张丘建在《算经》一书中提出了“百鸡问题”:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?请设计一个“高效”的算法求解。 也就是说: 买一只公鸡要五文,买一只母鸡要三文,而一文可以买三只小鸡,共有100文,问公鸡母鸡小鸡各能买几只? 话不多说,我们先给出一种最容易想到的方式,也就是列两个三元方程组 也就是满足鸡的总数为100,同时花钱数也为100,我们来看一下代码实现 方式一: //i j k 分别代表公鸡 母鸡 雏鸡的数量 //20、34、300 为100元最多能买公鸡、母鸡、小鸡的数量for (int i = 0; i < 20; i++) { for (int j = 0; j < 34; j++) { for (int k = 0; k < 300; k = k + 3) { //k = k + 3 是为了满足小鸡实际存在的要求 if (5*i + 3*j + k/3 == 100 && i + j + k == 100) { cout << "公鸡 母鸡 雏鸡 数量分别为:" << i <<", "<< j <<", "<< k << endl; } } }}我们用了三层循环,有没有办法能简化以下代码呢?能不能减少循环层数呢?这显然是可行的,上面小鸡的数量这一层明显可以省略的,因为总数是已知的 ...

September 20, 2019 · 3 min · jiezi

17-cpp中类的常见特性

1.7 cpp中类的常见特性返回目录 1 面向对象技术上一节 1.6 cpp的常见特性下一节 1.8 继承 public和privatepubulic:公有访问限定符,具有类外交互能力,类内部外部都可使用;private:私有访问限定符,仅允许本类中函数访问,不可在类外使用; protected:保护访问限定符,仅允许本类及本类的派生类访问。 源代码/* publicAndPrivate.cpp 公有私有访问权限实例 */#include <iostream>#include <string>class ExampClass{private: std::string priData;public: ExampClass(); // 默认构造函数,不作修改 ~ExampClass(); // 默认析构函数,不作修改 std::string pubData; void addData(std::string priData, std::string pubData); // 添加数据 void displayPriInfo(); // 显示私有成员};ExampClass::ExampClass(){ /* 构造函数里空空如也 */}ExampClass::~ExampClass(){ /* 析构函数里空空如也 */}void ExampClass::addData(std::string priData, std::string pubData){ this->priData = priData; this->pubData = pubData;}void ExampClass::displayPriInfo(){ std::cout << priData << std::endl;}int main(){ ExampClass exobj; exobj.addData("这是私有数据", "这是公有数据"); exobj.displayPriInfo(); // 显示刚刚添加的数据,我们可以用类内部函数访问公有和私有成员 std::cout << exobj.pubData << std::endl << std::endl; // 显示类中的公有成员 // exobj.priDate = "我修改了私有数据"; // <-- 这是错误的,无法直接访问私有访问权限的成员 exobj.pubData = "我修改了公有数据"; // 这是正确的,我们可以直接访问和修改公有访问权限的成员 exobj.displayPriInfo(); // 输出 std::cout << exobj.pubData << std::endl; return 0;}编译运行这是私有数据这是公有数据这是私有数据我修改了公有数据构造函数和析构函数1.5中,我们提到了类中的构造函数和析构函数。 ...

September 20, 2019 · 5 min · jiezi

18-继承

1.8 继承返回目录 1 面向对象技术上一节 1.7 cpp中类的常见特性下一节 1.9 多态 概念: 继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。 介绍: 继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。 另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。 特点: 基类成员publicprotectedprivate不可访问公有继承publicprotected不可访问不可访问保护继承protectedprotected不可访问不可访问私有继承privateprivate不可访问不可访问单继承接下来举个简单的单继承例子: 源代码/* Extends.cpp,这是个单继承实例 */#include <iostream>#include <string>/* 这个是基类,或者叫父类 */class Animal{protected: // 保护类型,只能被自己或者自己的子类调用 std::string name; std::string getSex() const; // 获取性别,const限制函数不能修改数据成员 int getAge() const; // 获取年龄private: std::string sex; // 性别 int age; // 年龄public: Animal(std::string name_l, std::string sex_l, int age_l) :name(name_l), sex(sex_l), age(age_l){} // 可以用这种方式为数据成员赋值 ~Animal() { /* 或者直接在类中实现函数 */ }};std::string Animal::getSex() const{ return sex; // 返回性别}int Animal::getAge() const{ return age; // 返回年龄}/* 这个叫派生类,或者叫子类 */class Cat: public Animal{private: std::string words; // 存储叫声public: Cat(std::string name_l, std::string sex_l, int age_l, std::string words_l) :Animal(name_l, sex_l, age_l), words(words_l){}; // 子类在构造函数中需要调用父类构造函数,除非父类有无参构造函数 ~Cat(){}; void speak() const; // 输出一段话};void Cat::speak() const{ std::cout << "我叫"; std::cout << name << std::endl; std::cout << "性别是"; // std::cout << sex << std::endl; // 不允许直接访问基类的私有成员 std::cout << getSex() << std::endl; // 可以访问基类保护成员 std::cout << "今年"; std::cout << getAge() << "岁了" << std::endl; std::cout << "我的叫声是:"; std::cout << words << std::endl; // 可以访问本类私有成员}int main(){ Cat cat("小花", "雌", 2, "喵喵喵"); cat.speak(); return 0;}编译运行我叫小花性别是雌今年2岁了我的叫声是:喵喵喵例子中,我们创建了一个动物类,并且派生了一个猫类。 ...

September 20, 2019 · 2 min · jiezi

19-多态

1.9 多态返回目录 1 面向对象技术上一节 1.8 继承 多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。 简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。 多态性在C++中是通过虚函数实现的。 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。 C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。 虚基类虚基类使得其派生类在间接地多次继承本类时,只继承本类的一份成员,避免出现多次继承产生多份拷贝的二义性。 源代码/* VirtualBaseClass.cpp 虚基类实例*/#include <iostream>class Animal // 定义动物类{protected: int age; // 存储年龄public: Animal(int age_t):age(age_t){}; // 参数列表初始化年龄 ~Animal(){}; // 析构函数,这里用不上};class Bird:virtual public Animal // 鸟虚继承动物类{private: int flyHight; // 定义鸟飞的高度protected: int getFlyHight(); // 获取鸟飞的高度public: Bird(int flyHight_t, int age_t):flyHight(flyHight_t), Animal(age_t){}; // 参数列表初始化高度和年龄 ~Bird(){}; // 析构函数};int Bird::getFlyHight(){ return flyHight; // 返回鸟飞的高度}class Fish:virtual public Animal // 鱼虚继承动物类{private: int divingDepth; // 定义鱼潜水深度protected: int getDivingDepth(); // 获取鱼潜水深度public: Fish(int divingDepth_t, int age_t):divingDepth(divingDepth_t), Animal(age_t){}; // 参数列表初始化深度年龄 ~Fish(){}; // 析构函数};int Fish::getDivingDepth(){ return divingDepth; // 返回鱼潜的深度}class WaterBird:virtual public Bird, Fish // 多重继承鸟和鱼,此时间接地两次继承了动物类,但是之前鸟和鱼继承时使用了虚继承,因此此处默认虚继承,但是为了可读性,这里最好写上virtual{public: WaterBird(int flyHight_t, int divingDepth_t, int age_t):Bird(flyHight_t, 5), Fish(divingDepth_t, 6), Animal(age_t){}; ~WaterBird(){}; void displayInfos();};void WaterBird::displayInfos(){ std::cout << "种族:飞鱼" << std::endl; std::cout << "年龄:" << age << std::endl; std::cout << "最高飞行高度:" << getFlyHight() << std::endl; std::cout << "最大潜水深度:" << getDivingDepth() << std::endl;}int main(){ WaterBird wb(200, 50, 4); wb.displayInfos(); return 0; }编译运行种族:飞鱼年龄:4最高飞行高度:200最大潜水深度:50我们发现,在水鸟内继承鸟和鱼的age时,初始化的值分别为5和6,然后虚继承动物类的age时,赋值为4,最终的结果为4。 ...

September 20, 2019 · 2 min · jiezi

14-cpp编程基础

1.4 cpp编程基础返回目录 1 面向对象技术上一节 1.3 面向对象设计下一节 1.5 结构体和类 HelloWorld废话不多说,敲代码的第一步,当然是HelloWorld。 源代码/* helloWorld.cpp,用C++写HelloWorld */#include <iostream>int main(){ std::cout << "Hello" << std::ends << "World!" << std::endl; // 输出语句 return 0;}源文件命名为HelloWorld.cpp。 编译你可以使用IDE自动编译,或者使用命令行手动编译(我是在Windows环境)。 我先使用vscode编写程序,在vscode中使用g++编译源文件;然后我使用命令行编译。 首先需要安装MinGW,并且配置环境变量;在win10的cmd中,使用cd /d 源文件目录命令定位到源文件目录;使用g++ HelloWorld.cpp -o HelloWorld 命令对HelloWorld.cpp源文件进行编译,并将编译完成的可执行文件重命名为HelloWorld.exe; g++ 源文件名.cpp可以用来编译,编译完成的可执行文件名为a.exe;上一条命令后面接-o HelloWorld用来将编译的a.exe重命名为HelloWorld.exe。运行在命令行中输入HelloWorld直接运行HelloWorld.exe。编译运行结果: Hello World!解释如你所见,只需包含iostream头文件,就可以使用: std::cout函数进行输出。std::ends表示字符串的结束,并且添加空格。std::endl终止一行,刷新缓冲区,并且添加换行。要输出的内容跟在<<后面即可,不必像C语言的printf()一般,要补充格式控制符。 同时,使用cout,同样可以使用转义字符,如n。 在C++中,std其实就是standard——标准的意思。这里涉及到一个命名空间的问题。 namespace即“命名空间”,也称“名称空间” 、”名字空间”。这是一种代码组织的形式,通过名称空间来分类,区别不同的代码功能,同时也是所有类的完全名称的一部分。 std::cout的意思就是std这个命名空间中的cout函数。 简单的输入输出输入输出是我们接触编程这一门技术的入门小菜,接下来我们用一个简单的加法函数来展示C++的简单输入输出。 源代码/* io.cpp,C++输入输出实例:输入两个整数输出其和 */#include <iostream>int addNum(int num_1, int num_2){ return num_1 + num_2; // 返回传入两个变量之和的值}int main(){ int num_1, num_2; std::cout << "请输入两个整数用来求和:\n"; // 输出提示语句 std::cin >> num_1 >> num_2; // 输入两个整型变量 std::cout << num_1 << " + " << num_2 << " = " << addNum(num_1, num_2); // 调用求和函数,得到两个整数的和并输出 return 0;}编译运行输出请输入两个整数用来求和:;输入 1 1;输出 1 + 1 = 2。很简单。std::cin表示输入,上面也讲了,std::cout表示输出。 ...

September 20, 2019 · 2 min · jiezi

13-面向对象设计

1.3 面向对象设计返回目录 1 面向对象技术上一节 1.2 面向对象分析下一节 1.4 cpp编程基础 面向对象设计(Object-Oriented Design,OOD)是将OOA所创建的分析模型转化为设计模型,其目标是定义系统构造蓝图。在模型到执行环境的转换中,需要考虑实现问题加以调整和增补,通过编程语言特性等因素调整比如类结构等具体的结构。 OOD与OOA一致,同样应遵循抽象、信息隐蔽、功能独立、模块化等设计准则。 感觉理论比较枯燥的,可以直接跳转到1.4开始实际操作。 面向对象设计面向对象设计的活动OOD在复用OOA模型的基础上,包含与OOA对应如下五个活动: 识别类及对象;定义属性;定义服务;识别关系;识别包。设计期必须充分考虑系统的稳定性,如目标环境所需的最大存储空间、可靠性和响应时间等,所有这些都会影响系统的结构;OOD应该尽可能隔离实现条件对系统的影响,对不可隔离的因素按实现条件调整OOA模型。面向对象设计的原则五大原则: 单一责任原则(Single Responsibility Principle,SRP);开放-封闭原则(Open&Close Principle,OCP);里氏替换原则(Liskov Substitution Principle,LSP);依赖倒置原则(Dependence Inversion Principle,DIP);接口分离原则(Interface Segregation Principle,ISP)。其它原则 重用发布等价原则(Release Reuse Equivalency Principle,REP);共同封闭原则(Common Closure Principle,CCP);共同重用原则(Common Reuse Principle,CRP);无环依赖原则(Acyclic Dependencies Principle,ADP);稳定依赖原则(Stable Dependencies Principle,SDP);稳定抽象原则(Stable Abstractions Principle,SAP)。面向对象程序设计面向对象程序设计(Object-Oriented Programming,OOP)的实质是选用一种面向对象程序设计语言(Object-Oriented Programming Language,OOPL)。OOP采用对象、类及其相关概念所进行的程序设计。它的关键在于加入了类和继承性,从而进一步提高了抽象程度。 特定的OOP概念一般是通过OOPL中特定的语言机制来体现的。 类继承和类层次结构对象、消息传递和方法对象自身引用重置类属类无实例的类面向对象测试在所有开发系统中都是根据规范说明来验证系统设计的正确性。程序验证应尽可能早地开始。程序调试步骤是从最底层开始,从单元测试、综合测试到系统测试。 单元测试是系统构件的分体测试;综合测试即将测试好的系统构件接起来看它们之间相互作用的正确性;系统测试是对整个系统的测试,包括软件系统所在相关环境的测试。综合测试尽可能在早期阶段处理,因为通信是系统开发的实质。 一般来说,对面向对象软件的测试可分为下列4个层次进行: 算法层; 测试类中定义的每个方法,基本上相当于传统软件测试中的单元测试。类层; 测试封装在同一个类中的所有方法与属性之间的相互作用;在面向对象软件中类是基本模块,因此可以认为这是面向对象测试中所特有的模块测试。模板层; 测试一组协同工作的类之间的相互作用,大体上相当于传统软件测试中的集成测试,但是也有面向对象软件的特点(例如,对象之间通过发送消息相互作用)。系统层。 把各个子系统组装成完整的面向对象软件系统,在组装过程中同时进行测试。返回目录 1 面向对象技术上一节 1.2 面向对象分析下一节 1.4 cpp编程基础 参考资料: 《软件设计师教程》第五版百度百科

September 20, 2019 · 1 min · jiezi

15-结构体和类

1.5 结构体和类返回目录 1 面向对象技术上一节 1.4 cpp编程基础下一节 1.6 cpp的常见特性 结构体源代码/* struct.cpp 结构体实例 */#include <iostream>#include <string>struct Student{ std::string name; // 姓名 std::string sex; // 性别 int age; // 年龄}typedef Student;void STUDENT_dispalyInfos(Student student){ /* 这个函数用来显示学生信息 */ std::cout << "学生信息" << std::endl; std::cout << "姓名:" << student.name << std::endl; std::cout << "性别:" << student.sex << std::endl; std::cout << "年龄:" << student.age << std::endl;}int main(){ Student XiaoMing; XiaoMing.name = "小明"; XiaoMing.sex = "男"; XiaoMing.age = 18; STUDENT_dispalyInfos(XiaoMing); return 0;}编译运行学生信息姓名:小明性别:男年龄:18类源代码/* class.cpp 类实例 */#include <iostream>#include <string>class Student{private: std::string name; // 姓名 std::string sex; // 性别 int age; // 年龄public: Student(); // 这是构造函数 Student(std::string name, std::string sex, int age); // 构造函数重载 ~Student(); // 析构函数 void Init(std::string name, std::string sex, int age); // 初始化函数 void displayInfos(); // 显示成员信息的函数};Student::Student(){ std::cout << "-++类实例化时,会先调用构造函数" << std::endl; this->name = ""; this->sex = ""; this->age = 0; std::cout << "+++" << name << "初始化完成" << std::endl;}Student::Student(std::string name, std::string sex, int age){ std::cout << "-++类实例化时,会先调用构造函数" << std::endl; this->name = ""; this->sex = ""; this->age = 0; std::cout << "+++" << name << "初始化完成" << std::endl; Init(name, sex, age);}Student::~Student(){ std::cout << "-++对象销毁时,会调用析构函数" << std::endl; std::cout << "+++" << this->name << "已销毁" << std::endl;}void Student::displayInfos(){ std::cout << "————学生信息————" << std::endl; std::cout << "——姓名:" << this->name << std::endl; std::cout << "——性别:" << this->sex << std::endl; std::cout << "——年龄:" << this->age << std::endl; std::cout << "————————————" << std::endl;}void Student::Init(std::string name, std::string sex, int age){ std::cout << "--+" << name << "完成初始化" << std::endl; this->name = name; this->sex = sex; this->age = age;}int main(){ Student XiaoMing; // 类实例化时调用无参的构造函数 XiaoMing.Init("小明", "男", 20); // 手动调用初始化函数 Student XiaoHong("小红", "女", 20); // 类实例化时调用有参的构造函数,直接初始化完成 XiaoMing.displayInfos(); // 显示信息 XiaoHong.displayInfos(); return 0;}编译运行-++类实例化时,会先调用构造函数+++初始化完成--+小明完成初始化-++类实例化时,会先调用构造函数+++小红初始化完成--+小红完成初始化————学生信息——————姓名:小明——性别:男——年龄:20————————————————学生信息——————姓名:小红——性别:女——年龄:20————————————-++对象销毁时,会调用析构函数+++小红已销毁-++对象销毁时,会调用析构函数+++小明已销毁可以看到,结构体与类有很大的相似之处,但是类内部允许有函数,并且有默认的构造函数(在类实例化时自动运行,可以重载)和默认的析构函数(对象生命周期结束时自动运行,回收资源)。 ...

September 20, 2019 · 2 min · jiezi

12-面向对象分析

1.2 面向对象分析返回目录 1 面向对象技术上一节 1.1 面向对象基础下一节 1.3 面向对象设计 面向对象分析(Object-Oriented Analysis,OOA)的目的是为了获得对应用问题的理解。理解的目的是确定系统的功能、性能要求。 面向对象分析包含5个活动:认定对象、组织对象、描述对象间的相互作用、确定对象的操作、定义对象的内部信息。 感觉理论比较枯燥的,可以直接跳转到1.4开始实际操作。 认定对象在应用领域中,按自然存在的实体确立对象;在定义域中,首先将自然存在的名词作为一人对象;通过实体间关系寻找对象,这是系统稳定的基础;在学生管理系统中,实质性对象应当包含学生学号姓名等数据,课桌账单则不是实质性对象。组织对象分析对象间的关系,将相关对象抽象成类;对象抽象成类的目的是为了简化关联对象;利用类的继承性建立具有继承性层次的类结构;考虑一个对象是另一个对象的一部分; 例如电脑由CPU、存储器等构成,后两者是前者的组成部分;系统的行为和信息间的分析过程是一种迭代表征过程。描述对象间的相互作用描述出各对象在应用系统中的关系; 如一个对象是另一个对象的一部分,一个对象与其他对象间的通信关系等。尽可能完整地描述每个对象的环境; 一个对象解释另一个对象;一个对象如何生成另一个对象。得到对象的界面描述。确定对象的操作简单的如增删改查;复杂的如将几个对象的信息连接起来;当连接的对象过于复杂,考虑标识新对象;定义对象的内部信息内部数据信息信息存储方法继承关系可能生成的实例数分析阶段最重要的是理解问题域的概念,其结果将影响整个工作。 完成分析后,要写出具体的规范文档,如果想到一出是一出,写的程序就会过于松散零碎。 面向对象分析方法有便于修改的优点,早期阶段修改有利于提高软件的可靠性。 返回目录 1 面向对象技术上一节 1.1 面向对象基础下一节 1.3 面向对象设计 参考资料: 《软件设计师教程》第五版百度百科

September 20, 2019 · 1 min · jiezi

音视频入门09RGBYUV互转使用开源库

音视频入门文章目录 介绍开源库使用第三方开源库来简化开发,屏蔽一些底层的复杂度,节省大量编写代码的时间。 libyuv: Google 开源的实现各种 YUV 与 RGB 之间相互转换、旋转、缩放的库。yuv2rgb:C library for fast image conversion between yuv420p and rgb24. 使用开源库libyuvFFmpeg 生成代码所需文件: ffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt yuv444p rainbow-yuv444p.yuvffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt yuv420p rainbow-yuv420p.yuvffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt rgb24 rainbow-rgb24.rgbYUV444P to RGB32将 FFmpeg 生成的 YUV444P 格式转换成 RGB32 格式。 [rainbow-yuv444p.yuv] -> [rainbow-yuv444p-to-rgb32-libyuv.rgb]#include <stdio.h>#include "libyuv.h"void libyuv_I444_to_Rgb(char *srcFileName, char *dstFileName, int width, int height) { FILE *src_file = fopen(srcFileName, "rb"); FILE *dst_file = fopen(dstFileName, "wb"); int size_src = width * height * 3; int size_dest = width * height * 4; char *buffer_src = (char *)malloc(size_src); char *buffer_dest = (char *)malloc(size_dest); fread(buffer_src, 1, size_src, src_file); I444ToARGB( (const u_int8_t*)buffer_src, width, (const u_int8_t*)(buffer_src + width * height), width, (const u_int8_t*)(buffer_src + 2 * width * height), width, (u_int8_t*)buffer_dest, width * 4, width, height); fwrite(buffer_dest, 1, size_dest, dst_file); free(buffer_src); free(buffer_dest); fclose(dst_file); fclose(src_file);}int main() { int width = 700, height = 700; libyuv_I444_to_Rgb("/Users/staff/Desktop/rainbow-yuv444p.yuv", "/Users/staff/Desktop/rainbow-yuv444p-to-rgb32-libyuv.yuv", width, height); return 0;}FFplay 显示生成的 rainbow-yuv444p-to-rgb32-libyuv.rgb ...

September 19, 2019 · 3 min · jiezi

虚拟币挖矿软件开发云矿机挖矿平台搭建

虚拟币挖矿软件开发,云矿机挖矿平台搭建 要问19年最赚钱的项目,挖矿绝对占其中之一。今年数字货币价格暴涨,导致许多矿商矿机供不应求。虽然挖矿赚钱,但前期的投资却是不小,购买实体矿机、电费、人工管理费、维护......等等,对于提前没有布局的项目方来说未必是好事。但有一种模式可以让项目方快速发展,积累大量用户,那就是虚拟挖矿,这种模式算是早期模式,但至今仍有大量的市场和用户(三四五线城市),虚拟币挖矿平台开发联系汪先生:xnbwang(微)。 但不同的系统对应不同的项目方,源中瑞科技不仅仅可以开发虚拟矿机系统,也为有矿场的项目方提供云算力系统,来帮助矿场扩展线上业务: 1.云算力系统。指把有实体矿场的矿机算力,拿到线上进行出售,当然也可以有矿机租赁、买卖矿机等功能,帮助实体矿场扩展市场,增加线上流量。 2.虚拟矿机系统。也就是我们统称的云挖矿,虚拟的矿机,然后结合一定的推广制度,可以帮助项目方快速发展用户,后期也可以结合交易、商城、游戏等一些其他应用。 虚拟币挖矿软件开发,云矿机挖矿平台搭建联系源中瑞汪顾问。专业的技术团队提供专业系统,帮助项目方快速在市场中取得自己的一份天地。

September 19, 2019 · 1 min · jiezi

设计模式6适配器模式

适配器模式适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。我们通过下面的实例来演示适配器模式的使用。其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件。 介绍意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)如何解决:继承或依赖(推荐)。关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。应用实例: 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。 实现我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下,AudioPlayer 可以播放 mp3 格式的音频文件。我们还有另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。我们想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter,并使用 AdvancedMediaPlayer 对象来播放所需的格式。AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型,不需要知道能播放所需格式音频的实际类。AdapterPatternDemo,我们的演示类使用 AudioPlayer 类来播放各种格式。 ...

September 10, 2019 · 1 min · jiezi

Qt-5SM4加解密客户端-实例开发-可使用

注: 支持 cbc&ebc 模式;支持任意待加密数据长度(PKCS7Padding);仅支持 ASCII;开发环境:Qt 5.12.1.源代码 & 可执行文件 文件:sm4.h /** * \brief SM4 context structure */#ifndef SM4_H#define SM4_H#define SM4_ENCRYPT 0#define SM4_DECRYPT 1#define SM4_RESTART 1#define SM4_KEEP 0#ifdef __cplusplusextern "C" {#endif/** * \brief SM4-CBC buffer encryption/decryption * \param ctx SM4 context * \param dec_en SM4_ENCRYPT or SM4_DECRYPT * \param flag SM4_RESTART or SM4_KEEP * \param iv initialization vector (updated after use) * \param length length of the input data * \param input buffer holding the input data * \param output buffer holding the output data */unsigned int sm4_cbc(unsigned char key[16], int dec_en, int flag, unsigned char iv[16], unsigned int length, unsigned char *input, unsigned char *output );/** * \brief SM4-CBC buffer encryption/decryption * \param ctx SM4 context * \param dec_en SM4_ENCRYPT or SM4_DECRYPT * \param flag SM4_RESTART or SM4_KEEP * \param length length of the input data * \param input buffer holding the input data * \param output buffer holding the output data */unsigned int sm4_ecb(unsigned char key[16], int dec_en, int flag, unsigned int length, unsigned char *input, unsigned char *output);#ifdef __cplusplus}#endif#endif文件:sm4.c ...

September 10, 2019 · 8 min · jiezi

足球数据API接口-足球赛事分析数据API调用示例代码

分享下足球赛事分析数据api接口的示例代码,详细可查看接口文档,需注册下 package com.huaying.demo.football; import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Arrays;import java.util.List; /** * 43.足球赛事分析数据 * * @Website: https://www.feijing88.com */public class FootballMatchStatistics { public static void main(String[] args) { try { String content = getContent(); Statistics statistics = Statistics.parseFrom(content); System.out.println(statistics); } catch (Throwable t) { t.printStackTrace(); } } /** * 获取API返回内容 * <p> * Note: 这里为了方便测试我使用了一份本地文件,使用时应替换为真实接口返回内容 */ private static String getContent() { try { StringBuilder builder = new StringBuilder(); List<String> lines = Files.readAllLines(Paths.get("./src/main/resources/FootballMatchStatistics.txt"), StandardCharsets.UTF_8); lines.forEach(line -> { builder.append(line); builder.append("|"); }); return builder.toString(); } catch (Throwable t) { t.printStackTrace(); return ""; } } public static class Statistics { private List<String> vsRecord; private List<String> homeRecentRecord; private List<String> awayRecentRecord; private List<String> homeFuture; private List<String> awayFuture; private List<String> homeOddsAll; private List<String> homeOddsHalf; private List<String> awayOddsAll; private List<String> awayOddsHalf; private List<String> homeGoalsDispersion; private List<String> awayGoalsDispersion; private List<String> homeStatistics; private List<String> awayStatistics; private List<String> homeGoalsTime; private List<String> awayGoalsTime; public static Statistics parseFrom(String data) { Statistics statistics = new Statistics(); statistics.parse(data); return statistics; } private void parse(String date) { String[] values = date.split("\\$\\|"); int i = 0; vsRecord = Arrays.asList(values[i++].split("\\|")); homeRecentRecord = Arrays.asList(values[i++].split("\\|")); awayRecentRecord = Arrays.asList(values[i++].split("\\|")); homeFuture = Arrays.asList(values[i++].split("\\|")); awayFuture = Arrays.asList(values[i++].split("\\|")); homeOddsAll = Arrays.asList(values[i++].split("\\|")); homeOddsHalf = Arrays.asList(values[i++].split("\\|")); awayOddsAll = Arrays.asList(values[i++].split("\\|")); awayOddsHalf = Arrays.asList(values[i++].split("\\|")); homeGoalsDispersion = Arrays.asList(values[i++].split("\\|")); awayGoalsDispersion = Arrays.asList(values[i++].split("\\|")); homeStatistics = Arrays.asList(values[i++].split("\\|")); awayStatistics = Arrays.asList(values[i++].split("\\|")); homeGoalsTime = Arrays.asList(values[i++].split("\\|")); awayGoalsTime = Arrays.asList(values[i].split("\\|")); } @Override public String toString() { return "Statistics{" + "\nvsRecord=" + vsRecord + ", \nhomeRecentRecord=" + homeRecentRecord + ", \nawayRecentRecord=" + awayRecentRecord + ", \nhomeFuture=" + homeFuture + ", \nawayFuture=" + awayFuture + ", \nhomeOddsAll=" + homeOddsAll + ", \nhomeOddsHalf=" + homeOddsHalf + ", \nawayOddsAll=" + awayOddsAll + ", \nawayOddsHalf=" + awayOddsHalf + ", \nhomeGoalsDispersion=" + homeGoalsDispersion + ", \nawayGoalsDispersion=" + awayGoalsDispersion + ", \nhomeStatistics=" + homeStatistics + ", \nawayStatistics=" + awayStatistics + ", \nhomeGoalsTime=" + homeGoalsTime + ", \nawayGoalsTime=" + awayGoalsTime + "\n}"; } }}API 返回数据如下(部分): ...

September 10, 2019 · 3 min · jiezi

设计模式4建造者模式

建造者模式建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 介绍意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。何时使用:一些基本部件不会变,而其组合经常变化的时候。如何解决:将变与不变分离开。关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。优点: 1、建造者独立,易扩展。 2、便于控制细节风险。缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。 实现我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo,我们的演示类使用 MealBuilder 来创建一个 Meal。 具体代码实现参见https://github.com/Hp1512/Lea...

September 10, 2019 · 1 min · jiezi

龙纪链利用区块链技术打造全球最大的沙盒游戏

3.0的游戏区块链不再限于游戏模块的区块链化,而是通过区块链的核心共识机制、激励机制,来激发玩家的创造性和主观能动性。由游戏厂商搭建平台,让玩家自主的在游戏中创造一切,包括装备、玩法等等。 图片描述什么是区块链游戏?首先,我们从字面意义上来了解,什么是区块链游戏,游戏我们都不陌生,现实中的人们几乎都有玩过,那我们就先来说说什么是区块链。区块链是一种新型的计算机应用模式,拥有更安全、更保密、去中心化、透明、可追查的特点。用一个玩笑的话形容就是,买家直接与卖家通话,没有中间商赚差价,而且过程完全透明,价格不可恶意炒高更改。 什么是龙纪链?龙纪链 DGEra Chain(www.dgera.net)是基于以太坊开发的大型 3D区块链 RPG 游戏,龙纪链计划打造一款全平台游戏,龙纪链目标打造全球最大的区块链沙盒游戏,致力于把区块链技术应用到大型 3D游戏中。而游戏也正是区块链技术应用最好的落地场景。利用区块链技术,对参与玩家而言这无疑是一个数据透明、高信任度、玩法正轨的可持续性收益项目。这将会颠覆现有游戏行业玩法。 图片描述龙纪链怎么玩?玩家在创始期可以通过ETH兑换龙晶购买一只创始龙,每只龙有固定属性,通过挖矿收益龙币,龙的等级越高产币速率越快。龙币是未来龙纪链的唯一通证。创始期时龙币可以通过挖矿空投等方式获得。龙币在创始期可以加速获得,创始期持有越久,一次性奖励就越多。在创始期过后,龙币可以通过打怪、挖矿、升级、PVP、PVE、副本等形式获得,创始龙和龙晶只在创始期时发售哦,但是会开放交易大厅,交易大厅可以进行创始龙的买卖交易,成年的龙可以繁衍小龙,小龙可以进行赠送。龙巢可以用来查看玩家持有的分红和龙币数量。 如何从龙纪链DGEra Chain中获利?除了培育龙,买卖龙与龙晶,挖矿囤龙币拿分红,等待创始期结束后,平台根据玩家持有的龙币总量,发放的相应持币奖励,幸运哈希和哈希DICE,玩家还可以开龙盒,做每日任务,英雄任务,传奇任务,这些都可以获得相当丰厚的一笔ETH,龙晶,龙币奖励!自己玩无聊,邀请好友,每日签到,随手转发至朋友圈,躺着就可以把奖励领到手啦!龙纪链还有返佣政策,只要你邀请好友成为下家,即可获得ETH!当然还不止这些!创始期结束后,即将开放的打怪,PVP,副本,驯龙师等等各种各样的玩法活动肯定会让玩家龙币拿到手软,奖励不停歇!福利多多期待玩家们去探索去发现,龙纪链希望玩家们能在龙纪链中实现乐趣和价值的双丰收! 图片描述 投资稳赚的赚钱游戏龙纪链:龙纪链尚处第一阶段,因丰厚的赚币红利吸引了一大批玩家,可谓“一龙难求”。据悉,平台将陆续上线PVP、驯龙师系统、时装系统、首个大型资料片黑暗之地、大秘境等玩法,将龙纪链打造成全球最大的区块链沙盒游戏!龙纪链已被大批玩家和币圈人看好,推荐大家尽早关注龙纪链DGEra Chain,希望与你同行! 未来龙纪链还将建设更多通证使用场景,拓宽游戏生态,给币圈的用户们一个全新体验,也将彻底颠覆游戏领域的垄断现象,让更多游戏开发者更专注自己的梦想、让玩家玩到更多有创意高质量游戏。同时将引入更多泛娱乐内容,创造兼容并包的区块链游戏生态。图片描述 龙纪链官网:www.dgera.netTelegram群1:https://t.me/DGEraChain Telegram群2:https://t.me/dgerachain001 币用群1:https://0.plus/DGEraChain 币用群2:https://0.plus/dgerachain001新手指南:https://dgera.zendesk.com/hc/... 进微信群加:suger66699、linec159注册下载链接: https://front.dgera.net/regis...

September 10, 2019 · 1 min · jiezi

音视频入门01认识RGB

音视频入门文章目录 RGB 简介RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB 即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。RGB格式RGB16 格式RGB16 数据格式主要有两种:RGB565 和 RGB555。 1. RGB565每个像素用 16 比特位表示,占 2 个字节,RGB 分量分别使用 5 位、6 位、5 位。高字节 低字节R R R R R G G G G G G B B B B B得到 RGB565 各分量的值: #define RGB565_MASK_RED 0xF800#define RGB565_MASK_GREEN 0x07E0#define RGB565_MASK_BLUE 0x001FR = (color & RGB565_MASK_RED) >> 11; // 取值范围0-31G = (color & RGB565_MASK_GREEN) >> 5; // 取值范围0-63B = color & RGB565_MASK_BLUE; // 取值范围0-31#define RGB(r,g,b) (unsigned int)( (r|0x08 << 11) | (g|0x08 << 6) | b|0x08 )#define RGB(r,g,b) (unsigned int)( (r|0x08 << 10) | (g|0x08 << 5) | b|0x08 )2. RGB555每个像素用 16 比特位表示,占 2 个字节,RGB 分量都使用 5 位(最高位不用)。高字节 低字节X R R R R R G G G G G B B B B B (X表示不用,可以忽略)得到 RGB555 各分量的值: ...

September 10, 2019 · 2 min · jiezi

使用stl的queue实现线程安全队列

简介近日有朋友问起线程安全队列的问题。本文基于stl的queue容器实现了线程安全的队列,可多线程生产,多线程消费。同时与基于boost的circular_buffer实现的环形缓冲区相比较,性能略优(实验测试下来优势也不大,不到5%)。源码比较简单,使用stl和boost实现,并且实现了超过队列最大长度丢弃消息的功能。 实验环境准备CPU主要参数:intel 2.3GHz,4核内存:12G操作系统:windows7 实验结果线程数为0,表示生产线程和消费线程是同一线程,统一生产完后,从消费第一条开始的计算时间。其他的是在一个线程生产,N(N>=1)个线程消费的情况。折线图显示如图所示,可以看到使用queue实现的队列性能最优,circular_buffer其次,list稍差。 源码在simplethreadqueue.h中注释的部分是使用boost的circular_buffer实现的。simplethreadqueue.h #ifndef CSIMPLETHREADQUEUE_H#define CSIMPLETHREADQUEUE_H#include <string>#include <queue>#include <iostream>#include "boost/timer.hpp"#include <boost/date_time/posix_time/posix_time.hpp>#include <boost/thread/thread.hpp> #include <boost/circular_buffer.hpp>using namespace std;using namespace boost;extern long g_nMaxCount;template <class T>class CSimpleThreadQueue{public: CSimpleThreadQueue():m_nSize(0),m_nConsumed(0){/*m_container.resize(g_nMaxCount);*/} size_t size(){return m_nSize;} long GetConsumed(){return m_nConsumed;} void EnQueue(T item) { m_mutex.lock(); while(m_nSize >= g_nMaxCount) { m_container.pop(); --m_nSize; } m_container.push(item); //m_container.push_back(item); ++m_nSize; m_cond.notify_one(); m_mutex.unlock(); } void Dequeue(T& item) { while (true) { m_mutex.lock(); if ( m_nSize > 0) break; m_cond.wait_for(m_mutex, boost::chrono::microseconds(1)); m_mutex.unlock(); } item = m_container.front(); m_container.pop(); //m_container.pop_front(); -- m_nSize; ++m_nConsumed; m_mutex.unlock(); } private: std::queue<T> m_container; //circular_buffer<T> m_container; size_t m_nSize; boost::mutex m_mutex; condition_variable_any m_cond; long m_nConsumed;};#endifmain.cpp ...

September 9, 2019 · 2 min · jiezi

关于值对象的思考

使用值对象模式的好处。 一般我们操控类内属性都是get和set方法,很常用也很好用。 class MyClass {public: MyClass(); void setProperty1(const QString &value); QString getProperty1() const; void setProperty2(const QString &value); QString getProperty2() const;} 但是我们在操控类的过程中,自己不小心或第三方接口使用者误调用了set方法导致MyClass类内状态发生变化,这个是我们不想要的。 应该对类加以限制,那么该如何限制。这时候我们可以用Builder模式,它是一个只读对象,但Builder模式使用起来比较繁琐。 有没有更简单的只读对象呢? 答案肯定的,就是使用值对象,通过类的构造函数来设置属性。 class MyClass {public: MyClass(const QString &property1, const QString &perperty2); QString getProperty1() const; QString getProperty2() const;}; 上面代码我们可以看到MyClass类不再提供设置属性的方法,只提供读的方法。如果需要设置MyClass类内属性只能通过重新创建MyClass的对象方式去设置,这方法就很好地避免被它人随便修改了。 这种方法就像是Http请求一样,无状态,用起来让人感觉踏实安心,但是它有一个致命的缺点是构造函数参数不能过多。

September 9, 2019 · 1 min · jiezi

WEBRTC穿透技术探讨之STUN协议详解

1. STUN协议概述 STUN(Session Traversal Utilities for NAT/NAT环境下的会话传输工具),是一种处理NAT传输的协议,主要作为工具来服务其他协议。它允许位于NAT(或者多重NAT后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间建立UDP通信。目的就是找到外界连接内部地址所需的信息。 2. STUN协议的架构 STUN协议是一个C/S架构的协议,支持两种传输类型:1.请求/响应(request/response)类型,由客户端给服务器发送请求,并等待服务端返回响应,用于确定一个NAT给客户端分配的具体绑定。客户端通过事务ID将请求响应连接起来。2.指示类型(indication transaction),由服务器或者客户端发送指示,另一方不产生响应,用于保持绑定的激活状态。事务ID通常作为debugging aid使用。 所有的STUN报文信息都包含有一个固定头部,包含了方法,类和事务ID。方法表示是具体哪一种传输类型。STUN中只定义了一种方法,即binging(绑定),其他方法可以由使用者自行扩展;Binding方法可以用于请求/响应类型和指示类型。 STUN基于客户机-服务器协议。如一个VoIP电话或者软件可能会包含一个STUN客户端。这个客户端向STUN服务器发送请求,之后,服务器就会向STUN客户端报告NAT路由器的公网IP地址以及NAT为允许传入流量传回内网而开通的端口。同时还使得STUN客户端能够确定正在使用的NAT类型——因为不同的NAT类型处理传入的UDP分组的方式不同。四种主要网络类型中有三种是可以使用的:完全圆锥型NAT、受限圆锥型NAT和端口受限圆锥型NAT。但大型公司网络中经常采用的对称型NAT(又称为双向NAT)则不能使用。 3. STUN报文格式3.1 STUN报头格式 STUN报文和大多数网络类型的格式一样,是以大端编码(big-endian)的,即最高有效位在左边。所有的STUN报文都是以20字节的头部开始,后面跟着若干个属性。下面来详细说说。 STUN头部包含了STUN消息类型,magic cookie,事务ID和消息长度,如下: 最高的两位必须为0,当STUN和其他协议复用的时候,用来区分STUN包和其他数据包。 STUN Message Type(16位,包括前面置零的2位) 定义了STUN的消息类型:0x0001:捆绑请求0x0101:捆绑响应0x0111:捆绑错误响应0x0002:共享私密请求0x0102:共享私密响应0x0112:共享私密错误响应 Message Type(后14位)又可以进一步分解为以下结构: 1.其中M11到M0表示方法的12位编码。STUN目前只定义了一个方法,即bindingO(绑定),其他的方法可以由使用者自行拓展;Binding方法可以用于请求/响应类型和指示类型。 2.C1-C0表示类编码,分别标识报文的reqest(0b00),indicatioon(0b01),success response(0b10),error response(0b11),每一个不同的方法都有可能对应不同的类别。 Message Length 16位消息大小的字节数,但不包括20字节的头部。所有的STUN属性都是四字节对齐的 Magic Cookie:32位字段包含固定值0x2112A442 Stransaction ID,96位的事务ID标识符,用于随机请求和响应,请求其相应的所有响应具有相同的标识符。 3.2 STUN报文常见属性3.2.1 MAPPED-ADDRESS/映射地址 MAPPED-ADDRSS同时也是class STUN的一个属性,之所以还存在也是为了前向兼容。其包含了NAT客户端的反射地址。 Family位IP类型,即IPV4(0x01)或IPV6(0x02) port位端口 address为32位或128位的IP地址。 3.2.2 XOR-MAPPED-ADDRESS/异或映射地址 XOR-MAPPED-ADDRESS和MAPPED-ADDRESS基本相同,不同点是反射地址部分经过一次异或(XOR)处理。 X-port字段:是将NAT的映射端口以小端形式与magic cookie的高16位进行异或,再将结果转换成大段形式而得到的,X-Address也是类似。之所以要经过这么一次转换,是因为在实践中发现很多NAT会修改payload中自身公网IP的32位数据,从而导致NAT打洞失败。 3.2.3 ERROR-CODE/错误码 ERROR-CODE属性用于error response报文中。其中包含了300-699表示的错误码,以及一个UTF-8格式的文字出错信息(Reason phrase)。

September 9, 2019 · 1 min · jiezi

gcc编译参数

编译 gcc -c main.c ==== 编译不链接,生成.o目标文件 gcc -E main.c ==== 预处理 gcc -S main.c ==== 只编译不汇编 gcc -g main.c -o main_d ==== 可进行gdb调试 gcc -Dname='xinzhu' === 定义宏 define name 'xinzhu' gcc main.c -o main -I../path gcc main.c -o main -I../path -L../path gcc -I [大写字母i]寻找头文件目录 /usr/local/include gcc -L [大写字母l]寻找库文件 /usr/local/lib gcc -l word [小写字母l], 寻找动态链接库文件libword.so静态库 .a结尾 # 创建.o目标文件 gcc -c test.c -o libtest.o #创建libtest.a静态库 ar rcs libtest.a libtest.o #链接静态库 gcc -o test main.c -ltest动态库 .so结尾 # 使用位置无关代码创建目标文件 gcc -c -fPIC test.c -o test.o # 创建共享库libtest.so gcc -shared -o libtest.so test.o # 链接静态库 gcc -o test main.c -ltest

September 8, 2019 · 1 min · jiezi

C-智能指针-笔记

C++ 智能指针 笔记几种智能指针auto_ptr, unique_ptr, shared_ptr, weak_ptr, 其中第一个已经被c++11弃用 从较浅的层面看, 智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装, 这使得智能指针实质是一个对象, 行为表现的却像一个指针。by 柴小喵 基本作用/为什么应对两种情况: i) 忘记 delete, ii) 程序出现异常的时候也可以回收内存 c++ 需要手动进行内存管理, new --- delete 但是我们不能避免程序还未执行到 delete 时就跳转了或者在函数中没有执行到最后的 delete 语句就返回了, 如果我们不在每一个可能跳转或者返回的语句前释放资源, 就会造成内存泄露 memory leak 使用智能指针可以很大程度上的避免这个问题, 因为智能指针就是一个类, 当超出了类的作用域时, 类会自动调用析构函数, 析构函数会自动释放资源 其他作用TODO: 理解详情 value 语义转化为 reference 语义 所有权 对于特定的对象, 只能有一个智能指针可拥有, 这样只有拥有对象的智能指针的构造函数会删除该对象。然后让赋值操作转让所有权。这就是用于 auto_ptr 和 unique_ptr 的策略, 但 unique_ptr 的策略更严格。Alexia 下面会有例子, 简单理解就是 p2 = p1 后, p1 变成空指针, 防止两个指针共同管理一个对象的情况。如果此时再用 p1->foo(), 那么 auto_ptr 会在运行时报错, 而 unique_ptr 将在编译时报错 ...

September 8, 2019 · 3 min · jiezi

浮点数与十六进制互相转换

利用强制转换类型实现。浮点数转十六进制实现:float f = 123.45f;unsigned char *hex = (unsigned char *)&f;打印输出:for(int i = 0; i < 4; i++) printf("0x%02X ", hex[i]); printf("\n");十六进制转浮点数实现:unsigned char hex[] = { 0x66, 0xE6, 0xF6, 0x42 };float f = *(float *)hex;打印输出:printf("%g\n", f); // %g参数按浮点精度四舍五入去除多余的0.

September 8, 2019 · 1 min · jiezi

C知识点总结篇

const在不同位置时的不同意义指针类型前:声明一个指向常量的指针,程序中不能通过指针来改变它所指向的值,但指针本身的值可以改变,即指针可以指向其他数据;"*"号和指针名之间,声明一个指针常量(常指针),指针本身的值不可改变,即不能指向其他数据,但指向的数据的值可以改变;两个地方都加,声明指向常量的指针常量,指针本身的值不可改变,指向的数据也不能通过指针改变;函数指针使用函数指针之前,必须先赋值,使它指向一个函数入口地址,赋值语法格式为:函数指针名 = 函数名,其中函数名代表的函数必须是一个已经定义过的,和函数指针具有相同返回类型的函数,指针调用函数格式:(*指针变量)(实参列表);访问权限public:可被任意实体访问;protected:只允许本类及子类的成员函数访问;private:只允许本类的成员函数访问;构造函数创建对象时,自动调用构造函数,不能在程序中直接调用,可有任意类型参数,但不能有返回类型;构造函数作用:为对象分配空间、为数据成员赋初值、请求其他资源;构造函数工作:初始化虚函数表、建立基类对象、建立非静态数据成员对象、安置虚基类对象信息、执行构造函数体中的代码;若一个类中没有定义构造函数,编译器会自动生成不带参数的默认构造函数,格式为:<类名>::<默认构造函数名>(){}析构函数析构函数作用:清除对象、释放内存;析构函数工作:执行析构函数中的代码、将对象占据的存储空间归还系统、做公共及用户要求的善后工作;析构函数无参数和返回值,一个类中只能定义一个析构函数,故不能重载,格式为:~<类名>();内存布局全局数据区:存放全局变量、静态数据、常量;代码区:存放类成员函数、其他函数代码;栈区:存放局部变量、函数参数、返回数据、返回地址;堆区:自由存储区;运算符重载重载形式重载为类的成员函数,参数个数比原来的运算数少一个<函数类型> operator <运算符> (<形参列表>){<函数体>;}重载为类的友元函数,参数个数与原运算数个数一样多;friend <函数类型> operator <运算符> (<形参列表>){<函数体>;}函数重载:返回值类型可以相同可以不同,但形参列表一定不同;静态数据成员初始化格式:<类型><类名>::<静态数据成员>=<值>;引用格式:<类名>::<静态数据成员>;继承方式私有继承private:父类的公有成员和保护成员作为子类的的私有成员,且不能被子类的派生类访问;公有继承public:父类的公有成员和保护成员作为子类的成员时,仍保持原有状态,父类私有成员仍为私有;保护继承protected:父类的公有成员和保护成员成为子类的保护成员,且只能被他的派生类成员函数或友元访问,父类私有成员仍为私有; 派生类构造函数调用顺序:调用基类的构造函数,调用顺序按继承时说明的顺序;调用子对象类的构造函数,调用顺序按在类中说明的顺序;派生类构造函数体中内容;函数模版template < 模板形参表 >template <typename T>返回值类型 函数名(形式参数列表){ 函数体语句}例:template <typename T>void swap( T& v1, T& v2){ T temp; temp = v1; v1 = v2; v2 = temp;}虚函数虚函数必须是类的成员函数,不能是友元,但可以是另一个类的友元,不能为全局函数,也不能为静态函数,析构函数可以为虚函数,但构造函数不能为虚函数;变量初始化局部变量:定义时,系统不会进行初始化;全局变量:定义时,系统自动初始化, 友元函数在类中定义过,但不是成员函数,定义在类外部,但有权访问类中成员;this指针只有成员函数才有this指针,友元函数没有this指针;静态成员函数与普通成员函数区别:静态成员函数没有this指针,只能访问静态成员(包括静态成员变量和静态成员函数);普通成员函数有this指针,可以访问类中任意成员,而静态成员函数无this指针;创作不易,未经同意,转载请注明出处。

September 8, 2019 · 1 min · jiezi

前端压缩工具

野子电竞数据官网改版https://www.xxe.io/ 全新登场介绍一下webpack和gulp以及项目中的具体使用现今的很多网页其实可以看做是功能丰富的应用,它们拥有复杂的javascript代码和一大堆依赖包,为了简化开发的复杂度,有很多实践方法模块化:将复杂的程序细化为小的文件类似于typescript这种在js基础上拓展的开发语言,可以简化我们的开发,并且之后可以用bable等工具将其转化成为js即浏览器认识的语言Sass,less等css预处理器webpack是模块打包机,它做的事情是分析你的项目结构,找到js模块以及其他的一些浏览器不能直接运行的语言,并将其打包为合适的格式供浏览器使用Babel编译js的平台,可以使用下一代的es6和es7使用基于js拓展的语言,如 react的JSX模块组织的几种方法通过书写在不同文件中,使用script标签进行加载common.js node.js使用的就是这种方式amd进行加载 require.js使用这种模式es6模块webpackwebpack的特点丰富的插件方便进行工作大量的加载器,包括加载各种静态资源代码分割,提供按需加载的能力发布工具webpack的优势webpack是以commonjs的形式来书写脚本 但对amd和cmd也支持全面,方便旧项目进行代码迁移能被模块化的不仅仅是js了开发便捷 能替代部分grunt和gulp的工作 比如打包 压缩 图片转base64扩展性强 插件机制完善webpack的属性值entry 入口文件output:定义构建后的输出文件module:loaders加载各种资源reslove resolve属性中的extensions数组用于配置程序中可以自行补全哪些文件后缀plugin 提供了丰富的组件来满足不同的需求externals 当我们想在项目中require一些其他的类库或者api 而又不想让这些类库的源码被构建到运行时文件中contextGrunt配置gruntfile.jsmodule.exports = function () {grunt.initConfig({pkg: grunt.file.readJSON(‘package.json’);});grunt.registerTask(‘default’, []);}123456grunt插件介绍contrib-jshint js语法错误勘察contrib-watch 实时监控文件变化 调用相应的任务重新执行contrib-cleancontrib-copycontrib-concatkarma 前端自动化测试化工具uglify 插件uglify: {options: {stripBanner: true,banner:},build: {src:dest}} 在initConfig之后grunt.loadNpmTasks(‘grunt-contrib-ugify’);grunt.registerTask(‘default’,‘ugify’);1234567891011121314banner: 即在生成的压缩文件第一行加一句话说明,pkg可以获得package.json的内容build: 配置了源文件和压缩文件,即规定了要压缩谁 压缩之后生成谁jshintjshint: {build: [‘Gruntfile.js’, ‘src/.js’],options: {jshintrc: ‘.jshintrc’}}123456使用watch插件watch: {build: {files: ['src/.js’, ‘src/.css’],tasks: [‘jshint’, ‘uglify’],options: {spawn: false}}}1234567gulpgulpfile.jsgulp的配置文件var gulp = require(‘gulp’)var less = require(‘gulp-less’)gulp.task(‘testLess’, function () {gulp.src=(‘src/less/index.less’).pip(less()).pip(gulp.dest(‘src/css’))});//定义默认任务,并让gulp监视文件变化自动执行gulp.task(‘default’, [‘watch’], function () {gulp.watch('sass/.scss’,[‘sass’]);});123456789101112gulp常用插件js压缩var gulp = require(‘gulp’);var rename = require(‘gulp-rename’);var uglify = require(‘gulp-uglify’);gulp.task(‘rename’, function () {gulp.src(‘src//*.js’).pipe(uglify()).pipe(rename(‘idnex.min.js’)).pipe(gulp.dest(‘build/js’);});gulp.task(‘default’, [‘rename’]);12345678910html压缩var minifyHtml = require(‘gulp-minify-html’);gulp.task(‘minify-html’, function () {gulp.src('src//.html’)//要压缩的html文件.pipe(minifyHtml())//压缩.pipe(gulp.dest(‘build’))});123456js文件合并var concat = require(‘gulp-concat’);gulp.task(‘concat’, function () {gulp.src('src/**/.js’)//要合并的文件.pipe(concat(‘index.js’))//合并匹配到的js文件并命名为index.js.pipe(gulp.dest(‘build/js’))});123456gulp-lessvar gulp = require(‘gulp’), ...

September 7, 2019 · 1 min · jiezi

头铁君码代码

头铁君一早狂码代码,究竟是什么回事呢? 一早看到头铁君满头大汗地敲键盘,平时最迟来的,今天却一早就在敲代码,这是怎么了?走进一看,飞快的Ctrl+C,Ctrl+V。头铁君你这是干嘛。。。手速这么快?Qt君你别说了,快给我弄一下,我的手都麻了。这烫手的山芋接不得啊。只看到它写了一堆的计算方法。 int add(int v1, int v2){ return v1 + v2;}float add(float v1, float v2){ return v1 + v2;}double add(double v1, double v2){ return v1 + v2;}... 变量类型这么多靠复制粘贴到什么时候啊。头铁君虽然你很头铁也不至于这样啊。你可以使用auto和模板配合操作,让编译器给你做这些繁琐的事。 头铁君好似明白了,立马一顿操作猛如虎,一看编译都错误!!! auto add(auto v1, auto v2){ return v1 + v2;}头铁君啊,这里你需要注意的是: 使用auto关键字的变量必须有初始值(类似于引用的使用)。函数参数和模板参数不能被声明为auto。 auto是给编译器使用的,让编译器在编译器推断出变量的类型,而auto变量没有初始值则不能令编译器自动推断导致编译错误。 头铁君有些不解地问,你给的方法是错误的,我还是复制粘贴去了。 头铁君你别急,看下这个吧。 template<typename L, typename R>auto add(L v1, R v2){ return v1 + v2;} 头铁君若有所思一会,立马拿起代码来验证了。 auto result1 = add(1, 2);auto result2 = add(2.1, 3.1);auto result3 = add(2, 3.0); 一会后,头铁君果断将之前复制粘贴的代码删除了。并问,你这方法太好用了,怎么做到的? ...

September 7, 2019 · 1 min · jiezi

C顺序表实现

手写了一波C++顺序表,加深理解。 最大感触:创建一个数组对象,指定数组长度(自定义创建参数length)为100,数组中每个元素都会随机分配内存(随机数据),且数组长度无法修改,只通过修改length的值来限制数组元素访问。像js、python等等之类的语言无非是将底层数据结构做了包装吧。 实现目标(对应js中array对象的部分内建函数实现): 1.创建数组2.删除数组3.显示数组每个元素4.获取数组指定位子的元素5.查找元素6.指定位子插入元素7.删除数组中指定元素 void CreateList(SqList *&L, ElemType a[], int n); // 建立顺序表 void DestroyList(SqList *&L); void ListEmpty(SqList *L); int ListLength(SqList *L); void DispList(SqList *L); // 显示数组每哥元素 bool GetElem(SqList *L, int i, ElemType &e); // 求某个位置数据元素的值(1开始) int LocateElem(SqList *L, ElemType e); // 查找元素 bool ListInsert(SqList *&L, int i, ElemType e); // 插入元素(让第i个位置开始的所有元素一起后移) bool ListDelete(SqList *&L, int i, ElemType &e); // 删除元素部分代码: #include <stdio.h>#include <stdlib.h>#define MAX_SIZE 100typedef int ElemType;typedef struct{ ElemType data[MAX_SIZE]; int length;}SqList;int main() { printf("start\n"); SqList *L = NULL; int n = 5; ElemType arr[5] = {1, 2, 3, 4, 5}; ElemType newArr[5] = {7, 8, 9, 10, 6}; CreateList(L, newArr, n); DispList(L); ListLength(L); ElemType result = 0; GetElem(L, 1, result); printf("GetElem: %d\n", result); int locate = -1; locate = LocateElem(L, 4); printf("locate: %d\n", locate); ListInsert(L, 1, 1); DispList(L); ElemType delResult = -1; ListDelete(L, 4, delResult); DispList(L); printf("del: %d\n", delResult); return 0;}void CreateList(SqList *&L, ElemType a[], int n) { L = (SqList *)malloc(sizeof(SqList)); for (int i = 0; i < n; i++) { L->data[i] = a[i]; }; L->length = n;}void DispList(SqList *L) { for (int i = 0; i < L->length; i++) { printf("index: %d, value: %d;\n", i, L->data[i]); }; printf("\n");}int ListLength(SqList *L) { printf("length: %d\n", L->length); return L->length;}bool GetElem(SqList *L, int i, ElemType &e) { if (i <= 0 || i > L->length) { return false; } e = L->data[i - 1]; return true;}int LocateElem(SqList *L, ElemType e) { ElemType result = -1; for (int i = 0; i < L->length; i++) { if (L->data[i] == e) { result = i; break; } } return result;}bool ListInsert(SqList *&L, int i, ElemType e) { int realIndex = i - 1; if (realIndex < 0 || realIndex > L->length) { return false; } L->length++; for (int index = L->length; index > realIndex && index > 0; --index) { L->data[index] = L->data[index - 1]; } L->data[realIndex] = e; return true;}bool ListDelete(SqList *&L, int i, ElemType &e) { if (i < 0 || i >= L->length) { return false; } e = L->data[i - 1]; L->length--; for (int index = i - 1; index < L->length; index++) { L->data[index] = L->data[index + 1]; } return true;}

September 6, 2019 · 2 min · jiezi

实现windows锁屏待机重启关机

最近项目中要用到通过远程下发指令实现对操作系统的锁屏,待机,重启和关机功能。查了下windows的系统api,做了个demo来实现这些功能,带UI,使用的vs2017+Qt(能用Qt就不要用MFC了),国际惯例记录下。 锁屏比较简单直接调提供API即可 void Controller::LockScreen(){ LockWorkStation();}待机void Controller::Standby(){ HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; //获取当前进程token BOOL bRet = ::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (bRet) { //查看系统权限的特权值 ::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid); tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //提升权限 ::AdjustTokenPrivileges(hToken, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); } ::SetSystemPowerState(false, true);}重启void Controller::Restart(){ HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; //获取当前进程token BOOL bRet = ::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (bRet) { //查看系统权限的特权值 ::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid); tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //提升权限 ::AdjustTokenPrivileges(hToken, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); } ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);}关机void Controller::Shutdown(){ HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; //获取当前进程token BOOL bRet = ::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (bRet) { //查看系统权限的特权值 ::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid); tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //提升权限 ::AdjustTokenPrivileges(hToken, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); } ::ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE | EWX_POWEROFF, 0);}UI界面如下: ...

August 20, 2019 · 1 min · jiezi

关于洛谷刷题的一些整理待整理更新

ST表P3865 #include<iostream>#include<cstdio>#include<cmath>using namespace std;int Max[100100][21],Log2[100100];int readd(){ int ret=0; char ch=getchar(); while('0'<=ch&&'9'>=ch){ ret*=10; ret+=(ch-'0'); ch=getchar(); } return ret;}int main(){ ios::sync_with_stdio(false); int n,q,sj; n=readd();q=readd(); sj=int(log2(n)); for(int i=1;i<=n;i++) Max[i][0]=readd(); for(int i=1;i<=n;i++) Log2[i]=(int)log2(i); for(int i=1;i<=sj;i++){ for(int j=1;j+(1<<i)<=n+1;j++){ Max[j][i]=max(Max[j][i-1],Max[j+(1<<(i-1))][i-1]); //cout<<Max[j][i]<<' '; } //cout<<endl; } int l,r; for(int i=0;i<q;i++){ l=readd(); r=readd(); printf("%d\n",max(Max[l][Log2[r-l+1]],Max[r-(1<<Log2[r-l+1])+1][Log2[r-l+1]])); } return 0;}线性求逆元3805 #include<iostream>#include<cstring>#include<cstdio>using namespace std;char y[52000100],a[102000100];int b[52000100];int main(){ int ans=0; scanf("%s",y); int olen=strlen(y),len; a[0]='#';a[1]='#'; for(register int i=0;i<olen;i++){ a[(i<<1)+2]=y[i]; a[(i<<1)+3]='#'; } len=strlen(a); int maxr=0,mid=0;b[0]=1; for(register int i=1;i<=len;i++){ if(i<maxr) b[i]=min(b[(mid<<1)-i],b[mid]+mid-i); else b[i]=1; for(;a[i-b[i]]==a[i+b[i]];b[i]++); if(b[i]+i>maxr){ maxr=i+b[i]; mid=i; } ans=max(b[i],ans); } cout<<ans-1; return 0;}3811 ...

August 19, 2019 · 7 min · jiezi

有趣的数

CCF模拟题 有趣的数问题描述 我们把一个数称为有趣的,当且仅当: 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。最高位数字不为0。因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。输入格式 输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。 输出格式 输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。 样例输入 4样例输出3 思路为DP 每一次决策都基于前一次决策的最优解即n位数的解基于n-1位数的最优解。我们对一个数的第n位根据 规则 共有以下几种状态 0 --用了2,剩 0 ,1, 3 1 -- 用了 0 ,2 剩下 1,32 -- 用了 2 3 剩下 0,13 -- 用了 0 1 2 剩下34 -- 用了 0 2 3 剩下15 全用 然后呢我们需要让用户输入位数,然后声明同等位数的数组,在每个元素里是6种状态中所包含状态下的“符合条件数”的个数。(是二维数组)然后用动态规划思想从最小位数逐层向上计算。例:对于在i位状态5的计算,考虑在i-1位时 有三种状态可以到达状态5, 状态3 i位填 3 状态4 i位填1 和状态5 填1或3; 所以定义好状态之后 根据i位和i-1位的关系 得出以下关系然后for训练动态规划递推即可 j=i-1; ...

August 19, 2019 · 1 min · jiezi

修改系统时间导致semtimedwait函数一直阻塞的问题解决和分析

修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析介绍最近修复项目问题时,发现当系统时间往前修改后,会导致sem_timedwait函数一直阻塞。通过搜索了发现int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);传入的第二个阻塞时间参数是绝对的时间戳,那么该函数是存在缺陷的。 sem_timedwait存在的缺陷的理由: 假设当前系统时间是1565000000(2019-08-05 18:13:20),sem_timedwait传入的阻塞等待的时间戳是1565000100(2019-08-05 18:15:00),那么sem_timedwait就需要阻塞1分40秒(100秒),若在sem_timedwait阻塞过程中,中途将系统时间往前修改成1500000000(2017-07-14 10:40:00),那么sem_timedwait此时就会阻塞2年多! 这就是sem_timedwait存在的缺陷!! sem_timedwait函数介绍int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);如果信号量大于0,则对信号量进行递减操作并立马返回正常如果信号量小于0,则阻塞等待,当阻塞超时时返回失败(errno 设置为 ETIMEDOUT)第二个参数abs_timeout 参数指向一个指定绝对超时时刻的结构,这个结果由自 Epoch,1970-01-01 00:00:00 +0000(UTC) 秒数和纳秒数构成。这个结构定义如下 struct timespec { time_t tv_sec; /* 秒 */ long tv_nsec; /* 纳秒 */};解决方法可以通过sem_trywait + usleep的方式来实现与sem_timedwait函数的类似功能,并且不会发生因系统时间往前改而出现一直阻塞的问题。 sem_trywait函数介绍函数 sem_trywait()和sem_wait()有一点不同,即如果信号量的当前值为0,则返回错误而不是阻塞调用。错误值errno设置为EAGAIN。sem_trywait()其实是sem_wait()的非阻塞版本。 int sem_trywait(sem_t *sem)执行成功返回0,执行失败返回 -1且信号量的值保持不变。 sem_trywait + usleep的方式实现主要实现的思路:sem_trywait函数不管信号量为0或不为0都会立刻返回,当函数正常返回的时候就不usleep;当函数不正常返回时就通过usleep来实现延时,具体是实现方式如下代码中的bool Wait( size_t timeout )函数: #include <string>#include<iostream>#include<semaphore.h>#include <time.h>sem_t g_sem;// 获取自系统启动的调单递增的时间inline uint64_t GetTimeConvSeconds( timespec* curTime, uint32_t factor ){ // CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响 clock_gettime( CLOCK_MONOTONIC, curTime ); return static_cast<uint64_t>(curTime->tv_sec) * factor;}// 获取自系统启动的调单递增的时间 -- 转换单位为微秒uint64_t GetMonnotonicTime(){ timespec curTime; uint64_t result = GetTimeConvSeconds( &curTime, 1000000 ); result += static_cast<uint32_t>(curTime.tv_nsec) / 1000; return result;}// sem_trywait + usleep的方式实现// 如果信号量大于0,则减少信号量并立马返回true// 如果信号量小于0,则阻塞等待,当阻塞超时时返回falsebool Wait( size_t timeout ){ const size_t timeoutUs = timeout * 1000; // 延时时间由毫米转换为微秒 const size_t maxTimeWait = 10000; // 最大的睡眠的时间为10000微秒,也就是10毫秒 size_t timeWait = 1; // 睡眠时间,默认为1微秒 size_t delayUs = 0; // 剩余需要延时睡眠时间 const uint64_t startUs = GetMonnotonicTime(); // 循环前的开始时间,单位微秒 uint64_t elapsedUs = 0; // 过期时间,单位微秒 int ret = 0; do { // 如果信号量大于0,则减少信号量并立马返回true if( sem_trywait( &g_sem ) == 0 ) { return true; } // 系统信号则立马返回false if( errno != EAGAIN ) { return false; } // delayUs一定是大于等于0的,因为do-while的条件是elapsedUs <= timeoutUs. delayUs = timeoutUs - elapsedUs; // 睡眠时间取最小的值 timeWait = std::min( delayUs, timeWait ); // 进行睡眠 单位是微秒 ret = usleep( timeWait ); if( ret != 0 ) { return false; } // 睡眠延时时间双倍自增 timeWait *= 2; // 睡眠延时时间不能超过最大值 timeWait = std::min( timeWait, maxTimeWait ); // 计算开始时间到现在的运行时间 单位是微秒 elapsedUs = GetMonnotonicTime() - startUs; } while( elapsedUs <= timeoutUs ); // 如果当前循环的时间超过预设延时时间则退出循环 // 超时退出,则返回false return false;}// 获取需要延时等待时间的绝对时间戳inline timespec* GetAbsTime( size_t milliseconds, timespec& absTime ){ // CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时, // 中间时刻如果系统时间被用户改成其他,则对应的时间相应改变 clock_gettime( CLOCK_REALTIME, &absTime ); absTime.tv_sec += milliseconds / 1000; absTime.tv_nsec += (milliseconds % 1000) * 1000000; // 纳秒进位秒 if( absTime.tv_nsec >= 1000000000 ) { absTime.tv_sec += 1; absTime.tv_nsec -= 1000000000; } return &absTime;}// sem_timedwait 实现的睡眠 -- 存在缺陷// 如果信号量大于0,则减少信号量并立马返回true// 如果信号量小于0,则阻塞等待,当阻塞超时时返回falsebool SemTimedWait( size_t timeout ){ timespec absTime; // 获取需要延时等待时间的绝对时间戳 GetAbsTime( timeout, absTime ); if( sem_timedwait( &g_sem, &absTime ) != 0 ) { return false; } return true;}int main(void){ bool signaled = false; uint64_t startUs = 0; uint64_t elapsedUs = 0; // 初始化信号量,数量为0 sem_init( &g_sem, 0, 0 ); ////////////////////// sem_trywait+usleep 实现的睡眠 //////////////////// // 获取开始的时间,单位是微秒 startUs = GetMonnotonicTime(); // 延时等待 signaled = Wait(1000); // 获取超时等待的时间,单位是微秒 elapsedUs = GetMonnotonicTime() - startUs; // 输出 signaled:0 Wait time:1000ms std::cout << "signaled:" << signaled << "\t Wait time:" << elapsedUs/1000 << "ms" << std::endl; ////////////////////// sem_timedwait 实现的睡眠 //////////////////// ///////////////////// 存在缺陷,原因当在sem_timedwait阻塞中时,修改了系统时间,则会导致sem_timedwait一直阻塞 ////////////////// // 获取开始的时间,单位是微秒 startUs = GetMonnotonicTime(); // 延时等待 signaled = SemTimedWait(2000); // 获取超时等待的时间,单位是微秒 elapsedUs = GetMonnotonicTime() - startUs; // 输出 signaled:0 SemTimedWait time:2000ms std::cout << "signaled:" << signaled << "\t SemTimedWait time:" << elapsedUs/1000 << "ms" << std::endl; return 0;}测试结果: ...

August 18, 2019 · 3 min · jiezi

Android90AudioPolicy之audiopolicyconfigurationxml解析二

前言之前通过代码说了audio_policy_configuration的解析过程,代码确实需要一定耐心来看,那么今天结合具体xml再来说明下audio_policy_configuration的解析过程 正文audio_policy_configuration.xml位于frameworks/av/services/audiopolicy/config/ 目录下 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><!-- Copyright (C) 2015 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.--><audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude"> <!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” --> <!-- Global configuration Decalaration --> <globalConfiguration speaker_drc_enabled="true"/> <!-- Modules section: There is one section per audio HW module present on the platform. Each module section will contains two mandatory tags for audio HAL “halVersion” and “name”. The module names are the same as in current .conf file: “primary”, “A2DP”, “remote_submix”, “USB” Each module will contain the following sections: “devicePorts”: a list of device descriptors for all input and output devices accessible via this module. This contains both permanently attached devices and removable devices. “mixPorts”: listing all output and input streams exposed by the audio HAL “routes”: list of possible connections between input and output devices or between stream and devices. "route": is defined by an attribute: -"type": <mux|mix> means all sources are mutual exclusive (mux) or can be mixed (mix) -"sink": the sink involved in this route -"sources": all the sources than can be connected to the sink via vis route “attachedDevices”: permanently attached devices. The attachedDevices section is a list of devices names. The names correspond to device names defined in <devicePorts> section. “defaultOutputDevice”: device to be used by default when no policy rule applies --> <modules> <!-- Primary Audio HAL --> <!--从module标签开始,module即对应的HwModule,对应初始化module 的name和halVersion,解析完module会通过AudioPolicyConfig的--> <module name="primary" halVersion="3.0"> <!--attachedDevices是最后解析的,通过AudioPolicyConfig赋值mAvailableOutputDevices(DeviceDescriptor集合)和mAvailableInputDevices(DeviceDescriptor集合) --> <attachedDevices> <item>Speaker</item> <item>Built-In Mic</item> <item>Built-In Back Mic</item> </attachedDevices> <!--defaultOutputDevice也是最后解析的,通过AudioPolicyConfig赋值mDefaultOutputDevices(DeviceDescriptor) --> <defaultOutputDevice>Speaker</defaultOutputDevice> <!-- 找到module标签后便首先开始解析mixport,将所有的mixport作为集合赋值给module下的mInputProfiles(IOProfile集合)和mOutputProfiles(IOProfile集合) 和mPorts(AudioPort集合)--> <mixPorts> <!-- 每一个mixrort就是一个IOProfile即AudioPort,对应初始化name和falgs属性,以及根据role属性判断是加入mInputProfiles或mOutputProfiles中 将所有的profile作为集合赋值给mixPort下的mProfiles--> <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY"> <!-- 每一个profile 就是一个IOProfile即AudioProfile,对应初始化format samplingRates 和channelMasks--> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </mixPort> <mixPort name="deep_buffer" role="source" flags="AUDIO_OUTPUT_FLAG_DEEP_BUFFER"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </mixPort> <mixPort name="compressed_offload" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING"> <profile name="" format="AUDIO_FORMAT_MP3" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/> <profile name="" format="AUDIO_FORMAT_AAC" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/> <profile name="" format="AUDIO_FORMAT_AAC_LC" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/> </mixPort> <mixPort name="voice_tx" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/> </mixPort> <mixPort name="primary input" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/> </mixPort> <mixPort name="voice_rx" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/> </mixPort> </mixPorts> <!--解析devicePort 标签通过HwModule初始化mDeclaredDevices(DeviceDescriptor集合)和mPorts(AudioPort集合)--> <devicePorts> <!-- Output devices declaration, i.e. Sink DEVICE PORT --> <devicePort tagName="Earpiece" type="AUDIO_DEVICE_OUT_EARPIECE" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_MONO"/> </devicePort> <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address=""> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> <gains> <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT" minValueMB="-8400" maxValueMB="4000" defaultValueMB="0" stepValueMB="100"/> </gains> </devicePort> <devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </devicePort> <devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </devicePort> <devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/> </devicePort> <devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/> </devicePort> <devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/> </devicePort> <devicePort tagName="Telephony Tx" type="AUDIO_DEVICE_OUT_TELEPHONY_TX" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/> </devicePort> <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/> </devicePort> <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/> </devicePort> <devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/> </devicePort> <devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/> </devicePort> <devicePort tagName="Telephony Rx" type="AUDIO_DEVICE_IN_TELEPHONY_RX" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/> </devicePort> </devicePorts> <!-- route declaration, i.e. list all available sources for a given sink --> <!-- 解析route标签赋值给HwModule的mRoutes(AudioRoute集合) 并对应初始化mixport即IOProfile的mSupportedDevices,这个对应规则可以参照 [Android9.0AudioPolicy之audio_policy_configuration.xml解析(一)][1]--> <routes> <route type="mix" sink="Earpiece" sources="primary output,deep_buffer,BT SCO Headset Mic"/> <route type="mix" sink="Speaker" sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/> <route type="mix" sink="Wired Headset" sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/> <route type="mix" sink="Wired Headphones" sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/> <route type="mix" sink="Telephony Tx" sources="voice_tx"/> <route type="mix" sink="primary input" sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic"/> <route type="mix" sink="Telephony Tx" sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic"/> <route type="mix" sink="voice_rx" sources="Telephony Rx"/> </routes> </module> <!-- HDMI Audio HAL --> <module description="HDMI Audio HAL" name="hdmi" version="2.0"> <mixPorts> <mixPort name="hdmi output" role="source"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000"/> </mixPort> </mixPorts> <devicePorts> <devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </devicePort> </devicePorts> <routes> <route type="mix" sink="HDMI Out" sources="hdmi output"/> </routes> </module> <!-- A2dp Audio HAL --> <xi:include href="a2dp_audio_policy_configuration.xml"/> <!-- Usb Audio HAL --> <xi:include href="usb_audio_policy_configuration.xml"/> <!-- Remote Submix Audio HAL --> <xi:include href="r_submix_audio_policy_configuration.xml"/> </modules> <!-- End of Modules section --> <!-- Volume section --> <xi:include href="audio_policy_volumes.xml"/> <xi:include href="default_volume_tables.xml"/> <!-- End of Volume section --></audioPolicyConfiguration>总结这样整个policy的xml就解析完成了。有点需要注意这里有个 ...

August 18, 2019 · 4 min · jiezi

Android90AudioPolicy之audiopolicyconfigurationxml解析一

前言说audio_policy_configuration.xml的解析之前,先熟悉下audiopolicy的启动过程,开机时会通过init.rc启动audioservice,audioservice会启动AudioPolicyService,而AudiopolicyService会创建AudioPolicyManager,这样AudioPolicyManager就被初始化了。感兴趣的可看下这个博客有具体的讲解https://blog.csdn.net/Qidi_Hu... 正文回到AudioPolicyService的onFirstRef()函数中有两句代码 mAudioPolicyClient = new AudioPolicyClient(this); mAudioPolicyManager =createAudioPolicyManager(mAudioPolicyClient); 在createAudioPolicyManager函数中会 new AudioPolicyManager(clientInterface)。此刻正式开始了AudioPolicyManager的初始化。我们看下frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp 的源码 AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/){ loadConfig(); initialize();}void AudioPolicyManager::loadConfig() {//Android7.0之后便使用此宏#ifdef USE_XML_AUDIO_POLICY_CONF if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {#else if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, getConfig()) != NO_ERROR) && (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, getConfig()) != NO_ERROR)) {#endif ALOGE("could not load audio policy configuration file, setting defaults"); getConfig().setDefault(); }}deserializeAudioPolicyXmlConfig函数的getConfig()即AudioPolicyConfig,函数声明在AudioPolicyManager.h文中中 AudioPolicyConfig& getConfig() { return mConfig; }static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH]; std::vector<const char*> fileNames; status_t ret; if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) && property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { // A2DP offload supported but disabled: try to use special XML file fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME); } //文件名#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml",位于frameworks/av/services/audiopolicy/config/目录下。 fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); for (const char* fileName : fileNames) { for (int i = 0; i < kConfigLocationListSize; i++) { PolicySerializer serializer; snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), "%s/%s", kConfigLocationList[i], fileName); ret = serializer.deserialize(audioPolicyXmlConfigFile, config); if (ret == NO_ERROR) { return ret; } } } return ret;}今天要说的重点就是这个for循环了,serializer.deserialize(audioPolicyXmlConfigFile, config) ...

August 18, 2019 · 8 min · jiezi

欢迎加入我的知识星球C语言解惑课堂

我在知识星球上开通了一个有关C语言基础答疑解惑的星球,它叫做:“C语言解惑课堂”。看这名字你就知道虽然有点俗,俗才贴近你的真正需求嘛!这是一个专门帮助C语言初学者答疑解惑的课堂。嗯~~~,关于这个星球我是有一些想法的,各位看官请继续看! 一、我为啥要开通这个星球? 我在知乎、今日头条、CSDN、微信公众号等平台做C语言基础答疑工作差不多有三四年时间了,这期间我有跟不少同学交流,在为同学们答疑解惑的过程中我有以下几点感触,不知你们是否感同身受。 1、C语言基础的疑难点太多了 很多同学学习C程序课程差不多是大二学年,早一点的大一学年学的也有,其中不少同学是非计算机专业的。在学习这门课程前,同学们的计算机基础也是各不相同,计算机基本功好一点的学习C语言时也许感觉还好一点,基本功薄弱一点的同学可能就比较吃力了,实际上在学习C语言之前大部分人的计算机基础都是薄弱的,于是在学习过程中就会感觉力不从心,从而都有一句口头禅:“这是啥?这TM又是啥?” 同学们的问题其实都很基础,之所以不会还是基本知识没能理解,话说这反而大大激发了我“帮助的热情”!得,索性我找个地方把大家都聚集起来,一起学习讨论C语言基础,在知识星球上开通一个圈子就是个不错的选择。 2、C语言的课时有点短 C程序课程一般要求一学期修完,而一学期的课时你掐掐手指都能数过来,真的很少。在这么短的时间内完完整整学完一本C程序教材是可以的,但是要完完全全地理解C语言基础就不太可能了,而且老师也不太可能在这么短的课时内把C语言所有的基础知识点全部讲到,这就导致了同学们对不少知识点没有学懂、没有学深,不知如何写程序。 你可以回想,你感觉好像C语言学会了,可是当你坐在电脑前编写C程序,是不是觉得脑袋周围一圈的问号?在我的星球里,所有的C语言基础疑难知识点解惑都可以沉淀,不管你什么时候加入我的星球,都可以看到往期的内容,不受时间的限制。 3、C语言基础有些知识点确实有点绕脑筋的 C语言是一门编程语言,它的抽象性较高。抽象的东西本身就不是特别容易理解的,因此有一些知识点是颇有点绕脑筋的意味,比如指针、二维数组、链表等。这就像顺口溜让你快速念出来,没有经过专门训练的话很难做到吧! 于是我在想怎么来给同学们解答这些知识点呢?有那么一天我手捧泡面突然灵光乍现,何不来点“饭后甜点”?同学们学习归学习,那么我就来整理C程序课程的疑难杂点,把这些点一条条地梳理出来,一条条地分门别类的归纳好,并且一条条地形象地、深入地、细致地讲解,在拉家常式的讲法中彻底理解这些疑难点,你学完了就可以来我这看看了。 换句话说,我做的事不是从头到尾把教程再写一遍,而是更加像是编写“C语言答疑解惑大全”! 4、C语言解惑内容可以沉淀 我在其他平台写文章,随着文章数越来越多,同学们想查看某个文章时可能找不到很好的查询办法,而且我自己也不一定记得我写过什么文章,所以找起来确实不方便。 在我的星球中,我写过的所有内容都沉淀下来,同学们加入后可以看到往期我写的所有内容,如果要查询也是很方便的。星球提供了搜索入口,同学们也可以根据自己的需求做关键字搜索,体验也十分不错。 二、我的星球的目标 做任何事情都要有一个目标,我做这个星球也不例外。我想了很长时间,我觉得我的星球“C语言解惑课堂”的目标是为C语言初学者解答C语言基础中的所有疑难点,打好基础,为以后的C语言进阶、学习其他技术打好基础,省的你遇到不懂的问题抱着书问你问他的,有时还找不到人问,而在这里也许你就找到了。 用一句话来概括,应该就是“让天下没有盘不会的C语言基础疑难点”! 三、我的星球提供哪些内容? 我在这个星球中致力于主动输出高价值的C语言基础疑难点解惑内容,而且都沉淀在这里,内容如下: 1、【C语言学习书籍推荐】 同学们问的比较多的一个问题是“有没有好的C语言学习书籍?”你们呀,也别烦了!我索性为你整理一份经典的计算机基础书单、C语言基础书单和C语言进阶书单等,你就直接来看就行了。 这是星球里一张书单的截图: 书单中的书都是京东的,你看中了哪本书,直接在书单里一站式购买,方便快捷! 2、【C语言编程软件推荐】 同学们也会经常问的一个问题:“有哪些好的C语言编程环境?”我也为你整理了大家伙常用的几款C语言编程软件,并尽量贴出官网下载地址,你也是直接来看就行。 3、【C语言学习的正确姿势】 同学们问的最多的估计就是这个问题了:“我怎么学习C语言呢?” 学习C语言不仅是一项智商活动,也是一项情商活动,我也给你准备了一些学习建议与方法。 这是星球里一张关于学习C语言方法的截图: 只要你真有心学习C语言,你一定能学成! 4、【C语言基础疑难点剖析】 这是我的星球主要做的事情之一。我为你整理、剖析C语言基础的所有疑难点,帮你把这些点各个击破,并且归类好,建立好索引,方便查询,助你C语言基础相关的考试、升学、工作一臂之力。还有盘不会的疑难点?我不信! 这是星球里一张疑难点剖析的截图: 5、【C语言基础试题剖析】 这也是我的星球主要做的事情之一。我为你整理、剖析C语言基础中的难题与教程的习题,帮你击破难题中的难点。 这是星球里一张试题剖析的截图: 6、【C程序代码参考】 这也是我的星球主要做的事情之一。您在学习C语言中,经常要做C编程题吧,您是不是一头雾水,无从下手?这编程题可都是考试、笔试特爱考的题型,而且往往是大题,分值比较高。我会经常给出一些编程题,并给出代码实现供参考。 这是星球里一张代码实现的截图: 7、【C语言基础学习资料】 这也是我的星球主要做的事情之一。我会与你分享优质的学习资料,让你获取高价值的学习资源。 这是星球里一张学习资料的截图: 8、【你问我答】 另外,同学们在学习C语言基础的过程中有疑问也可在星球里提出来,可以是教程上的某道题某段话等,我们一起讨论,多多互动,多多交流。 一个人的C语言学习之路是枯燥的,一群人在一起学习交流是才有趣的、有动力的! 还有更多内容,后续会推出! 四、我的星球内容的展现形式是怎样的? 星球内的内容展现形式必须得采用形象直观的,譬如全部采用如下基本三级结构形式: 【第XX篇】【大类】【小类】 1、【第XX篇】:第一个方括号表明当前内容是星球里的第几篇内容。 2、【大类】:第二个方括号表明当前内容所属的大类,比如“基础”类、“试题”类、“代码”类、“资料”类等。 3、【小类】:第三个方括号表明当前内容的提炼,比如“int类型的取值范围是多少?”、“用C实现简单的文本复制程序”等。 如果问题比较复杂,可能还会有第四个方括号。 采用这种三级结构形式,也是方便同学们的搜索。如果您记得篇号,那么可以直接搜索篇号,如下图: 如果您记得标题关键词,那么可以搜索关键词,如下图: 超简单有木有! 五、这星球适合哪些人? 如果你属于下述的几种情况之一,那么我的星球很适合你,欢迎你的加入! 1、刚入门C语言的 2、正在学习C语言打基础的,遇到很多疑问的找不到解答的 3、C语言看似学了一遍还是云里雾里的 4、学完C语言还是不懂的,不会做题的 5、有兴趣自学C语言的 六、加入的费用 ...

August 18, 2019 · 1 min · jiezi

跟我一起学习pybind11-之一

关于pybind11pybind11是一个轻量级的“Header-only”的库,它将C++的类型暴露给Python,反之亦然。主要用于将已经存在的C++代码绑定到Python。pybind11的目标和语法都类似于boost.python库。利用编译时的内省来推断类型信息。 boost.python最大问题在于,boost太过复杂和庞大。pybind11除去注释,代码仅仅4000多行,需要依赖Python2.7或Python3。 支持的编译器Clang/LLVM (any non-ancient version with C++11 support)GCC 4.8 or newerMicrosoft Visual Studio 2015 or newerIntel C++ compiler v17 or newer开始使用pybind11介绍pybind11的基本特性。 编译测试用例Linux/MacOS需要安装python-dev或者python3-dev、cmake。 mkdir buildcd buildcmake ..make check -j 4最后一行命令make check -j 4将会编译并自动执行测试用例。 Windows仅仅支持Visual Studio 2015以及更新的版本。 mkdir buildcd buildcmake ..cmake --build . --config Release --target check以上命令将会创建一个Visual Studio工程,并且该工程将会被自动编译。 注意:如果所有的测试都失败了,请确保Python二进制类型和测试用例被编译的二进制类型与处理器类型匹配。 头文件和命名空间为了简洁起见,所有的示例都将假设存在以下两行代码: #include <pybind11/pybind11.h>namespace py = pybind11;某些功能也许需要其它更多的头文件,但是那些部分将在需要时列出来。 绑定简单函数让我们以一个极度简单的函数来开始创建python绑定,函数完成两数相加并返回结果 int add(int i, int j){ return i + j;}为简单起见,我们将函数和绑定代码都放在example.cpp这个文件中 #include <pybind11/pybind11.h>namespace py = pybind11;int add(int i, int j){ return i + j;}PYBIND11_MODULE(example, m){ m.doc() = "pybind11 example plugin"; // 可选的模块说明 m.def("add", &add, "A function which adds two numbers");}PYBIND11_MODULE()宏函数将会创建一个函数,在由Python发起import语句时该函数将会被调用。模块名字“example”,由宏的第一个参数指定(千万不能出现引号)。第二个参数"m",定义了一个py::module的变量。函数py::module::def()生成绑定代码,将add()函数暴露给Python。 ...

August 18, 2019 · 2 min · jiezi

快排分析

最难的问题是这样的:他做到的哪个细节是我做不到的?如果她做到的每个细节,我都能做到呢?绝大多数人不敢追问自己、逼问自己的。人们宁愿使蛮力,宁愿身体勤奋,却不愿问更深层的问题感悟: 最难的问题是我们每个人都心存侥幸,都不愿意在每一个细微处下功夫。对“不厌其烦”这4个字不屑一顾。如此一来,就造成了我们思维当中的诸多漏洞。导致我们不断的漏知识,漏技术,漏能力,漏财富…… 快速排序无论在空间还是时间都是最好的排序算法之一~首先快排有几个优点: 1 原地排序(不需要额外的空间) 2平均状态下时间复杂度Nlogn 虽然最坏时依然为O(n^2)但是几率非常小而且 O(nlgn)中隐含的常数因子也非常小 ,属于高效排序算法 而快排的缺点则是其不稳定性因为前牵扯到空间地址的交换所以快速排序不能保证出现相同元素下的稳定排序效果。 关于快排pivot的选取 根据斯坦福算法专项课,我们有三种不同的pivot选取方式,并计算相应比较次数,分别为choose first, choose last, median of three, 还可以进行随机选取,这也是快速排序为什么是一种随机化算法。 首先来看算法导论上的第一种选取 choose last QUICKSORT(A,p,r) if(p<r) q=PARTITION(A,p,r); QUICKSORT(A,p,q-1); QUICKSORT(A,q+1,r);算法的关键部分是PARTITION 他实现了算法的原址操作。PARTITION(A,p,r) x=A[r]i=p-1for j=p to r-1if A[j]<=x i++ exchangea[j] with A[i]exchage A[i+1]with A[r] return i+1 以上PATITION算法 在左端点左边放一个哨兵也就是p-1 从左哨兵开始 j的目的是遍历一遍数组查看是否有小于A[pivot]的 若有则依次放左边。 经过PARTITION一遍过后,进入递归。为什么快排可以完成排序功能这个理解重点就在这里,每次PARTITION过后就代表有一个数完成排序所以进入递归的是没有完成排序功能的数。从方法上来看 q点 已完成排序(前面比它小,后边比他大)所以将数组其他部分进入递归 排除q点。每进行完一次PARTITION 就代表有一个数完成排序,所以如果仅仅要找第k大(小)的数 可以将快排稍微改一改 就可以实现而并不需要完全排序之后取第k个值 算法如下 //PARTITION可以不变int _k =k(第k小/大);int k =k;//这k作为变量;QUICKSORT(A,p,r,k) if(p<r) q=PARTITION(A,p,r,k) if(q-p+1<k)QUICKSORT(A,p,q-1,k); if(q-p+1>k)QUICKSORT(A,q+1,r,k-(p-l))//然后直接查找A[_k]即可。

August 17, 2019 · 1 min · jiezi

关于ucore可能会有的语法笔记待更新

如图所示~ 一开始可能看着感觉有点懵比 然而 仔细分析 不难得出这并非是定义一个结构体~, 里面的pmm_manager 为一个已经定义的结构体类型 后面的buddy... 为新创建对象名称 图片中其实就是定义一个结构体类型常量 有人会问大括号内的点的变量为什么可以,开头呢??其实这里的.是表示属于对象buddy_pmm_manager的属性而已~ static +局部变量 ~用static 若无static修饰时运行到局部变量的代码块时 才给局部变量分配内存,出了代码块后释放变量空间 而用static修饰后的局部变量使得局部变量在编译时就会得到空间 但由于是局部变量 只有进入代码块之后才会被访问所以这样的变量仅具有记忆功能~ asm伪指令 #define SEG_NULLASM \ .word 0,0; \ .byte0,0,0,0;说明.word就地生成一个指定长度的数 .byte 就地生成一个指定字节的数 以上代码生成两个指定字长度的数接着生成两个指定字节数的数。

August 17, 2019 · 1 min · jiezi

C程序员专用表白程序让你度过一个美妙的七夕节

今天的别人,今天的你 [代码运行效果截图] include<iostream.h>include<windows.h>include<stdio.h>define stoptimeshort 40define stoptimelong 100void main() { ////////////////// char ch[10]; int f9={ 0,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, 0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0 }; int s9={ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0, 0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,1,0, 1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1, 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0, 0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0, 0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, }; int t9={ 0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0, 0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0, 1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1, 0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0, 0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0, 0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0, 0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, }; system ( "title I Love You" ); ////////////////// printf ( "%s"," " ); Sleep ( stoptimelong ); ...

August 8, 2019 · 5 min · jiezi

推荐一款P2P不限速的百度云下载工具吗

目前支持百度云的不限速下载工具中,pandownload呼声最高,用的人也最多,但是用多了就会被限速封号,速度直接变成几十KB,今天就给大家推荐一款不怕被限速的下载工具——enfi下载器。 主要特点: 1、免登陆下载 不用登录百度云账号就可以下载,避免了被封号或者限速的困扰。 2、高速下载 因为是不限速,所以速度自然是比超级会员的3-5MB/S要快很多的。 3、离线下载 迅雷走了以后,很少有软件支持离线下载了,还好,enfi下载器弥补了这个空缺,让广大宅男有了新的选择。 4、挂机免费下载 相比于其他付费软件,enfi下载器是可以免费下载的,只要你挂机就可以了,原理是利用你的上传带宽,来为其他用户下载提供加速,这也是点对点下载的精髓,并且挂机的积分还可以提现。 5、按需付费 相比于百度云一个月30元的一刀切,这里提倡按需付费,用多少买多少,适合下载量不多的用户。 产品官网:https://www.enfi.cloud/#/index

August 7, 2019 · 1 min · jiezi

FRR学习第九天完整的数据中心网络模型

网络拓扑 拓扑说明实验环境是一台16G内存的主机。上面使用vmware运行了三个虚拟机,运行的系统为ubuntu-19.04。三个虚拟机采用host-only模式连接。 spine,leaf1,leaf2三个设备均为ubuntu-19.04.上面运行了FRR程序。host1,host2,host3,host4为网络命名空间。underlay网络采用的是二层模式(局限于实验条件)整个实验是一个比例缩小的数据中心spine-leaf模型。leaf2还需要作为边界网关,使用默认路由将流量发送到公网,同时作为firewall(这里只进行了nat)。 leaf作为border和vtep的功能细化图 实验功能说明整个数据中心一个租户,使用vni:100作为租户的l3vni租户使用了三个subnet。1.1.1.0/24子网有两个虚机,分布在两个vtep下,使用10作为l2vni。2.2.2.0/24和3.3.3.0/24都只有一个虚机。5.5.5.0/24作为relay子网用于连接default-vrf和evpn-vrf。整个实验需要实现租户内所有主机互通,同时主机可以访问公网。(暂时不能实现公网访问虚机,需要申请floating-ip才可以,申请了公网IP后,可以在default-vrf中做1:1 nat实现互访)spine配置bgp evpn配置router bgp 7677 bgp router-id 192.168.59.130 bgp bestpath as-path multipath-relax neighbor fabric peer-group neighbor fabric remote-as external neighbor 192.168.59.128 peer-group fabric neighbor 192.168.59.129 peer-group fabric ! address-family l2vpn evpn neighbor fabric activate exit-address-family!leaf1配置接口配置#开启转发sudo sysctl -w net.ipv4.ip_forward=1 sudo sysctl -p#添加host1sudo ip netns add host1sudo ip link add veth1 type veth peer name eth0 netns host1sudo ip netns exec host1 ip link set lo upsudo ip netns exec host1 ip link set eth0 upsudo ip netns exec host1 ip addr add 1.1.1.1/24 dev eth0sudo ip netns exec host1 ip route add default via 1.1.1.254 dev eth0sudo ip link add br10 type bridgesudo ip link add vxlan10 type vxlan id 10 local 192.168.59.128 dstport 4789 nolearningsudo ip link set br10 upsudo ip link set veth1 upsudo ip link set vxlan10 upsudo ip link set veth1 master br10sudo ip link set vxlan10 master br10sudo ip addr add 1.1.1.254/24 dev br10sudo ip link set dev br10 address 00:00:01:02:03:10 #分布式二层网关,mac需要一致#添加host2sudo ip netns add host2sudo ip link add veth2 type veth peer name eth0 netns host2sudo ip netns exec host2 ip link set lo upsudo ip netns exec host2 ip link set eth0 upsudo ip netns exec host2 ip addr add 2.2.2.2/24 dev eth0sudo ip netns exec host2 ip route add default via 2.2.2.254 dev eth0sudo ip link add br20 type bridgesudo ip link set br20 upsudo ip link set veth2 upsudo ip link set veth2 master br20sudo ip addr add 2.2.2.254/24 dev br20#添加vni 100,作为l3vnisudo ip link add br100 type bridgesudo ip link add vxlan100 type vxlan id 100 local 192.168.59.128 dstport 4789 nolearningsudo ip link set br100 upsudo ip link set vxlan100 upsudo ip link set vxlan100 master br100 #sudo ip addr add 5.5.5.254/24 dev br100 切记,作为l3vni的svi接口不能配置IP,否则收到type-5路由不会安装。sudo ip link set dev br100 address 00:00:01:02:03:04 #这个是路由mac#添加vrfsudo ip link add evpn-vrf type vrf table 100sudo ip link set evpn-vrf upsudo ip link set br100 master evpn-vrf sudo ip link set br10 master evpn-vrf sudo ip link set br20 master evpn-vrf bgp evpn配置vrf evpn-vrf vni 100 exit-vrf!router bgp 7675 bgp router-id 192.168.59.128 bgp bestpath as-path multipath-relax neighbor fabric peer-group neighbor fabric remote-as external neighbor 192.168.59.130 peer-group fabric ! address-family l2vpn evpn neighbor fabric activate advertise-all-vni exit-address-family!router bgp 7675 vrf evpn-vrf ! address-family ipv4 unicast network 2.2.2.0/24 exit-address-family ! address-family l2vpn evpn advertise ipv4 unicast exit-address-family!注: ...

August 7, 2019 · 5 min · jiezi

各种排序算法总结

排序算法是最基本最常用的算法,不同的排序算法在不同的场景或应用中会有不同的表现,我们需要对各种排序算法熟练才能将它们应用到实际当中,才能更好地发挥它们的优势。今天,来总结下各种排序算法。 下面这个表格总结了各种排序算法的复杂度与稳定性: 各种排序算法复杂度比较.png 冒泡排序冒泡排序可谓是最经典的排序算法了,它是基于比较的排序算法,时间复杂度为O(n^2),其优点是实现简单,n较小时性能较好。 算法原理相邻的数据进行两两比较,小数放在前面,大数放在后面,这样一趟下来,最小的数就被排在了第一位,第二趟也是如此,如此类推,直到所有的数据排序完成c++代码实现 void bubble_sort(int arr[], int len) {for (int i = 0; i < len - 1; i++){for (int j = len - 1; j > i; j--){if (arr[j] < arr[j - 1]){int temp = arr[j];arr[j] = arr[j - 1];arr[j - 1] = temp;}}}} 选择排序 算法原理先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。c++代码实现 void select_sort(int arr[], int len) { for (int i = 0; i < len; i++){int index = i;for (int j = i + 1; j < len; j++){if (arr[j] < arr[index])index = j;}if (index != i){int temp = arr[i];arr[i] = arr[index];arr[index] = temp; }} }插入排序 ...

August 7, 2019 · 3 min · jiezi

产品经理画流程图的工具

在流程图的绘制过程中,选择一款优秀的流程图绘制软件往往能带来事半功倍的效果,亿图就是这样一款实用的绘图软件。为什么这么说?因为一款优秀的流程图绘制软件往往具有以下三个特点,而亿图图示正好拥有这些特点。 1、操作简单 现在市面上很多的流程图绘制软件操作都过于繁琐,有时候连一个符号都需要重复画好几遍,导致绘图效率低下。而亿图流程图绘制软件采用的是拖曳式绘图方式,只需要用鼠标简单移动一下,即可轻松完成绘图。 2、功能丰富 亿图流程图软件不但自带丰富精美的模板和符号素材供我们免费使用,而且还支持对图形素材进行各种美化设置,功能之强大只有你想不到,没有它做不到。 3、兼容多种文件格式 亿图软件不仅可以导出PDF、 Word、 PPT、Excel、 图片、 HTML、Visio、SVG等文件格式,还可以导出Visio所不支持的格式,如PS&EPS等等。 系统流程图绘制步骤在选择好绘图工具以后,接下来我们就要开始准备画流程图了。 1、打开亿图图示软件,选择新建—流程图—创建空白文档进入画布,也可以选择自己喜欢的流程图模板点击进入。 2、进入之后我们可以左侧面板选择想要的图形素材,用鼠标点击或拖动它到图表区域中。 3、选择完之后双击图形可以在里面输入文本内容。输入完成后可以在右侧面板中自由更改图形线条颜色、粗细等样式。 4、重复以上步骤很快就可以将流程图画出来了,之后我们可以将画好的系统流程图进行保存或者导出发送。点击菜单栏上的文件按钮返回到软件新建界面,然后选择导出,选择需要导出的格式点击确定即可保存。 软件下载地址:亿图图示流程图软件

August 7, 2019 · 1 min · jiezi

Qt596Kit81VS2015安装及开发环境配置

1.软件下载首先需要下载qt5.9.6和Visual Studio 2015以及相关插件1.qt下载 打开qt官网下载网站:Qt官网下载地址qt5.9.6百度云下载:Qt5.9.6百度云 提取码:h96g2.VS2015下载 vs2015下载地址:https://visualstudio.microsof...vs2015百度云下载:vs2015百度云 提取码:u6if2.软件安装1.安装VS2015运行VS2015安装软件,编程语言选择Visual C++,如果还使用其他编程语言,按照自己需求进行选择,其他选项按照默认设置就行。 然后安装就一直下一步就行,安装位置按照自己需求进行修改。直至VS2015安装完成。 2.安装Qt5.9.6运行Qt安装软件qt-opensource-windows-x86-5.9.6 .exe,进行安装,安装最好在有网络的情况下,避免出现错误。以上组件可根据自己需求进行选择安装,如果你只在windows 64位下使用,选择msvc2015 64-bit,如果你需要在Linux或Android下使用,选择对应组件即可。Qt里集成了像二维图表、三维图表模块等插件,这里可以按照需求选择,如果安装空间允许,建议都选了。qt的工具就是对应的工具软件, Qt Creator 4.3.1 是用于 Qt 程序开发的 IDE; MinGW 5.3.0 是 MinGW 编译工具链; Strawberry Perl 是一个 Perl 语言工具。我这里只选择了第一项。接下来安装步骤按照下一步即可,直至安装完成。 3.安装微软调试器Kit注意:Qt没有自己的调试器,所以需要安装调试器Debuggers作为调试器,调试器在微软的官网下载,下载地址:s://www.microsoft.com/zh-cn/p/windbg-preview/9pgjgd53tn86?rtc=1我这里选择了Kit8.1,如果是Win10的系统可以选择Kit10.0,版本信息里有支持的系统版本。下载后默认按照即可 以上为Qt的安装步骤,可以在Qt下面开始编程了 3.配置VS2015要实现在VS下调用Qt Designer,需要安装相关插件。 点击扩展和更新,进入扩展和更新界面 选择:联机,搜索关键字和“Qt”,就会出现Qt Visual Studio Tools插件,点击下载安装即可。安装完成即可发现VS的菜单栏里有Qt VS Tools 4.配置Qt选择:Qt VS Tools -> Qt Options,配置 Qt 5.9.6。点击“Add”按钮,Path 选择 D:QtQt5.9.65.7msvc2015_64,然后点击“Ok”进行保存。 注意:如果没有配置QT,在新建Qt项目时会出现“Unable to find a Qt build!”错误

August 7, 2019 · 1 min · jiezi

Chap10IO流字节流和字符流的区别初学者可能有时候会分不清不过很正常这篇文章希望客运帮到各位

一、流的概念1.1、程序中所有的数据都是以流的方式进行传输或保存的1.2、程序需要读取数据的时候要使用输入流读取数据,而当程序需要将一些数据保存(写)到磁盘的时候,就要使用输出流完成。1.3、切记:程序是内,文件...是外二、流的超类2.1、字节流的超类:InputStream、OutputStream 作用:处理字节、二进制对象。(其实可用于任何类型的对象,但它不能直接处理Unicode字符)操作对象:字节流处理单元为1个字节,操作字节和字节数组2.2、字符流的超类: Reader、Writer 作用: 直接处理字符、字符串。操作对象:处理的单元为2个字节的Unicode字符拓展:字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的2.3、用途推荐 音频文件、图片、歌曲,就用字节流好点关系到中文(文本)的,用字符流好点2.4、前言:其实前言放在这里,笔者认为是不合适的,之前的铺垫只是方便读者快读进入状态 字节流和字符流的关系:字节流不足:实际开发中很多的数据是文本,字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,于是提出了字符流的概念。字符流的诞生:字符流按虚拟机的encode处理,也就是通过InputStreamReader,OutputStreamWriter进行字符集的转化 转化(注:初学者可以不看该转化知识,如果想了解原理,该段是有帮助的): InputStreamReader:用于将一个字节流中的字节解码成字符,其构造函数InputStreamReader isr = new InputStreamReader(InputStream in)的参数是一个InputStream对象,用 对象.read(char[] data),的方式获取内容OutputStreamWriter:用于将写入的字符编码成字节后写入一个字节流,其构造函数OutputStreamWriter osw = new OutputStreamWriter(OutputStream out)是一个OutputStream对象,通常用对象.write(String str)的方式写到 对象绑定的输出流上。二者都是处理流(包装流、非结点流)---不可直接操作数据!。由上可知二者底层分别是通过byte[]和String来关联它们对应的流对象!三、字节流与字符流的区别3.1、操作文件时的区别: 字节流:在操作时不会用到缓冲区(内存),是直接对文件本身进行操作的字符流:在操作时则使用了缓冲区,通过缓冲区再操作文件。3.2、存在形式区别: 在硬盘上的所有文件都是以字节形式存在的(图片,声音,视频),而字符值在内存中才会形成四、分析与优化分析: 对于3.1中的第一点:如果一个程序频繁对一个资源进行IO操作,效率会非常低优化 此时,通过缓冲区,先把需要操作的数据暂时放入内存中,以后直接从内存中读取数据,则可以避免多次的IO操作,提高效率。下面是部分代码:/*(1)对于视频、图片等文件的加缓冲优化*/byte[] fileToByteArray (String filePath){ File des = new File(filePath); FileInputStream fis = new FileInputStream(des); ByteArrayOutputStream bios = new ByteArrayOutputStream(); byte[] datas = new byte[1024]; //缓冲容器:提高效率` int len = -1; while ((len = fis.read(datas)) != -1){ bios.write(datas, 0, len); //把字节数组datas的内容 写入到 字节数组输出流 中--分段写入 } bios.flush(); //强制将缓冲区中的数据写入输出流,并清空缓冲区 return bios.toByteArray(); }/*(2)对于纯文本文件的加缓冲优化:*/ //BufferedReader,内部其实维护一个缓冲字符数组 String tempStr = ""; try (FileReader fr = new FileReader("a.txt"); FileWriter fw = new FileWriter("b.txt"); BufferedWriter bw = new BufferedWriter(fw); BufferedReader br = new BufferedReader(fr)){ //包装: while ((tempStr = br.readLine()) != null){ //将读取的"一行"字符串写入文件中 bw.write(tempStr); //写入下一行的时候,先换行 bw.newLine(); } } catch (IOException e){ e.printStackTrace(); }附:笔者并没有把规范的代码展示出来,只是把与本文相关的核心代码展示给了大家大家有兴趣可以自行补充。内容其实不多,加多几个try,catch语句即可! 希望作者的文章希望可以被您采纳,我们一起学习,共同进步!

July 16, 2019 · 1 min · jiezi

fopen函数找不到文件

有同学问我,以下代码会输出“===”,为什么呀? if( (fp = fopen("data.dat","r"))==NULL){printf("===");} 我看了下,代码是以“读”的方式打开data.dat文件,可是代码都没有指定data.dat 在哪里,程序找不到这个文件,所以就认为出错了。咋办呢?要么创建data.dat文件,且指定文件的路径;要么就以“写”方式打开文件,则data.dat文件不存,程序也会新建一个data.dat文件。

July 16, 2019 · 1 min · jiezi

C代码覆盖率

参考链接1. linux下codecoverage工具gcov/lcov使用

July 16, 2019 · 1 min · jiezi

flutter-窗口初始与绘制流程

环境: flutter sdk v1.7.8+hotfix.3@stable 对应 flutter engine: 54ad777f 这里关注的是C++层面的绘制流程,平台怎样驱动和响应绘制与渲染的过程,并不是Dart部分的渲染。 结合之前的分析,在虚拟机实例的构造函数中调用了一个重要方法DartUI::InitForGlobal(), 调用流程再罗列一下: DartVMRef::Create DartVMRef::DartVMRef DartVM::Create DartVMData::Create DartVM::DartVM DartUI::InitForGlobal()实现体很明了,注册了各种类对象的方法,也就是说,这些在dart语言继承NativeFieldWrapperClass2的类都有一份在C++层的实现,也说明了DartSDK是如何提供接口绑定与C++层的实现,相当于java语言中的jni。另外还有针对Isolate的初始化,不过只是设置了一个可以import的路径,并不重要: DartIsolate::CreateRootIsolate DartIsolate::CreateDartVMAndEmbedderObjectPair DartIsolate::LoadLibraries DartUI::InitForIsolate Dart_SetNativeResolver视口设置我们知道RuntimeController持有一个Window实例,看Window实例被创建之后做了哪些制作: RuntimeController::RuntimeController Window::Window DartIsolate::CreateRootIsolate DartIsolate::DartIsolate DartIsolate::SetWindow => UIDartState::SetWindow WindowClient::UpdateIsolateDescription => RuntimeController::UpdateIsolateDescription RuntimeDelegate::UpdateIsolateDescription => Shell::UpdateIsolateDescription ServiceProtocol::SetHandlerDescription Window::DidCreateIsolate library_.Set("dart:ui") RuntimeController::FlushRuntimeStateToIsolate RuntimeController::SetViewportMetrics Window::UpdateWindowMetrics library_, _updateWindowMetrics操作从最里层的Window一直传递到了Shell,最重要的一个作用是初始化了ViewPort(视口:用作画布的大小,分辨率等尺寸信息),再跟一下ViewPort被初始化后又如何被设置的: FlutterView.onSizeChanged FlutterView.updateViewportMetrics FlutterJNI.setViewportMetrics FlutterJNI.nativeSetViewportMetrics ::SetViewportMetrics AndroidShellHolder::SetViewportMetrics [async:ui]Engine::SetViewportMetrics RuntimeController::SetViewportMetrics Window::UpdateWindowMetrics Engine::ScheduleFrame这里从Java调用到C++,FlutterView.onSizeChanged这个操作是在FlutterView实例创建之后被系统调用的(而FlutterView的创建发生在Activity.onCreate时机),显然是响应平台层的通知,这符合我们的认知预期,因为画布的大小可能因为用户操作发生变化,dart层需要被动响应。 需要注意的是响应onSizeChanged在Platform线程,调用Engine::SetViewportMetrics切到了UI线程,铭记Engine的所有的操作都是在UI线程。 启动画帧Engine在通过RuntimeController设置了窗口的尺寸之后,调用了另一个重要方法ScheduleFrame,于是看它的实现: Engine::ScheduleFrame Animator::RequestFrame [async:ui]Animator::AwaitVSync VsyncWaiter::AsyncWaitForVsync callback_= {Animator::BeginFrame} VsyncWaiter::AwaitVSync => VsyncWaiterAndroid::AwaitVSync [async:platform]FlutterJNI.asyncWaitForVsync AsyncWaitForVsyncDelegate.asyncWaitForVsync => VsyncWaiter.asyncWaitForVsyncDelegate Choreographer.getInstance().postFrameCallback Delegate::OnAnimatorNotifyIdle => Shell::OnAnimatorNotifyIdle Engine::NotifyIdle通知VSync这里操作有些凌乱,首先切到UI线程,又切到Platform线程,其实就是为了调用平台接口,搞清这个最终目的。终于涉及到了绘制图像所需要的关键类Animator 和VSyncWaiter : ...

July 14, 2019 · 1 min · jiezi

对比-C-和-Python谈谈指针与引用

花下猫语:本文是学习群内 樱雨楼 小姐姐的投稿。之前已发布过她的一篇作品《当谈论迭代器时,我谈些什么?》,大受好评。本文依然是对比 C++ 与 Python,来探讨编程语言中极其重要的概念。祝大家读有所获,学有所成! 樱雨楼 | 原创作者 豌豆花下猫 | 编辑润色 本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/k0... 0 引言指针(Pointer)是 C、C++ 以及 Java、Go 等语言的一个非常核心且重要的概念,而引用(Reference)是在指针的基础上构建出的一个同样重要的概念。 指针对于任何一个编程语言而言都是必须且重要的,虽然 Python 对指针这一概念进行了刻意的模糊与限制,但指针对于 Python 而言依然是一个必须进行深入讨论的话题。 本文基于 C++ 与 Python,讨论了 Python 中与指针及引用相关的一些行为。 1 什么是指针?为什么需要指针?指针有两重含义: (1)指代某种数据类型的指针类型,如整形指针类型、指针指针类型 (2)指代一类存放有内存地址的变量,即指针变量 指针的这两重含义是紧密联系的:作为一种变量,通过指针可以获取某个内存地址,从而为访问此地址上的值做好了准备;作为一种类型,其决定了内存地址的正确偏移长度,其应等于当前类型的单位内存大小。 如果一个指针缺少指针类型,即 void *,则显然,其虽然保存了内存地址,但这仅仅是一个起点地址,指针会因为无法获知从起点向后进行的偏移量,从而拒绝解指针操作;而如果一个指针缺少地址,即 nullptr,则其根本无法读取特定位置的内存。 指针存在的意义主要有以下几点: 承载通过 malloc、new、allocator 等获取的动态内存使得 pass-by-pointer 成为可能pass-by-pointer 的好处包括但不限于: 避免对实参无意义的值拷贝,大幅提高效率使得对某个变量的修改能力不局限于变量自身的作用域使得 swap、移动构造函数、移动赋值运算等操作可以仅针对数据结构内部的指针进行操作,从而避免了对临时对象、移后源等对象的整体内存操作由此可见,与指针相关的各操作对于编程而言都是必须的或十分重要的。 2 C++中的引用在 C++ 中,引用具有与指针相似的性质,但更加隐形与严格。C++ 的引用分为以下两种: 2.1 左值引用左值引用于其初始化阶段绑定到左值,且不存在重新绑定。 左值引用具有与被绑定左值几乎一样的性质,其唯一的区别在于 decltype 声明: int numA = 0, &lrefA = numA; // Binding an lvaluecout << ++lrefA << endl; // Use the lvalue reference as lvalue & rvaluedecltype(lrefA) numB = 1; // Error!左值引用常用于 pass-by-reference: ...

July 12, 2019 · 2 min · jiezi

FFmpeg小点记AVDiscard的作用

声明定义AVDiscard 定义在 avcode.h 中。内容如下: /** * @ingroup lavc_decoding */ enum AVDiscard{ /* We leave some space between them for extensions (drop some * keyframes for intra-only or drop just some bidir frames). */ AVDISCARD_NONE =-16, ///< discard nothing AVDISCARD_DEFAULT = 0, ///< discard useless packets like 0 size packets in avi AVDISCARD_NONREF = 8, ///< discard all non reference AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames AVDISCARD_NONINTRA= 24, ///< discard all non intra frames AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes AVDISCARD_ALL = 48, ///< discard all };上述是FFmpeg v4.1 中的定义。简单的中文翻译下: ...

July 12, 2019 · 2 min · jiezi

sonic-orchagent与syncd之间的请求与应答

syncd进程是介于orchagent与driver之间的进程。syncd从asic-db中读取的数据经转换后调用驱动提供的sai接口进行下硬件,同时需要将驱动的应答进行一定的处理,还需要处理驱动的事件通知(比如端口up/down,mac老化等信息)。处理的消息如下图所示: orchagent与syncd之间的操作orchagent与syncd之间会进行如下几种操作: create:创建一个对象remove:删除一个对象set:设置对象属性get:获取对象属性notify:driver事件通知对于create,remove,set请求,orchagent会在sairedis层构建一个虚拟的sai层:sairedis。orchagent执行sai接口只是对asic-db进行操作,生成或者删除虚拟对象(vid)。默认所有操作都是成功的,直接返回,不等待syncd的应答。执行上图的1和6。syncd从asic-db中读出请求执行上图的2,3,4。如果4步骤返回成功,则整个请求运行结束,否则syncd将会发送shutdown通知给orchagent。orchagent会退出,如上图的5,6. 对于get操作,orchagent执行1后会使用select阻塞等待syncd的应答,如果syncd在60分钟内没有应答,那么orchagent会产生segment退出。get操作执行顺序为1->2->3->4->5->6。 对于driver的notify,orchagent会在主进程的select中监听asic-db。驱动检测到硬件事件后,调用syncd注册的回调函数通知syncd。syncd中有一个专门处理driver-notify的线程ntf-thread。ntf-thread解析driver的notify,然后通过asic-db通知orchagent。执行顺序7->8->9。 注:orchagent与syncd关于sai这一层非常相似。它们会调用大量的同名函数。这些函数只是名字相同,orchagent调用的是sai-redis库中的函数,而syncd调用的是driver提供的sai库 get操作阻塞等待orchagent执行sai的get操作时会调用到redis_generic_get函数。 std::shared_ptr<swss::ConsumerTable> g_redisGetConsumer;sai_status_t redis_generic_get( _In_ sai_object_type_t object_type, _In_ sai_object_id_t object_id, _In_ uint32_t attr_count, _Out_ sai_attribute_t *attr_list){ SWSS_LOG_ENTER(); std::string str_object_id = sai_serialize_object_id(object_id); return internal_redis_generic_get( object_type, str_object_id, attr_count, attr_list);}sai_status_t internal_redis_generic_get( _In_ sai_object_type_t object_type, _In_ const std::string &serialized_object_id, _In_ uint32_t attr_count, _Out_ sai_attribute_t *attr_list){ SWSS_LOG_ENTER(); /* * Since user may reuse buffers, then oid list buffers maybe not cleared * and contain som garbage, let's clean them so we send all oids as null to * syncd. */ clear_oid_values(object_type, attr_count, attr_list); std::vector<swss::FieldValueTuple> entry = SaiAttributeList::serialize_attr_list( object_type, attr_count, attr_list, false); std::string str_object_type = sai_serialize_object_type(object_type); std::string key = str_object_type + ":" + serialized_object_id; SWSS_LOG_DEBUG("generic get key: %s, fields: %lu", key.c_str(), entry.size()); if (g_record) { recordLine("g|" + key + "|" + joinFieldValues(entry)); } // get is special, it will not put data // into asic view, only to message queue // 写入本次get事件 g_asicState->set(key, entry, "get"); // wait for response // 创建临时 select swss::Select s; // 添加事件 s.addSelectable(g_redisGetConsumer.get()); //循环等待syncd的应答 while (true) { SWSS_LOG_DEBUG("wait for response"); swss::Selectable *sel; //阻塞等待,时间为GET_RESPONSE_TIMEOUT int result = s.select(&sel, GET_RESPONSE_TIMEOUT); //只处理应答情况OBJECT if (result == swss::Select::OBJECT) { swss::KeyOpFieldsValuesTuple kco; g_redisGetConsumer->pop(kco); const std::string &op = kfvOp(kco); const std::string &opkey = kfvKey(kco); SWSS_LOG_DEBUG("response: op = %s, key = %s", opkey.c_str(), op.c_str()); if (op != "getresponse") // ignore non response messages { continue; } sai_status_t status = internal_redis_get_process( object_type, attr_count, attr_list, kco); if (g_record) { const std::string &str_status = kfvKey(kco); const std::vector<swss::FieldValueTuple> &values = kfvFieldsValues(kco); // first serialized is status recordLine("G|" + str_status + "|" + joinFieldValues(values)); } SWSS_LOG_DEBUG("generic get status: %d", status); return status; } SWSS_LOG_ERROR("generic get failed due to SELECT operation result: %s", getSelectResultAsString(result).c_str()); break; } //超时和异常都返回SAI_STATUS_FAILURE if (g_record) { recordLine("G|SAI_STATUS_FAILURE"); } SWSS_LOG_ERROR("generic get failed to get response"); return SAI_STATUS_FAILURE;}对于get操作,当syncd比较忙的时候,极端情况下会导致orchagent异常退出。 ...

July 11, 2019 · 7 min · jiezi

征集令-云原生技术实践黑客松即将开战赛题由你定

9月,坐标北京。CNBPA(云原生技术实践联盟)与灵雀云要一起搞事情了! 9月6-8日,由CNBPA(云原生技术实践联盟)与灵雀云共同发起的“2019云原生技术实践黑客松编程比赛”将在北京开战!我们希望通过一场以“云原生技术实践”为主题的黑客松活动,为当今云原生领域相关的技术热点、企业转型升级中遇到的迫切需求,寻找新的创意和算法,帮助解决技术落地过程中遇到的实际问题! 赛制说明 7月9日--8月8日 项目征集 本次活动欢迎云原生技术相关领域厂商、互联网厂商、各行业软件开发商、云原生技术终端用户共同参与,进行大赛的项目命题并提供环境、相关场景、专家资源的支持。参与企业将作为本次黑客松大赛的战略合作伙伴! 7月15日-8月15日 选手报名 选手可关注“云原生技术社区”公众号,后续公众号会发布“黑客召集令”,填写个人(团队)参赛信息报名。 8月8日-30日 赛题筛选+公布 此阶段中,报名入围的个人(团队)选手会收到大赛组委会邀约参赛通知。赛题方向经过筛选,将和参赛通知一并发送给参赛选手。 9月6日-8日 组团参赛+评选颁奖 9月6日 :签到,热身9月6日-8日:持续创作9月8日 :演示原型+评选9月8日 :颁奖 赛题示例:流量可视化:ovs本身提供了端口流量的监控数据,同时 ovn 会在每个机器上开启一个 mirror0 端口镜像当前机器的所有容器流量。通过这些信息(或者其他你能想到的信息)你可以如何可视化容器集群的流量呢? 如对大赛项目命题感兴趣的合作伙伴,可联系大赛负责人:付智丹,获取活动相关合作介绍! **Tel/WeChat:13717573070Email:zdfu@alauda.io** 活动相关筹备进程将在“云原生技术社区”微信公众号上发布!敬请关注!!!

July 11, 2019 · 1 min · jiezi

sonic-orch调度系统1select

sonic orch调度系统之----select 常见的服务器模型有多进程模型,多线程,IO多路复用,协程等模型。sonic的核心守护进程orchagent采用的是IO多路复用模型,早期的sonic采用的是select实现多路复用,后面的版本采用的是epoll。使用select(跟多路复用的select名字一样)类对底层进行了封装,屏蔽了差异。 class Selectable事件基类,描述了epoll事件,可以是读,写,异常等事件。该结构对通用epoll事件进行了封装,真实事件通过该类派生出来,比如redis数据库事件:class RedisSelect : public Selectable;netlink事件:class NetLink : public Selectable;通知:class NotificationConsumer : public Selectable,orch执行单元:class Executor : public Selectable,定时器:class SelectableTimer : public Selectable等。 class Selectable{public: Selectable(int pri = 0) : m_priority(pri), m_last_used_time(std::chrono::steady_clock::now()) { lastusedsequence = g_lastusedsequence++;} virtual ~Selectable() = default; /* return file handler for the Selectable */ virtual int getFd() = 0; /* Read all data from the fd assicaited with Selectable */ virtual void readData() = 0; /* true if Selectable has data in its cache */ // 判断是否还有数据,如果有放入就绪事件set virtual bool hasCachedData() { return false; } /* true if Selectable was initialized with data */ // 判断是否需要读取初始数据 virtual bool initializedWithData() { return false; } /* run this function after every read */ // 更新事件数 virtual void updateAfterRead() { } int getPri() const { return m_priority; }private: friend class Select;//友元类为Select // only Select class can access and update m_last_used_time std::chrono::time_point<std::chrono::steady_clock> getLastUsedTime() const { return m_last_used_time; } // 最后使用序列号 unsigned long getLastUsedsequence() const { return lastusedsequence; } // 跟新最后使用序列号 void updateLastUsedTime() { m_last_used_time = std::chrono::steady_clock::now(); lastusedsequence = g_lastusedsequence++; } // 优先级,实现基于优先级调度 int m_priority; // defines priority of Selectable inside Select // higher value is higher priority std::chrono::time_point<std::chrono::steady_clock> m_last_used_time; unsigned long lastusedsequence;//上次使用序列号 static unsigned long g_lastusedsequence;//全局基准序列号,用于对同优先级业务进行公平调度};class Selectclass Select{public: Select(); ~Select(); /* Add object for select 给epoll添加一个事件 */ void addSelectable(Selectable *selectable); /* Remove object from select 删除一个epoll事件 */ void removeSelectable(Selectable *selectable); /* Add multiple objects for select 添加多个epoll事件 */ void addSelectables(std::vector<Selectable *> selectables); enum {//返回的事件类型 OBJECT = 0, ERROR = 1, TIMEOUT = 2, }; //执行epoll int select(Selectable **c, unsigned int timeout = std::numeric_limits<unsigned int>::max()); int select(std::vector<Selectable *> &vc, unsigned int timeout = std::numeric_limits<unsigned int>::max());private: //epoll事件比较函数,通过该函数实现事件的优先级 struct cmp { bool operator()(const Selectable* a, const Selectable* b) const { /* Choose Selectable with highest priority first */ if (a->getPri() > b->getPri()) return true; else if (a->getPri() < b->getPri()) return false; /* if the priorities are equal */ /* use Selectable which was selected later */ if (a->getLastUsedsequence() < b->getLastUsedsequence()) return true; else if (a->getLastUsedsequence() > b->getLastUsedsequence()) return false; /* when a == b */ return false; } }; //epoll轮询函数 int poll_descriptors(Selectable **c, unsigned int timeout); int poll_descriptors(std::vector<Selectable *> &vc, unsigned int timeout); int m_epoll_fd;//epoll句柄 std::unordered_map<int, Selectable *> m_objects;//监听的事件句柄与其对应的selectable之间的关系 std::set<Selectable *, Select::cmp> m_ready;//已经就绪的事件集合,提供了比较函数,从而实现优先级调度};Select::Select()Select::Select(){ m_epoll_fd = ::epoll_create1(0);//创建epoll句柄 if (m_epoll_fd == -1) { std::string error = std::string("Select::constructor:epoll_create1: error=(" + std::to_string(errno) + "}:" + strerror(errno)); throw std::runtime_error(error); }}Select::~Select()Select::~Select(){ (void)::close(m_epoll_fd);}void Select::addSelectable(Selectable *selectable)void Select::addSelectable(Selectable *selectable){ const int fd = selectable->getFd(); if(m_objects.find(fd) != m_objects.end())//已经添加了该事件,退出 { SWSS_LOG_WARN("Selectable is already added to the list, ignoring."); return; } m_objects[fd] = selectable; if (selectable->initializedWithData())//是否已经有数据可读,读出已有的数据 { m_ready.insert(selectable); } //添加可读事件 struct epoll_event ev = { .events = EPOLLIN, .data = { .fd = fd, }, }; int res = ::epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, fd, &ev); if (res == -1) { std::string error = std::string("Select::add_fd:epoll_ctl: error=(" + std::to_string(errno) + "}:" + strerror(errno)); throw std::runtime_error(error); }}void Select::removeSelectable(Selectable *selectable)void Select::removeSelectable(Selectable *selectable){ const int fd = selectable->getFd(); m_objects.erase(fd); m_ready.erase(selectable); //从epoll中删除事件 int res = ::epoll_ctl(m_epoll_fd, EPOLL_CTL_DEL, fd, NULL); if (res == -1) { std::string error = std::string("Select::del_fd:epoll_ctl: error=(" + std::to_string(errno) + "}:" + strerror(errno)); throw std::runtime_error(error); }}void Select::addSelectables(vector<Selectable *> selectables)void Select::addSelectables(vector<Selectable *> selectables){ for(auto it : selectables)//添加多个事件 { addSelectable(it); }}int Select::poll_descriptors(......)提取一个就绪事件 ...

July 10, 2019 · 5 min · jiezi

算法第一课学习笔记

一、哈佛大学智商测试(离散数学)1. 题目皇帝不是穷人,在守财奴之中也有穷人,所以,有一些( )并不是( )。 A. 皇帝,皇帝B. 守财奴,守财奴C. 守财奴,皇帝D. 皇帝,守财奴2. 解答这题可以采用离散数学中的逻辑推理来做,首先,假设下列命题为真: p: 这个人是皇帝q: 这个人是穷人r: 这个人是守财奴皇帝不是穷人: p → ¬q在守财奴之中也有穷人: ∃x(x∈r ⋀ x∈q)然后,根据p → ¬q得到其逆否命题q → ¬p也为真, 此时,由∃x(x∈r ⋀ x∈q)和q → ¬p,根据假言三段论可推知∃x(x∈r ⋀ x∈¬p), 翻译之后就是:存在一些人是守财奴且不是皇帝。 故而此题选C,有一些守财奴并不是皇帝。 3. 相关知识:假言三段论假言三段论又称假言推理。假言推理总是以假言判断为前提来进行推理的。在逻辑运算符记号中表示为: p → qq → r所以 p → r经典例子: 人都是要死的,苏格拉底是人,所以苏格拉底是要死的。参见: Hypothetical syllogism - Wikipedia 二、天平和硬币1. 题目现在有16枚外形相同的硬币,其中一枚是假币,且己知假币比真币重量轻。先给定一架没有砝码的天平,问至少需要多少次称量才能找到这枚假币? 思考:给出一种称量方案容易,但如何证明这种方法用到的称量次最少呢? 2. 解答可以采取三分法,将硬币分为三堆: A:5枚B:5枚C:6枚先称量A和B,若A(或B)较轻,则可以通过将5枚硬币再分为2枚、2枚、1枚三堆来称量,最多需要两次称量找出假币。 若A和B平衡,则假币在剩下的六枚硬币中,将其分为2枚、2枚、2枚三堆,天平两端各放两枚,无论平或不平,最多只需要两次称量就能找出假币。 综上,至少需要称量3次才能找到假币。 3. 理论下界既然一次天平称量能得到左倾、右倾、平衡三种情况,若把一次称量当成一位编码,该编码是3进制的,则该问题转换为:用多少位编码能够表示16呢? 解答:假设需要n位,则3^n >= 16,两边取对数得到n >= 2.52,这表示至少3次才能找到该轻质的假币。 三、链表相加1. 题目给定两个链表,分别表示两个非负整数,它们的数字按逆序存储在链表中,且每个结点只存储一个数字,计算两个数的和,并且返回和的链表头指针。 ...

July 10, 2019 · 3 min · jiezi

我对C的前景看法-C和C的对比

从我进入CSDN VC/MFC的那一刻起,总能在这个版块看到这样那样的讨论VC、C++语言是不是要淘汰,被C#彻底取代诸如此类,这就是传说中的“月经贴”。一些新手可能就会因为看到这些帖子感到迷茫,比如他们正在开始学VC,要是突然发现被淘汰了,岂不是白学了?对此我认真看了每次的“月经贴”,论坛中的一些老大们的言语我也都看在眼里,他们对此是看得很清楚的。接下来我将会用中肯的语言说说我的见解,各位有什么意见可以跟帖讨论。 先说语言,C++作为数据结构入门的最佳语言的说法不少,其实有人认为Java更加适合做数据结构入门的语言,我感觉其实要看个人的喜好。但是C#肯定不会用来做数据结构入门的语言,C++我认为是一个比较好的理解远离的语言,是一切之本。Java中所说的引用,其实也就是C++中的地址传递的一种形式罢了,C++灵活的强制转换,我觉得有得天独厚的优势,拿到一个地址,就能让它成为指向一个数据结构的指针,这是其他语言所无法比拟的。 没有什么语言能比C++更加贴近Windows本身了,这一点也是不可否认的。如果哪一天C#也能写驱动的时候,那么C++就真的会淘汰了(这天可能不会太远又或者很遥远)。 C#能做的,C++不一定都能做,C++能做的,C#也不一定都好做,所以经常看到有人拿这2个语言对比,我觉得确实没什么必要。 C#繁杂的调用Win32 API的方式,确实让人很不舒服,C++这一点做得比它好。但是,C#强大的网络操作,代码量大大地低于C++,我们抛弃不用Socket,改用WinInet或是MFC中的封装好的网络类,比如CHttpFile,还是没C#简单。C#的傻瓜式的类库操作和面向对象编程的完美特性,确实让其成为了Windows平台上最受欢迎的语言。也许,是微软想将Win32 API渐渐地全部封装到C#的类库中去,我感觉微软有这个意图,也许将来C#就不需要调用那些繁杂的Win32 API了,类库中均有提供也说不定。站群系统 我们为什么学VC?这点我曾经也问过自己。圣经上说:你必须知道真相,真相会使你自由。我们学VC是为了自由,不受微软的框制。微软通过种种的FrameWork让你陷于其中,你觉得,哇,原来编程是这么容易啊,几句话就能搞定。你想知道微软在内部干了些什么呢?你不知道,当然,你如果认为你没必要知道,这个我完全赞同,我后面会说。就说杀进程吧,C#中不通过Win32 API就那么一种方式,通过System.Diagnostics.Process找到进程,然后Kill之(如果我说的不对,使用C#的朋友请指正),而C++中,我们完全有无数种选择,就说不邪恶的,TerminateProcess,邪恶点的呢?不计其数。进程内存填0,卸载模块,消息洪水,句柄强制关闭,强制释放它的堆,太多太多了,甚至我注入进去,内部Raise一个异常,它也就挂掉了。保护进程, 不管内核下还是应用层,C#肯定是做不了的,C++有自己高端的地方,其他语言无法涉足。你见过C#做的杀毒软件吗? C#,极其简易的界面操作,令人看着很舒服的编码,他有太多的地方,C++根本无法涉足。拿C++做Web Services除非是大脑进水,用C++做网络蜘蛛,也根本是无聊之举,要么就C#,要么就拿Python来干(个人漏见),超级大量的数据,拿垃圾回收基本上为0的C++去做,简直就是自找麻烦。C#的WebForm,和Jsp占据着Web的大片江山,C++能吗?不能。C#是一门博大精深的语言,类库强大到变态,基本上可以这么讲,所以用C++的朋友也不能随便贬低C#程序员,C#的程序员的层次分得比C++多很多,高层的也是象牙塔级别的。C#的架构师,那种恐怖的实力,C++中级程序员是无法想象的。C#更加注重于软件工程的应用,各种设计模式的使用,C++则注重于实现功能。 C#的前景是不错的,我个人很看好它,MSDN 杂志每期的文章可能有8成是C#,C++不算很多,微软的重心可能在C#上,希望C#逐渐能取代C++。但是C++会这么容易被取代?不可能。就像一个搞管理的,你让他接管搞开发的人,让他从干一样到同时兼干这二样,会有这么简单么?c++的生命力至少还要7-8年,之后才会渐渐的淡出,但是只要追求自由的人存在,C++就不会消失;只要操作系统一天是用C++写的,C++就不会消失。 下面说说语言的选择问题,这2种语言各自的优劣,我在上面都做了简要的比较。我在月经贴中总看到争辩,讨论这2种语言哪个更好,甚至会出现不和谐的字眼。我觉得这是何必呢?各自有各自的天地,请你也不要再说什么C++会没落的的话,那是你自己不用,就说C++没落?就像MSDN英文你看不懂,你就说API垃圾?你自己心里认为它不行,怎么贬低,这个随便你自己心里高兴,别拿出来充专家,更别用自己幼稚的思想左右别人。微软4年前就放言,C#将要取代C++,今年都2009了,每年还不是一样有人跳出来叫嚣? 选什么语言,随自己高兴,需要哪个用哪个,这是不矛盾的。开发数据库,我倾向于用C#,做Windows底层,我肯定是C++,都掌握下没有坏处。VS2010中C++的新特性各位同胞可以关注下,不知道会加入什么,VC2008的特性我探索了这么久,还没有探索结束。C#是越来越好用了,Linq to Sql让不懂数据库的人都能去操做数据库了,辅助一些第三方类库,能发挥强大的威力。完全取代C++,等操作系统是C#写的,C#能开发系统中的一切组件的时候再说吧,至少现在C#还不能开发IsAPI,驱动吧?嘻嘻

July 9, 2019 · 1 min · jiezi

201907月技术笔记

Week 2820190709svn版本和测试环境不一样,提交到开发库上后,需要全量到测试环境上测试。mv文件夹时,第一反应想用-r参数呢。 mv /dir/* /dirbak,还是支持-r,-i,-f参数的啊。参考链接:linux实用命令之如何移动文件夹及文件下所有文件提交开发库时没确认版本,已经用svn在73a上进行了add操作了并没有commit,跟人说了一嘴后回馈是90a,我看了一下单子确实是,要想着如何在svn中取消add 选中add上去的文件或文件夹,右击有个Undo Add,然后就OK了。参考链接:SVN中取消addget到的一些知识点 微博上逛到有人推荐Goodrich 写的《Data Structures and Algorithms in C++》,没找到电子书,看到人家这书的官网了。今天偶尔听到视频里显式调用析构函数时,析构函数没有重载(编译器只有显式析构函数,但之前听另一个说编译器会有显式析构函数,默认析构函数) 参考链接:(1)、显式调用析构函数; (2)、显式析构函数的陷阱及解决方案(转)可以理解为默认析构是删除类里对象,显式析构成员函数是delete掉malloc分配的内存,这里面有两次调用。

July 9, 2019 · 1 min · jiezi

Rainbond-515发布企业应用市场远程一键安装

2019年7月8日,Rainbond发布5.1.5版本,本次版本更新带来了全新的应用市场交付Pipeline体验,并对源码类服务的运行机制、ServiceMesh架构、服务管理等方面做了大量优化。 Rainbond:支撑企业应用的开发、架构、交付和运维的全流程,通过“无侵入”架构无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和物理服务器。发布版本:5.1.5版本更新:推荐更新范围:应用市场、源码构建、ServiceMesh架构、日志收集下面为大家带来详细的版本解读: Rainbond应用市场体验升级 Rainbond应用市场一直以来都是Rainbond的重点方向,有用户会问,Helm社区目前越来越成熟,应用越来越多,你们为什么不遗余力做自己的应用市场。不可否认,Helm应用规范已经得到了Kubernetes社区用户的认可,越来越多的开源应用企业在贡献Helm应用。但是,用过的人都知道,Helm应用规范是技术性要求很高的应用打包方式,要制作一个优秀的Helm应用,其难度甚至远远超出Kubernetes原生规范。Rainbond项目的目标是让广大用户无需学习Kubernetes技术(甚至无需学习容器技术)即可将自己的应用在云上开发、交付和运维。Rainbond应用市场的目标就是让所有开发者能够低门槛的,一键完成从代码到应用完整交付。 本次升级我们带来如下功能: 支持直接从云端安装应用到本地。 过去的版本中我们不得不先完成从云端同步到本地市场,再选择安装应用安装,过程略微繁琐且隐含较深,现在你可以直接查看到云端应用并直接安装。云端应用市场直接安装应用发布私有的应用到云端应用市场,交付给你想要交付的Rainbond用户。 应用市场为每个应用中的所有介质资源进行加密授权,发布的私有应用只有你授权的用户可以下载安装,跨云交付将非常简单。发布社区公开应用。 Rainbond应用流通将携带发布人的信息,你可以将你的企业应用上云推广了。除此之外,Helm应用那么多如何让Rainbond用户可以直接安装也是我们必须要解决的问题。我们正在进行的是将Helm应用转化为Rainbond应用存放于Rainbond应用市场中供用户直接安装使用。这个进程请关注:https://market.goodrain.com/h... ServiceMesh架构支持入站网络治理 本次版本继续对ServiceMesh架构进行优化,数据面板层面envoy版本升级到v1.9.0版本。控制面板层面增加了入站方向的网络治理支持,包括基于连接数、并发请求数等条件的连接熔断功能,同时引入了基于第三方限流服务的全局限流功能。优化了XDS控制器,有效比对资源的实际变化,有效降低推送到envoy的更新事件频率。 ServiceMesh透明化架构植入 Rainbond用户只需要开通相应的插件即可将业务服务与ServiceMesh架构相结合。 新增综合网络治理插件,支持入站网络治理 其他功能优化 源码构建的服务版本介质由slug包更改为容器镜像,源码类服务的运行将不再依赖于分布式文件系统,运行环境支持版本化控制。服务构建任务是一个消耗管理节点资源的动作,批量的服务构建如果不受限制将导致管理节点的资源耗尽。此次更新带来了构建任务的并发限制,最大并行任务数取决于当前管理节点的CPU核数。未能执行的任务将在消息系统中等待。优化了服务访问策略自动以环境变量形式注入服务的策略,便于服务能够获取当前服务的访问策略,对于部分Web类服务非常有用。更改了服务插件与应用运行时的通信地址,由过去Docker0网桥网卡地址更改为节点IP地址,如此移除了过去版本对Docker0网桥网卡IP地址的依赖。避免了如下异常情况出现:性能分析数据无法获取, 服务发现工作异常服务容器的标准输出和错误输出日志收集方式由之前的node服务从docker进程获取更改为node服务从宿主机日志文件获取,减小docker进程的压力,同时减小了日志呈现给用户的延迟时间。优化了grctl service get命令,便于用户之间查看服务的容器运行状态。BUG修复 修复自定义服务访问域名访问数据被忽略的BUG。修复服务连接信息前端未显示分页模块导致显示不全的BUG。修复服务构建版本、已删除服务持久化数据自动清理未正常工作的BUG,新版本中默认开启清理。应用备份时更改备份策略为只备份服务当前运行的构建版本,减小备份数据,提供备份成功率。修复了应用市场安装应用第一次未构建成功,后续无法继续构建的BUG。修复了服务环境变量与连接信息转移未生效的BUG。修复了应用升级时,新增的服务包含插件无法直接完成插件挂载的BUG。修复了节点更改节点属性后元数据不自动更改的BUG。修复了NodeJS语言类型设置Runtime版本失败无法编译的BUG。版本安装 当前版本为5.1系列最新版本,从发布之日起安装的5.1版本系列平台即时最新的5.1.5版本,安装参考:Rainbond集群安装 版本升级 升级要求和注意事项 V5.1.5版本支持从V5.1.2-V5.1.4版本升级,如果你还未升级到V5.1.2版本,参考V5.1.x版本升级文档,先升级至V5.1.2版本:grctl version, 例如版本显示如下:Rainbond grctl v5.1.4-release-1b49703-2019-05-19-10升级过程会重启管理服务,因此只有单管理节点的集群会短暂影响控制台操作,请选择合理的升级时间段 。下载 5.1.5 更新包 离线包镜像大小约650MB,需要保证当前集群磁盘可用空间至少不低于2G # Rainbond 组件升级包wget https://pkg.rainbond.com/offline/5.1/rainbond.images.2019-07-07-5.1.5.tgz -O /grdata/services/offline/rainbond.images.upgrade.5.1.5.tgz# 升级脚本包wget https://pkg.rainbond.com/offline/5.1/rainbond-ansible.upgrade.5.1.5.tgz -O /grdata/services/offline/rainbond-ansible.upgrade.5.1.5.tgz解压安装脚本,执行升级脚本 rm -rf /tmp/rainbond-ansiblerm -rf /grdata/services/offline/upgradetar xf /grdata/services/offline/rainbond-ansible.upgrade.5.1.5.tgz -C /tmp/cd /tmp/rainbond-ansible/scripts/upgrade/bash ./upgrade.sh升级完成验证 执行 grctl cluster确定所有服务和节点运行正常grctl version 确认版本已升级到5.1.5,运行组件镜像版本为 v5.1.5-release升级如有问题,请至社区反馈。

July 8, 2019 · 1 min · jiezi

什么是程序

虽然在这里我们主要关注的是操作系统,但毕竟操作系统的目的就是能更好的运行用户程序,而且该教程主要是站在程序员的角度来讲解操作系统的,作为程序员是绕不开程序这个话题的。因此在深入理解操作系统之前还是有必要来聊一聊程序是怎么一回事。 那究竟是什么是程序呢?我们引用Wikipedia对计算机程序的定义: A computer program is a collection of instructions that performs a specific task when executed by a computer.翻译成大白话就是,能指挥计算机干活的一堆指令就叫计算机程序。那怎样才能写出指挥计算机干活的指令呢?这就涉及到程序是怎么来的。 程序是怎么来的呢?程序是我们伟大的可敬的广大程序员们用编程语言一个字符一个字符写出来的文本字符串,只不过这些文本字符串是人类可以认识的。无论用的什么程序语言,C/C++、Java、Python、JavaScript、C#、Perl、Lua、Shell、汇编语言等等等等,只要是你写出来的文本字符串能指挥计算机干活,这都叫程序。这些程序员认识的文本字符串就是可以指挥计算机完成特定任务的指令。你可能有点糊涂了,这些文本字符串真的就是计算机能用来完成特定任务的指令吗?计算机不是只认识0和1这两个数字吗? 天才的榆木疙瘩计算机其实是一个数学学得非常差的家伙,以至于差到只能认识两个数,0和1,其它的就都不能认识了,我们要面对的就是这样一个榆木疙瘩。 虽然这个榆木疙瘩数学不好识数不多,但是这家伙有一个我们人类难以望其项背的能力,不,对于人类来说简直就是超能力,那就是这个榆木疙瘩算数非常快,对于简单的加法我们人类可能一般一秒能算不超过10个,但是计算机一秒可以完成数十亿次的计算,简直是天才!虽然计算机能认识的数就只有0和1这么简单,但是其计算速度体现出了简单的威力。 从文本字符串到机器指令至此,我们知道程序员(人类)和计算机是两个完全不同的物种,不同的物种能理解的语言是完全不同的,就好比普通的人不会明白一群鸟语在说什么一样,我们人类也不能打开vim或者宇宙无敌IDE——Visual Studio直接写0和1吧(虽然这是可以的,早期的程序员确实就是这么干的,牛不牛)。 因此,我们需要某种魔法把人类认识的C/C++、Java、Python之类的翻译出计算机可以认识二进制01指令。这样的魔法就来自两个东西,编译器和解释器。 翻译官编译器和解释器编译器大家应该都比较熟悉,我们写好C/C++程序后第一步就是编译,这里编译工作就是编译器来完成的。你可以简单的理解为编译器把C/C++程序直接翻译成计算机可以认识的01二进制机器指令。 对于解释器有的同学可能就不是那么熟悉了,写Java、Python、C#程序的时候你从没有听说过要“编译一下Java,编译一下Python,编译一下C#”吧。你可能会说C/C++程序我能理解了,编译器把C/C++程序直接翻译成了01二进制机器指令,那Java、Python一类的程序是怎么运行的呢? 大家可以想一想,你在写Python、Java、C#程序之前是不是要安装一堆东西,称之为“运行时环境”? 如果你想不起来,赶紧重新搭一套环境试试是不是这么回事。 这里的运行时环境其实就是解释器。你可以把这个解释器简单的理解为就是一个程序,只不过。。。注意注意!!!前方高能!!!,只不过是解释器这个程序可以运行你写的Java、Python、C#的程序,解释器是一个可以运行程序的程序!!!那解释器这个程序又是怎么来的呢?一般情况下这些解释器其实是用C/C++写出来的。只不过用C/C++写的这个程序专门用来执行你写的Java、Python之类的程序,高能完毕。 伟大的C语言希望到目前为止你还没有晕,到这里我们知道了,不管我们用的是Java、Python、JavaScript什么的也好最终都逃不出C/C++(Go等语言除外),CPU不直接执行Java、Python、JavaScript之类的解释型语言程序,CPU可以直接执行的是解释器代码,解释器最终来执行Java等程序,这就是解释型语言效率不如编译型语言效率高的原因。因为C/C++程序最终被编译器翻译成了01机器指令,CPU可以直接运行运行机器指令,而对于解释型语言来说CPU首先执行的是解释器的程序,然后解释器再执行你写的程序,性能上当然不及编译型语言。 而C++程序其实在编译过程中也会转化为C程序然后再转为01二进制机器指令,并且们使用的Windows、Linux、MacOS等操作系统同样是用C语言来编写的,从最底层的操作系统到上层的应用程序实际上都逃脱不了C语言。 从这个角度看,C语言真是一门伟大的语言。 回到操作系统饶了一大圈我们回到操作系统,接下来关于操作系统的讲解中涉及到示例程序没有明确说明的话指的是C语言程序。请注意,如果你对C语言不熟悉也没有关系,我们示例都非常简单不会涉及到复杂的C语言相关概念与用法,有任何语言的使用经验都可轻松应对。 C语言程序编译好后生成的可执行程序在Windows中就是我们熟悉的exe程序,在Linux下是elf程序,这些可执行程序编译好后和普通文件一样存放在磁盘当中。 在接下来关于操作系统的讨论当中,没有明确说明的话,以下几个词汇,"程序","用户程序","应用程序"指的都是编译好后放在磁盘上的可执行程序。 操作系统也是程序一定要认识到,操作系统也是程序,只不过这个程序不是简单的往屏幕上打印helloworld,不能用来文字语音视频聊天,不能用来上网,不能用来看电影,不能用来玩游戏。那么这个貌似什么娱乐设施都提供不了的程序有什么用呢?这个程序的作用无比重要,该程序的作用是为以上用户程序提供一个良好的运行环境,管理计算机硬件资源包括:CPU、内存、磁盘、网卡、外设等等等等,这个程序就是该教程重点关注的操作系统。 总结这是该教程的第一节,在这一节中我们从各个方面讲解了程序这一话题。 程序分为编译型程序,比如C/C++,以及解释型程序比如Java、Python、JavaScript等。编译型程序被编译器直接翻译成CPU可以直接运行的机器指令,而解释型程序无需编译,其运行依靠的是解释器,解释器是一个可以执行程序的程序,解释器这个程序一般是由C/C++程序编写的。 需要我们注意的是操作系统也是一个程序,只不过这个程序的作用比较特殊,这个程序是用来管理计算机系统中各种软硬件资源的,比如提供进程、线程机制,管理CPU等等,这个程序也是接下来该教材的主角。 更多计算机内功文章,欢迎关注微信公共账号:码农的荒岛求生。 计算机内功决定程序员职业生涯高度

July 6, 2019 · 1 min · jiezi

linux下文件描述符限制

一.问题描述在调试一个问题的时候,socket始终连接不上,返回的句柄大约是1030左右。开始的时候是好的,运行一段时间后出现的问题。 二.问题分析问题过去有段时间了,忘记当时怎么想到是超过文件描述符限制了。大概是根据句柄的值或者返回的错误码了。嗯。linux下文件描述符最大限制默认最大为1024,通过 [root@localhost ~]# ulimit -n1024这个命令可以查看。此值可以修改。进程的文件描述符,可以通过 [root@localhost ~]# ls -al /proc/13623/fd |wc -l59输出的数字即为该进程文件描述符的个数。修改linux下文件描述符限制的方法:临时修改使用:ulimit -HSn 65536 其实1024这个限制一般足够了,至于超过此数值一般也是程序中的bug。在调试的程序中没new一个socket的时候,都新打开了一个文件,而忘记关闭,从而导致越来越多。

July 5, 2019 · 1 min · jiezi

sonic容器swss启动过程

sonic容器swss启动过程sonic业务进程都是运行在容器中的,那容器启动后是如何启动它的服务呢。 要分析这个问题,首先要搞清楚容器构建过程。我们以docker-orchagent容器为例进行分析。 Dockerfile文件sonic中的Dockerfile由Dockerfile.j2文件生成。 docker-orchagent/Dockerfile.j2 FROM docker-config-engineARG docker_container_nameRUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name\/%syslogtag%/;" /etc/rsyslog.conf## Make apt-get non-interactiveENV DEBIAN_FRONTEND=noninteractiveRUN apt-get updateRUN apt-get install -f -y ifupdown arping libdbus-1-3 libdaemon0 libjansson4## Install redis-tools dependencies## TODO: implicitly install dependenciesRUN apt-get -y install libjemalloc1COPY \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor -%}debs/RUN dpkg -i \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor %}## Clean upRUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -yRUN rm -rf /debsCOPY ["files/arp_update", "/usr/bin"]COPY ["enable_counters.py", "/usr/bin"]COPY ["start.sh", "orchagent.sh", "swssconfig.sh", "/usr/bin/"]COPY ["supervisord.conf", "/etc/supervisor/conf.d/"]## Copy all Jinja2 template files into the templates folderCOPY ["*.j2", "/usr/share/sonic/templates/"]#程序的入口点ENTRYPOINT ["/usr/bin/supervisord"]从上面的配置来看,容器启动后制定的程序为:/usr/bin/supervisord ...

July 2, 2019 · 5 min · jiezi

vpp-hqos分析

vpp支持两套qos实现,一套是基于policer实现的qos,另外一套是基于dpdk的qos套件实现的hqos。 基本流程图 如上图所示,worker线程从nic中读取报文进行处理,调用dpdk设备发送函数时,如果配置了hqos,那么设置hqos相关参数,将其送入swq队列(swq队列与worker线程是1:1的关系),worker线程处理结束,hqos线程(根据配置决定个数)轮询从swq中读取报文进行qos处理,qos使用的是dpdk qos套件。 HQOS配置解析dpdk配置dpdk { socket-mem 16384,16384 dev 0000:02:00.0 { num-rx-queues 2 hqos #使能网卡hqos } dev 0000:06:00.0 { num-rx-queues 2 hqos #使能网卡hqos } num-mbufs 1000000}cpu配置cpu { main-core 0 corelist-workers 1, 2, 3, 4 corelist-hqos-threads 5, 6 #启动两个hqos线程,分别使用cpu5和6}通过上面两步配置即可开启hqos配置,会使用默认的hqos配置。 dpdk qos相关参数配置port configurationport { rate 1250000000 /* Assuming 10GbE port */ frame_overhead 24 /* Overhead fields per Ethernet frame: * 7B (Preamble) + * 1B (Start of Frame Delimiter (SFD)) + * 4B (Frame Check Sequence (FCS)) + * 12B (Inter Frame Gap (IFG)) */ mtu 1522 /* Assuming Ethernet/IPv4 pkt (FCS not included) */ n_subports_per_port 1 /* Number of subports per output interface */ n_pipes_per_subport 4096 /* Number of pipes (users/subscribers) */ queue_sizes 64 64 64 64 /* Packet queue size for each traffic class. * All queues within the same pipe traffic class * have the same size. Queues from different * pipes serving the same traffic class have * the same size. */}subport configurationsubport 0 { tb_rate 1250000000 /* Subport level token bucket rate (bytes per second) */ tb_size 1000000 /* Subport level token bucket size (bytes) */ tc0_rate 1250000000 /* Subport level token bucket rate for traffic class 0 (bytes per second) */ tc1_rate 1250000000 /* Subport level token bucket rate for traffic class 1 (bytes per second) */ tc2_rate 1250000000 /* Subport level token bucket rate for traffic class 2 (bytes per second) */ tc3_rate 1250000000 /* Subport level token bucket rate for traffic class 3 (bytes per second) */ tc_period 10 /* Time interval for refilling the token bucket associated with traffic class (Milliseconds) */ pipe 0 4095 profile 0 /* pipe 0到4095使用profile0 pipes (users/subscribers) configured with pipe profile 0 */}pipe configurationpipe_profile 0 { tb_rate 305175 /* Pipe level token bucket rate (bytes per second) */ tb_size 1000000 /* Pipe level token bucket size (bytes) */ tc0_rate 305175 /* Pipe level token bucket rate for traffic class 0 (bytes per second) */ tc1_rate 305175 /* Pipe level token bucket rate for traffic class 1 (bytes per second) */ tc2_rate 305175 /* Pipe level token bucket rate for traffic class 2 (bytes per second) */ tc3_rate 305175 /* Pipe level token bucket rate for traffic class 3 (bytes per second) */ tc_period 40 /* Time interval for refilling the token bucket associated with traffic class at pipe level (Milliseconds) */ tc3_oversubscription_weight 1 /* Weight traffic class 3 oversubscription */ tc0_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 0 */ tc1_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 1 */ tc2_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 2 */ tc3_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 3 */}red 拥塞控制red { tc0_wred_min 48 40 32 /* Minimum threshold for traffic class 0 queue (min_th) in number of packets */ tc0_wred_max 64 64 64 /* Maximum threshold for traffic class 0 queue (max_th) in number of packets */ tc0_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 0 queue (maxp = 1 / maxp_inv) */ tc0_wred_weight 9 9 9 /* Traffic Class 0 queue weight */ tc1_wred_min 48 40 32 /* Minimum threshold for traffic class 1 queue (min_th) in number of packets */ tc1_wred_max 64 64 64 /* Maximum threshold for traffic class 1 queue (max_th) in number of packets */ tc1_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 1 queue (maxp = 1 / maxp_inv) */ tc1_wred_weight 9 9 9 /* Traffic Class 1 queue weight */ tc2_wred_min 48 40 32 /* Minimum threshold for traffic class 2 queue (min_th) in number of packets */ tc2_wred_max 64 64 64 /* Maximum threshold for traffic class 2 queue (max_th) in number of packets */ tc2_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 2 queue (maxp = 1 / maxp_inv) */ tc2_wred_weight 9 9 9 /* Traffic Class 2 queue weight */ tc3_wred_min 48 40 32 /* Minimum threshold for traffic class 3 queue (min_th) in number of packets */ tc3_wred_max 64 64 64 /* Maximum threshold for traffic class 3 queue (max_th) in number of packets */ tc3_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 3 queue (maxp = 1 / maxp_inv) */ tc3_wred_weight 9 9 9 /* Traffic Class 3 queue weight */}port,subport,pipe,tc,queue这些配置某些参数也可以使用命令行或者api进行配置。 ...

July 2, 2019 · 8 min · jiezi

flutter-深入通信接收端

环境: flutter sdk v1.5.4-hotfix.1@stable 对应 flutter engine: 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f 前言通过PlatformChannel为平台层作为接收端的例子我们已经了解到DartMessenger通过响应接口handleMessageFromDart来把Dart层的消息/操作发送到平台层,而这个方法是PlatformMessageHandler这个接口对象的,持有接口实例的对象正是FlutterJNI。 作为被动调用的一方,平台层等待消息接收,并不知道消息的来源和用途,所以我们只需要按图索骥,找出调用方,就可追踪接收过程的完整流程。 追溯容易看到FlutterJN.handlePlatformMessage调用了handleMessageFromDart,此函数被标记成@SuppressWarnings("unused"),很大可能与C++层有关了,搜索方法名称果然在`中找到"handlePlatformMessage", 函数签名是"(Ljava/lang/String;[BI)V"正是些方法,方法对象被全局变量g_handle_platform_message_method持有,又被FlutterViewHandlePlatformMessage`引用, 至此又进入到C++层。 这里HandlePlatformMessage这个名称实在太让人产生误解,感觉像是C++层在处理平台层发来的消息,然而实际却是传递Dart层的消息到平台,虽然handlePlatformXXX这种风格都表示处理Dart层的消息,并且保持的很好,但还是没有receiveXXX来的简单直观明了。 为便于理解以下是被调用序列 DartMessenger.handleMessageFromDart => PlatformMessageHandler FlutterJNI.handlePlatformMessage => g_handle_platform_message_method FlutterViewHandlePlatformMessage PlatformViewAndroid::HandlePlatformMessage <= PlatformView::HandlePlatformMessage ...Shell::OnEngineHandlePlatformMessage <= PlatformView::Delegate::OnEngineHandlePlatformMessage Engine::HandlePlatformMessage <= RuntimeDelegate::HandlePlatformMessage RuntimeController::HandlePlatformMessage <= WindowClient::HandlePlatformMessage ::SendPlatformMessage ...tonic::DartCallStatic(::_SendPlatformMessage ...Window::RegisterNatives这与发送端的序列层次完全一样,从上到下分别是Shell -> PlatformView -> Engine -> RuntimeController -> Window。 可知FlutterViewHandlePlatformMessage是C++调用的终点,全局变量g_handle_platform_message_method其实是平台java方法,所以需要知道java方法何时与C++方法关联起来的, 即g_handle_platform_message_method何时被设置的:以下是被调用序列 g_handle_platform_message_method = env->GetMethodID(,"handlePlatformMessage",) ::RegisterApi PlatformViewAndroid::Register JNI_OnLoad System.loadLibrary("flutter") (library_loader.cc:23) FlutterMain.startInitialization (FlutterMain.java:161) FlutterApplication.onCreate (FlutterApplication.java:22)JNI_OnLoad被声明在了链接器脚本(android_exports.lst)中,表示被加载时执行的操作。 结语结合前2篇的调用细节分析(精确到函数),及一些关键类的创建时机做一个简明flutter通道数据通信类图如下:左边是java类,右边是C++类

July 2, 2019 · 1 min · jiezi

内存泄漏的分析

一.问题描述当一个进程的内存异常大时,可能发生了内存泄漏,也可能是内存中相关队列等数据结构长度未作限制。 二.解决思路如果问题很容易出现,最好是使用umdh工具来进行判断,此工具的使用方法在其帮助手册中有详细介绍,此处不再赘述。本文中说的是另外一处思路。众所周知,进程中分为代码空间,数据空间,栈,堆等。除了堆之外,其他部分都是基本固定和有上限限制的。堆的空间大小对于64位的进程可以说基本不受限的,因此我们的思路是查看堆中的内容。 三.解决方法使用windbg attach到进程后,依次执行 0:003> !heap -sNtGlobalFlag enables following debugging aids for new heaps: stack back tracesLFH Key : 0x0000008f4b2ee335Termination on corruption : ENABLED Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast (k) (k) (k) (k) length blocks cont. heap -------------------------------------------------------------------------------------0000000002460000 08000002 1024 432 1024 1 6 1 0 0 LFH0000000000010000 08008000 64 8 64 5 1 1 0 0 0000000000020000 08008000 64 64 64 61 1 1 0 0 00000000001a0000 08001002 1088 256 1088 3 1 2 0 0 LFH00000000040a0000 08001002 512 28 512 0 2 1 0 0 0000000004080000 08001002 1088 332 1088 6 5 2 0 0 LFH-------------------------------------------------------------------------------------0:003> !heap -stat -h 0000000004080000 heap @ 0000000004080000group-by: TOTSIZE max-display: 20 size #blocks total ( %) (percent of total busy bytes) 6608 1 - 6608 (36.24) 4000 1 - 4000 (22.73) 1000 2 - 2000 (11.37) 1001 1 - 1001 (5.68) b00 1 - b00 (3.91) 2c8 3 - 858 (2.96) 200 4 - 800 (2.84) 401 1 - 401 (1.42) 3ff 1 - 3ff (1.42) 30a 1 - 30a (1.08) 178 2 - 2f0 (1.04) 28 11 - 2a8 (0.94) 38 a - 230 (0.78) 220 1 - 220 (0.75) 100 2 - 200 (0.71) 58 5 - 1b8 (0.61) 160 1 - 160 (0.49) 130 1 - 130 (0.42) 30 6 - 120 (0.40) 48 3 - d8 (0.30)0:003> !heap -flt s 401(这个是随机抽选的,当然我写的示例中正好是泄漏了这么大) _HEAP @ 2460000 _HEAP @ 10000 _HEAP @ 20000 _HEAP @ 1a0000 _HEAP @ 40a0000 _HEAP @ 4080000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0000000003f907e0 0043 0000 [00] 0000000003f90810 00401 - (busy)0:003> !heap -p -a 0000000003f90810 address 0000000003f90810 found in _HEAP @ 4080000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0000000003f907e0 0043 0000 [00] 0000000003f90810 00401 - (busy) 7700cc0d ntdll! ?? ::FNODOBFM::`string'+0x000000000001913b 71048d17 MSVCR100!malloc+0x000000000000005b 71048ddb MSVCR100!operator new+0x000000000000001f 13fa423d7 test!test1+0x0000000000000017 13fa47ed4 test!boost::detail::thread_data<long (__cdecl*)(void)>::run+0x0000000000000014 13fa52e43 test!boost::`anonymous namespace'::thread_start_function+0x0000000000000043 71001d9f MSVCR100!_callthreadstartex+0x0000000000000017 71001e3b MSVCR100!_threadstartex+0x000000000000007f 76e6652d kernel32!BaseThreadInitThunk+0x000000000000000d 76f9c521 ntdll!RtlUserThreadStart+0x000000000000001d这样就可以定位到申请内存的代码行数(需要注意的是,设置符号文件,并且执行 gflags.exe /i *.exe +ust) ...

July 1, 2019 · 2 min · jiezi

编程语言之问何时该借用何时该创造

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Oy... 6 月 22 日,Python 之父 Guido 发了一条推特,说了 Python 的一则历史故事,他说 elif 是从 C 语言中偷过来的: elif 是“else if”的简写,用于条件判断。当只有两个分支时,我们会写成“if...else...”,当出现更多分支时,我们会写成如下格式: if 判断条件1: 做事情1elif 判断条件2: 做事情2else: 做其它事简写而成的 elif 不仅是减少了几个字符,而且由于单一而清晰的用途,它还不会给我们带来理解或使用上的困惑。 但是,简写法并不是主流,完整写法才是主流,C 语言中就是采用完整的写法: if(判断条件1){ 做事情1}else if(判断条件2){ 做事情2}else { 做其它事}没错,C 语言使用的是全拼写法,但是在它的预处理/预编译语句中,还有一个 elif 指令,Guido 所说的“偷”,就是从这来的: #if 常量表达式1// 编译1#elif 常量表达式2// 编译2#else// 编译3#endifPython 没有预编译,所以所谓的偷,跟预编译没有关系,只是在对比两种写法后,借用了更简洁的写法而已。 为什么 C 语言不把两种写法统一起来呢?这我不得而知了,而 Guido 在两种写法中,选择了后一种非主流却更好用的写法。我想对他说,你“偷”得好啊! 实际上,留言区里的人也有同感,纷纷表示:不介意、很 okay、非常喜欢,还有人说“不是偷,而是收获(harvested)”、“不是偷,而是把它提升了一些高度”…… 前不久,我写了一篇《聊聊 print 的前世今生》,print 这个词就是从 C 语言中借用来的。除此之外,如果有人仔细比较这两种语言的关键字和习惯命名,肯定会发现不少相同的内容。 编程语言间有一些共享的元素,这很常见,创造一门语言并不意味着要原创每一个词句,毕竟大部分思想是共通的,作为基础设施的词语更是如此。 那么,我突然好奇了:创造一门编程语言时,什么时候该借用,什么时候该创造呢? 这个问题看起来可能没啥意义,因为终其一生,我们多数人也不大可能会参与创造一门编程语言。 但我觉得它还是极有意义的,首先,提问精神值得肯定,其次,它还提供了一种溯源、甄别、遴选、创造的体系性视角,我认为这是求知的正确思维方式。 带着这个疑惑,我特别想要考察的是 Python 的 for 循环。 ...

June 30, 2019 · 3 min · jiezi

VPP-vppmain线程时间轮用法

vpp_main线程通过在文件vppsrcvlibmain.c中包含#include <vppinfra/tw_timer_1t_3w_1024sl_ov.h>指定了使用1t_3w_1024sl_ov类型时间轮构建定时器。 数据结构[ ] vlib_node_main_t/* 线程与定时器相关成员 */typedef struct{ ...... /* Timing wheel for scheduling time-based node dispatch. */ /* 基于时间的调度策略,依赖时间轮构建,使用的是1t_3w_1024sl_ov类型时间轮 */ void *timing_wheel; /* vpp_main线程注册的定时器的私有数据保存向量,索引来自data_from_advancing_timing_wheel */ vlib_signal_timed_event_data_t *signal_timed_event_data_pool; /* Opaque data vector added via timing_wheel_advance. */ /* 已经发生的定时事件的用户句柄保存内存 */ u32 *data_from_advancing_timing_wheel; /* CPU time of next process to be ready on timing wheel. */ f64 time_next_process_ready;/* 该值目前没有使用,根据意思是最近一个可能超时的时间 */ ......} vlib_node_main_t;[ ] vlib_signal_timed_event_data_t/* 定时器私有数据 */typedef struct{ u16 n_data_elts;/* 数据元素个数 */ u16 n_data_elt_bytes;/* 每个元素字节数 */ /* n_data_elts * n_data_elt_bytes 即下面union存放的数据大小*/ u32 n_data_bytes; /* Process node & event type to be used to signal event. */ /* 该事件所属协程 */ u32 process_node_index; u32 event_type_index;/* 事件类型索引,用于通知协程的事件通知机制什么事情发生了 */ /* 私有数据存放位置,数据较少时,放在静态数组inline_event_data中, ** 否则放在动态内存event_data_as_vector中 */ union { u8 inline_event_data[64 - 3 * sizeof (u32) - 2 * sizeof (u16)]; /* Vector of event data used only when data does not fit inline. */ u8 *event_data_as_vector; };}vlib_signal_timed_event_data_t;代码流程初始化时间轮/* Main function. */intvlib_main (vlib_main_t * volatile vm, unformat_input_t * input){ ...... /* 分配时间轮描述控制块 */ nm->timing_wheel = clib_mem_alloc_aligned (sizeof (TWT (tw_timer_wheel)), CLIB_CACHE_LINE_BYTES); /* 确保可以存放10个事件 */ vec_validate (nm->data_from_advancing_timing_wheel, 10); _vec_len (nm->data_from_advancing_timing_wheel) = 0; /* Create the process timing wheel */ /* 创建时间轮 */ TW (tw_timer_wheel_init) ((TWT (tw_timer_wheel) *) nm->timing_wheel, 0 /* no callback */ , 10e-6 /* timer period 10us */ , ~0 /* max expirations per call */ ); ...... return 0;}定时器用户ID构成原则定时器的用户ID由两部分构成,私有数据索引和类型标志位,最低位为类型标志位: ...

June 28, 2019 · 4 min · jiezi

VPP-API机制分析上

VPP除了使用命令行进行配置外,还可以使用API进行配置。VPP不仅支持c语言的API,还支持python,java,lua等高级语言的API,非常适合自动化部署。 API简单使用案例VPP提供了一个VAT客户端程序,用于进行简单的API测试。可执行文件位于: debug版本:vpp源码路径/build-root/build-vpp_debug-native/vpp/bin/vpp_api_testrelease版本:vpp源码路径/build-root/build-vpp-native/vpp/bin/vpp_api_test启动vpp与vat程序sudo systemctl start vppcd vpp源码路径/build-root/build-vpp-native/vpp/bin/sudo vpp_api_testload_one_plugin:68: Loaded plugin: /usr/lib/x86_64-linux-gnu/vpp_api_test_plugins/memif_test_plugin.so......load_one_plugin:68: Loaded plugin: /usr/lib/x86_64-linux-gnu/vpp_api_test_plugins/mactime_test_plugin.sovat# vat# 简单使用[ ] help命令列出所有的命令vat# vat# helpHelp is available for the following:acl_add_replace......[ ] help + 具体命令 列出指定命令的帮助信息vat# help acl_delusage: acl_del <acl-idx>vat# [ ] quit 或者 q 命令退出vatvat# quitadmin@ubuntu:~/vpp/build-root/build-vpp-native/vpp/bin$ [ ] show_version 显示VPP版本vat# show_version program: vpe version: 19.08-rc0~65-g3b62e29c3 build date: Sat Apr 20 13:38:27 CST 2019build directory: /home/admin/vppvat# VPP API交互过程vpp的api是CS模型。VPP是服务器端,监听客户端连接请求,处理客户端发送的api请求,返回应答。如下图所示: 如上图所示,客户端VAT向VPP请求连接,VPP返回新连接。连接建立之后,VAT发送API请求,VPP处理请求返回应答。VPP支持unix套接字或者inet套接字。VPP支持多个客户端同时进行请求。VPP支持使用共享内存进行数据交换,当客户端和服务器端都在同一个节点上的时候,可以选择使用共享内存进行message交换(3,和4交互过程可以选择共享内存交换也可以选择套接字交换),提高通信速率。 VPP API编写说明VPP API命名规则vpp一共支持三种类型的API: ...

June 28, 2019 · 4 min · jiezi

VPPMAIN线程与VPPWORK线程之间的交互机制

VPP是多线程模型,共享地址空间,最快的通信机制就是直接访问彼此之间的数据。VPP自己实现了一套简单的线程安全机制,用于保护临界区。 VPP多线程之间同步采用的是类似于带信号和超时机制的自旋锁,主要有check、sync、release操作。总体上类似于pthread_cond_timedwait中的互斥体改成自旋锁所提供的功能,超过BARRIER_SYNC_TIMEOUT时间的话说明可能发生死锁故直接abort。其中: [ ] vlib_worker_thread_barrier_check类似于pthread_cond_wait操作,等待vlib_worker_threads->wait_at_barrier条件。[ ] vlib_worker_thread_barrier_sync类似于spin_lock操作,置位vlib_worker_threads->workers_at_barrier。只有主线程可以调用该函数,通知其它线程准备同步。[ ] vlib_worker_thread_barrier_release类似于spin_unlock操作,复位vlib_worker_threads->workers_at_barrier。只有主线程可以调用该函数,通知其它线程同步结束。vpp_main线程访问vpp_worker线程的数据的保护机制数据结构vlib_worker_thread_ttypedef struct{ ...... volatile u32 *wait_at_barrier;/* 通知work线程开始等待sync标志,main线程开启sync,设置为1,结束设置为0 */ volatile u32 *workers_at_barrier;/* 统计已经进入sync的worker线程的个数,由worker线程加1 */ i64 recursion_level;/* 当前递归深度 */ u64 barrier_sync_count;/* 当前多少个线程已经同步了,当该值等于work线程数时,开始执行临界区操作 */ u8 barrier_elog_enabled; const char *barrier_caller;/* 开启本次sync的函数名字 */ const char *barrier_context;} vlib_worker_thread_t;vlib_main_ttypedef struct vlib_main_t{ ...... /* debugging */ volatile int parked_at_barrier; /* * Barrier epoch - Set to current time, each time barrier_sync or * barrier_release is called with zero recursion. * 用于计算sync持续时间 */ f64 barrier_epoch; /* Earliest barrier can be closed again */ /* 当前时间小于barrier_no_close_before,不允许启动sync */ f64 barrier_no_close_before; ......} vlib_main_t;相关函数分析[ ] vlib_worker_thread_barrier_syncmain线程调用该函数通知worker线程开始sync,等待所有worker线程进入sync状态后,执行临界操作。 ...

June 28, 2019 · 9 min · jiezi

转载史上最简单的平衡树无旋Treap

【转载】史上最简单的平衡树——无旋Treap作者:fzszkl博客地址:https://ac.nowcoder.com/discu...使用此PDF文件时请保留上述信息!谢谢合作!觉得文章不错请点击链接为博客点赞!高能预警:所有示例代码都是数组版的,欢迎copy!前置知识:线段树!请确保你完全理解最基础的线段树和LazyTag(区间加法和区间求和). 一、简介无旋Treap,又称fhq_treap,是范浩强大佬发明的一种强力数据结构. 总的来说,它可以支持一切Treap和Splay等平衡树的操作,支持可持久化(但是这篇博客不会讲),常数远小于Splay,但是处理LCT问题略比Splay逊色,以至于我到现在还不会. 对于初学者来说,它比Splay好学,比Treap好用,实在不失为一个性价比极高的数据结构. 二、详解首先让我们来复习一下平衡树们的祖宗——二叉搜索树的性质: 递归定义,空树是一棵二叉搜索树,二叉搜索树的左子树的最大点权小等于根的点权小等于右子树的最小点权,二叉搜索树的左右子树也是二叉搜索树. 二叉搜索树的插入,删除,搜索等操作都容易被极端数据卡满复杂度,这时我们就需要各种神奇操作来确保它的树高期望大小为log级别.我们直接看无旋Treap是如何操作的(因为其他几种我不大会): 无旋Treap有两种基本操作: (1)分裂Split将树分为两棵树,其中树A的最大点权小等于树B的最小点权.因为树高期望为log,所以单次操作的复杂度期望为log. (2)合并Merge将两棵树合为一棵树,其中树A的最大点权小等于树B的最小点权.为了保证树高期望为log,我们要想办法随机合并. 没了(卧槽你啥都没说啊). 好吧,为了博客过审,还是再来仔细讲一下. 以下实例代码中,0代表空节点,Pushdown代表下传标记,Pushup代表向上更新. 先来看一眼Split: void Split( int Nod , int Siz , int &A , int &B ) { if( Nod == 0 ) return (void)( A = B = 0 ) ; Pushdown( Nod ) ; if( Siz <= Size[Ls[Nod]] ) B = Nod , Split( Ls[Nod] , Siz , A , Ls[Nod] ) ; else A = Nod , Split( Rs[Nod] , Siz - Size[Ls[Nod]] - 1 , Rs[Nod] , B ) ; Pushup( Nod ) ;}Nod为当前准备拆开的节点,A和B为左右子树根节点的指针,Siz为拆分出的左子树的大小. ...

June 28, 2019 · 1 min · jiezi

编程语言中的-DUCK-TYPING

如果一只动物走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只动物就可以被称为鸭子。许多编程语言都支持 Duck Typing ,通常 Duck Typing 是动态编程语言用来实现多态的一种方式。 在理解 Duck Typing 前,先看一张图片,这是曾经一度很火的大黄鸭 先问一个比较考三观的问题:图片中的大黄鸭,它是不是一只鸭子呢? 这个问题,得看你从哪个角度去看,如果从人们常识的认知中的角度去看,它显然不是一只鸭子,因为它连最基本的生命都没有。 但是从 Duck Typing 的角度来看,它就是一只鸭子! Duck Typing 的原话是,走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么它就是一只鸭子。 这个原话是可以灵活理解的,就看我们怎么定义鸭子的行为,我们可以说,能浮在水上游的,黄色的,可爱的就是鸭子,那么,图片中的大黄鸭,它就是一只鸭子! 这就是所谓的 Duck Typing,它只关心事物的外部行为而非内部结构。它并不关心你这只鸭子是长肉的还是充气的。 在编程中,也常常用这种方式来描述事物。那么不同的编程语言中,Duck Typing 是怎么样实现的呢? 1. Python 中的 Duck Typing 先看一个函数: def download(fetcher): return fetcher.get("http://xxx");有一个 download 函数,传过来一个 fetcher 参数,fetcher 是可以获取一个 url 链接的资源的。这个 fetcher 就是一个 Duck Typing 的对象,使用者约定好这个 fetcher 会有一个 get 函数就可以了。显然这个 download 函数会有以下问题: 运行时才知道传入的 fetcher 有没有 get 函数。那么站在 download 函数的使用者的角度上看,我怎么知道需要给 fetcher 实现 get 方法呢?我不可能去阅读 download 函数的代码,实际情况中,可能 download 函数的代码很长,可能 fetcher 不只要实现 get 方法,还有其它方法需要实现。通常这种情况需要通过加注释来说明。 ...

June 28, 2019 · 2 min · jiezi

Mac环境安装OpenCVVScode调试C程序

背景最近在研究图像识别相关代码——OpenCV,为了便于调试,就要在Mac上搭建一个调试c++程序的调试环境。我这跑通了,分享给大家。 环境Mac OS 10.14.5xcode-select v2354Visual Studio code 1.35.1OpenCV 3.4.5Clang 1001.0.46.4CMake 3.14.5步骤说明编译环境准备安装OpenCV安装VScode 插件VScode 项目配置OpenCV HelloWorld 环境准备Mac 自带有C/C++环境,就不再赘述。 Xcode Command Line Tools是必须的,可通过一下命令行触发安装程序,或者下载安装。 命令安装 xcode-select sudo xcode-select --install下载 xcode-select 安装Apple Developer Download 选择下载文件注意这里提示的版本号!命令行安装速度很慢,且失败率很高,建议通过下载dmg文件安装CMake 准备 编译OpenCV时要用到cmake。同样可以通过homebrew或者下载安装。下载方式安装的CMake,cmake可执行程序在/Applications/CMake.app/Contents/bin/cmake. 如果你用的homebrew方式安装opencv那么CMake就不是必须的.pkg-config 安装编译引用有opencv库的c/c++代码需要附带很多参数来告知include path,libs等,用pkg-config就能精简编译参数。 brew install pkg-config安装OpenCV有两种方式安装。 brew install## 一个命令安装好opencv3brew install opencv3用homebrew安装很方便,就是安装时间很长(我用了一个大白天)。额外还会安装python-opencv。编译安装点击下载源代码。这里我选择的是Sources 3.4.5 解压后,进入目录 cd <opencv 解压后目录>mkdir releasecd releasecmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local makesudo make install这里创建的release目录会放cmake命令产生的成果。CMAKE_INSTALL_PREFIX配置的目录会存make命令产生的成果。 到这里如果都没报错就算安装成功了。 一般都不会出问题,在虚拟环境不一定能成功。参考自 编译安装opencv 写个C++ Hello World先写一个只引用了标准库的C++代码试试看。 /// ./main.cpp#include <stdio.h>#include <iostream>int main(int argc, const char * argv[]) { std::cout << "Hello, World!\n"; return 0;}结果如下。没问题就继续尝试在代码引入OpenCV库。 ...

June 28, 2019 · 3 min · jiezi

GTKGTK介绍

最近在用GTK写一些工具,所以写一个基础教程系列,总结学习成果。 简介GTK是一款开源的、面向多平台的GUI工具箱,其英文全称为GIMP Toolkit。最初是Peter Mattis 和 Spencer Kimball 为GNU Image Manipulation Program (GIMP)编写,用来替代付费的Motif。在后续的发展中,它已经成为通用的GUI库,应用于越来越多的程序,Linux平台的图形应用程序的半壁江山都是使用GTK编写的。 GTK的英文全称GTK的英文全称,让我想到了GCC。GCC最初定位于GNU C Compiler,但随着支持的编译器越来越多,它的定义已经包不住编译器的多样性,所以现在改成了GNU Compiler Collection。这样看来,是不是GTK的名字也得换换了,毕竟现有的名字很局限。 GTK的语言绑定GTK是使用C语言写的,所以其原生API都是面向C的,同时GTK的一大特点是,在C语言层面实现了面向对象的特性。如果你是用C++语言作为开发语言、调用GTK的C接口的话,使用会稍显繁琐,这是语言层面的差异,跟框架关系不大。正是为了避免不同语言调用C的繁琐,GTK提供了多语言的绑定,为不同的语言提供同等抽象级别的语言调用,这样C++程序员就可以直接调用C++的语言绑定,使用方式友好。 GTK的授权GTK是完全免费的,而且基于LGPL协议,这可以保证私有软件通过链接使用GTK可以不把软件源代码开放,对商业应用较友好,这跟GPL协议是不一样的。也正是LGPL协议,使得早些年Gnome(基于GTK编写)风头胜过KDE(基于QT编写)。 GTK的跨平台GTK是跨平台的,支持Unix类的系统、Windows,甚至手机平台。之前我专门有篇文章介绍了在Windows下的环境搭建,C语言的开发环境的搭建还是非常容易的。 GTK vs GTK+关于名字。从网上的资料上,你可以看到GTK+的字眼,这个加号官方是有描述的: The "plus" was added to "GTK" once it was moved out of the GIMP sources tree and the project gained utilities like GLib and the GTK type system, in order to distinguish it from the previous, in-tree version.大意是:GTK从GIMP独立出来之后,加入了一些GLib和GTK类型系统的支持,为了和GIMP代码树中的版本区分,所以带上加号,这一区分就是好多年,给广大的人民群众带来了不小的认知麻烦。在今年,官方终于决定把加号去掉,以后直接叫GTK。 GTK的发布版本关于版本。现在开源的大环境是采用刷版本的方式,像火狐浏览器,谷歌浏览器版本蹭蹭的涨。之前GTK一直采用小步慢跑的版本方式,估计也快要刷版本了,下面引用一篇旧闻: GNOME开发者在多伦多举办的GTK会议上讨论了新的Gtk发布方案,针对Gtk 3.x系列中的问题,开发者提议加快大版本的发布速度:每两年发布一个大版本如 Gtk 4、Gtk 5和Gtk 6,每6个月发布一个与旧版本不兼容的小版本,如Gtk 4.2、Gtk 4.4和Gtk 4.6。这项计划意味着Gtk 4.0不是我们将称之为Gtk 4的最终稳定API。新的大版本能与旧的版本并行安装,如Gtk 4 和Gtk 3能安装在一个系统中,但不兼容的小版本不能,它们使用了相同的pkg-config名字和头文件目录。每一个连续小版本的API将逐渐成熟稳定,也就是说Gtk 4.6发布时API将最终稳定下来,Gtk 4.6可以称之为 Gtk 4了。使用Gtk的开发者可以选择跟随稳定的版本。为什么选择GTK免费这条最实在。大的组织,比如公司,也是很注重成本的;小的个人,财务的承受能力也是有限的,这是GTK的诞生的原因。而且,很多软件授权真的不便宜。 ...

June 27, 2019 · 1 min · jiezi

sonic容器swss启动过程

sonic容器swss启动过程sonic业务进程都是运行在容器中的,那容器启动后是如何启动它的服务呢。 要分析这个问题,首先要搞清楚容器构建过程。我们以docker-orchagent容器为例进行分析。 Dockerfile文件sonic中的Dockerfile由Dockerfile.j2文件生成。 docker-orchagent/Dockerfile.j2 FROM docker-config-engineARG docker_container_nameRUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name\/%syslogtag%/;" /etc/rsyslog.conf## Make apt-get non-interactiveENV DEBIAN_FRONTEND=noninteractiveRUN apt-get updateRUN apt-get install -f -y ifupdown arping libdbus-1-3 libdaemon0 libjansson4## Install redis-tools dependencies## TODO: implicitly install dependenciesRUN apt-get -y install libjemalloc1COPY \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor -%}debs/RUN dpkg -i \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor %}## Clean upRUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -yRUN rm -rf /debsCOPY ["files/arp_update", "/usr/bin"]COPY ["enable_counters.py", "/usr/bin"]COPY ["start.sh", "orchagent.sh", "swssconfig.sh", "/usr/bin/"]COPY ["supervisord.conf", "/etc/supervisor/conf.d/"]## Copy all Jinja2 template files into the templates folderCOPY ["*.j2", "/usr/share/sonic/templates/"]#程序的入口点ENTRYPOINT ["/usr/bin/supervisord"]从上面的配置来看,容器启动后制定的程序为:/usr/bin/supervisord ...

June 27, 2019 · 5 min · jiezi

sonic容器构建

sonic容器构建sonic中大量的组件运行在docker容器中,用于隔离彼此的运行环境,从而解决相互之间的互斥问题。下面我们分析一下sonic中各个容器的构建过程。 Dockerfile文件的生成过程sonic中的容器Dockerfile文件是通过jinjia2模板文件生成的,使用j2命令。 sonic在编译过程中首先会构建一个叫sonic-slave的容易,该容器用来做编译容器,后续所有的编译过程都是在该容器中进行的。 编译容器生成Dockerfile编译容器使用slave.mk作为makefile,其中有这样一段代码将Dockerfile.j2转换成Dockerfile # Targets for building docker images$(addprefix $(TARGET_PATH)/, $(SONIC_DOCKER_IMAGES)) : $(TARGET_PATH)/%.gz : .platform docker-start $$(addprefix $(DEBS_PATH)/,$$($$*.gz_DEPENDS)) $$(addprefix $(FILES_PATH)/,$$($$*.gz_FILES)) $$(addprefix $(PYTHON_WHEELS_PATH)/,$$($$*.gz_PYTHON_WHEELS)) $$(addsuffix -load,$$(addprefix $(TARGET_PATH)/,$$($$*.gz_LOAD_DOCKERS))) $$($$*.gz_PATH)/Dockerfile.j2 $(HEADER) # Apply series of patches if exist if [ -f $($*.gz_PATH).patch/series ]; then pushd $($*.gz_PATH) && QUILT_PATCHES=../$(notdir $($*.gz_PATH)).patch quilt push -a; popd; fi mkdir -p $($*.gz_PATH)/debs $(LOG) mkdir -p $($*.gz_PATH)/files $(LOG) mkdir -p $($*.gz_PATH)/python-wheels $(LOG) sudo mount --bind $(DEBS_PATH) $($*.gz_PATH)/debs $(LOG) sudo mount --bind $(FILES_PATH) $($*.gz_PATH)/files $(LOG) sudo mount --bind $(PYTHON_WHEELS_PATH) $($*.gz_PATH)/python-wheels $(LOG) # Export variables for j2. Use path for unique variable names, e.g. docker_orchagent_debs $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_debs=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_DEPENDS),RDEPENDS))\n" | awk '!a[$$0]++')) $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_whls=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_PYTHON_WHEELS)))\n" | awk '!a[$$0]++')) $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_dbgs=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_DBG_PACKAGES)))\n" | awk '!a[$$0]++')) j2 $($*.gz_PATH)/Dockerfile.j2 > $($*.gz_PATH)/Dockerfile #使用环境变量编译Dockfile.j2 docker build --squash --no-cache \ --build-arg http_proxy=$(HTTP_PROXY) \ --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg user=$(USER) \ --build-arg uid=$(UID) \ --build-arg guid=$(GUID) \ --build-arg docker_container_name=$($*.gz_CONTAINER_NAME) \ -t $* $($*.gz_PATH) $(LOG) docker save $* | gzip -c > $@ # Clean up if [ -f $($*.gz_PATH).patch/series ]; then pushd $($*.gz_PATH) && quilt pop -a -f; popd; fi $(FOOTER)根容器:docker-base编译的过程首先构建一个docker-base容器。 ...

June 27, 2019 · 4 min · jiezi

C模板初步

泛型:指在多种数据类型上皆可操作的含义。泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库)。模板的精神:类型参数化话不多说,看例子吧。 一个函数模板// template<typename T> 既可以与函数同行,也可以将函数另起一行来书写// T 即为泛化的类型// T 获取类型的过程,称为模板的实例化// 函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译:// 在声明的地方对模板代码本身进行编译(类型检查),在调用的地方对参数替换后的代码进行编译(代码生成)//泛型的特性:// 1.先实例化,再调用// 2.严格匹配, 不存在隐式转化// 3.尺有所短,寸有所长// 算法 sort 就是支持类型泛化的代表排序算法 include <iostream>using namespace std; template<typename T> void myswap(T &a, T &b){ T t = a;a = b;b = t;} int main(){ long a = 2; long b = 3;myswap<long>(a, b);cout<<a<<b<<endl; //32string m = "aaa";string n= "bbb";myswap<string>(m, n);cout<<m<<n<<endl;//如果自定义类型呢?int x = 1; char y = 2;// myswap(x, y); //严格类型匹配 return 0;} ...

June 27, 2019 · 2 min · jiezi

7天得罪36国Twitter治国的特朗普有多爽

作为一个国家领导人,无论是贸易战,还是签署行政令对伊朗最高领袖实施制裁,这位总统几乎每天都能刷屏全球媒体头条,特朗普留给世界的印象大多都是疯狂的,以至于很多人开始质疑这位 “志比天高,才如宝宝”的总统的精神状态,不少美国人开始觉得这位总统的精神状态似乎不是很稳定,于是乎,6月5日,美国众议院民主党紧急召开会议,决定将对特朗普进行一次精神状态评估:证明他可能不是很适合做总统! 接下来,我们先来说说这次精神状态评估的起因吧~ 1.特朗普如何做到7天得罪36国? 之所以会出现这种强烈要求检测总统精神状态的申请,可能是因为特朗普最近越发的癫狂了。 有媒体报道,特朗普在短短7天的时间里得罪了36个国家!特朗普宣布对36个国家威胁进行制裁或者确定执行制裁! 美国的邻国墨西哥,因为大量拉美移民通过各种途径进入美国,特朗普一怒之下决定讲出口商品税从原本的20%逐渐变成25%,并明确表示,只要墨西哥可以控制非法移民进入美国,就会将这些加征的关税削减掉。 美国威胁印度,如果购买俄罗斯的S-400防空系统,就会受到美国的经济制裁,印度无奈妥协,取消了购买计划。 美国威胁土耳其,同样是因为土耳其在向俄罗斯购买S-400防空系统。 美国威胁欧盟,如果欧盟执意向伊朗购入石油,将会遭到美国的经济打击。 就更不要说美国制裁中国和俄罗斯了... 在特朗普的疯狂决策下,美国逐渐与全世界为敌…… 说到这,还有一个国家就不得不提了,那就是目前反特朗普情绪最严重的英国,特朗普究竟做了什么天怒人怨的事情,才会让英国民众如此愤慨,这得从特朗普访问英国之前说起。 抵达英国之前,特朗普连发两条Twitter向伦敦市长开炮…… 6月3日,特朗普正式访问英国,和英女王见面的第一时间,特朗普竟然和女王来了一个碰拳!英国网友就怒了,我们的女王跟你握手,你就这样打招呼? 你以为这样就结束了吗,不,后面还有让英国民众更愤怒的,在白金汉宫享用了王室的晚宴后,特朗普在发表讲话时,随手拍了...拍女王的后背!对于尊重礼仪的英国,对于普通民众这都是十分失礼的表现,结果特朗普在全球媒体的面前拍了英女王的后背…… 当得知自己的行为都被CNN给报道了出去之后,特朗普很是愤怒!于是他连发两条Twitter去嘲讽CNN... 特朗普的一系列行为严重刺激了英国民众,英国民众的愤怒已经无法阻挡,这样的特朗普,或许真的如同Lee博士说的那样,精神状态堪忧,不知道这个精神检测会不会顺利进行,特朗普有没有可能因为精神问题被赶下总统宝座?让我们拭目以待~ 2.特朗普任职期间退出了哪些“群聊”? 据统计,到目前为止,特朗普就职以来已经宣布退出了六个“群聊”了。 1.跨太平洋伙伴关系协定(简称TPP) 机构简介:跨太平洋伙伴关系协定,是目前重要的国际多边经济谈判组织,前身是跨太平洋战略经济伙伴关系协定。2016年2月4日,美国、日本、澳大利亚、文莱、加拿大、智利、马来西亚、墨西哥、新西兰、秘鲁、新加坡和越南12个国家在新西兰最大城市奥克兰正式签署了跨太平洋伙伴关系协定(TPP)协议。 组织成员:日本、加拿大、澳大利亚、新加坡、文莱、智利、马来西亚、墨西哥、新西兰、秘鲁、越南,目前由日本主导(相当于群主)。 退出原因:特朗普多次指出,退出TPP对于美国工人来说是件“大好事”,因为TPP会给美国的制造业带来一定的冲击,这也是特朗普就任美国总统后,签署的第一个总统令。 2.《巴黎气候变化协定》(简称《巴黎协定》) 机构简介:是2015年12月12日在巴黎气候变化大会上通过、2016年4月22日在纽约签署的气候变化协定,该协定为2020年后全球应对气候变化行动作出安排。《巴黎协定》主要目标是将本世纪全球平均气温上升幅度控制在2摄氏度以内,并将全球气温上升控制在前工业化时期水平之上1.5摄氏度以内。 组织成员:196个国家,全球197个国家中,美国是唯一一个退出《巴黎协定》的国家。 退出原因:特朗普政府认为,《巴黎协定》将会损害美国经济。《巴黎协定》要求发达国家在2020年之前每年筹集1000亿美元作为“绿色气候基金”,2021年到2025年每年向发展中国家提供1000亿美元援助,特朗普认为美国不应该承担如此多的责任,这对美国不公平。 3.联合国教科文组织(UNESCO) 机构简介:1945年11月1日-16日,二战刚刚结束,在饱经战争苦难的两个国家——法国和英国的推动下,按照他们的设想,这个新的组织应建立“人类智力上和道义上的团结”,从而防止爆发新的世界大战。会议结束时,三十七个国家签署了《组织法》,联合国教育、科学及文化组织从此诞生,该组织旨在通过教育、科学和文化促进各国合作,对世界和平和安全作出贡献,其主要机构包括大会、执行局和秘书处。 组织成员:现有195个成员,全球197个国家,美国、以色列已退出。 退出原因:美国认为该组织存在反对以色列的偏见,需要改革,而且美国欠缴联合国教科文组织的会费(美国已欠会费5.5亿美元)。 4.全球移民协议(Global Compact on Migration) 机构简介:《全球性难民和移民协议》联合国拟定的协议。协议核心内容是,不管是否具备居留资格,参与国需要保护移民的权利,保障移民人口在参加工作方面不受歧视。该协议并不具备法律效力,参与国可在本国的移民政策框架下采取可行措施。 组织成员:162个国家。美国、匈牙利和澳大利亚宣布退出《全球移民协议》。 退出原因:特朗普政府认为《全球移民协议》和美国政府现行的难民政策和移民原则不符。 5.伊朗核协议 伊朗核协议内容:2015年7月20日,联合国安理会一致通过伊核协议,伊朗不得从事5%以上丰度的铀浓缩,停止阿拉克重水反应堆建设,允许更多核查,有关六国将不追加对伊新制裁并松绑部分现有制裁。 组织成员:伊朗与美国、俄罗斯、中国、英国、法国、德国。 退出原因:特朗普称必须采取措施阻止伊朗继续发展其弹道导弹项目和支持恐怖主义。美国总统特朗普指示美国财政部和其他联邦机构重启对伊朗的制裁。 6.联合国人权理事会(United Nations Human Rights Council) 机构简介:联合国人权理事会是联合国大会的下属机构,总部设在瑞士日内瓦。其目标是致力于维护各国人权免于侵害。人权理事会由47个成员组成,成员任期3年,在连续两任后不能连任。 组织成员:由47个成员组成,其中非洲13席,亚洲13席,东欧6席,拉美和加勒比8席,西欧和其他国家7席(包括北美和大洋洲)。 退出原因:联合国人权理事会对以色列“存在偏见”,以及“无法有效保护人权”。 特朗普本人已经扬言要退出WTO了,未来特朗普政府还会退出哪些国际组织,只能说“敬请期待”了…… 3.特朗普有哪些值得学习的地方? ...

June 27, 2019 · 1 min · jiezi

SONIC-VLAN配置流程

SONIC VLAN配置流程sonic vlan配置通过订阅config_db的键空间事件完成vlan配置信息从config_db到内核和硬件。config_db.json格式如下: "VLAN": { "Vlan1000": { "vlanid": "1000" } }, "VLAN_MEMBER": { "Vlan1000|Ethernet16": { "tagging_mode": "untagged" } }, "VLAN_INTERFACE": { "Vlan1000|192.168.0.1/27": {} }, 在swss容器中存在一个vlanmgrd进行用于订阅config_db的vlan键空间,响应vlan配置,解析后将其同步到内核和app_db。由portsorch订阅app_db vlan事件,将其同步到asic-db,最终将其同步到硬件。VLAN_INTERFACE接口信息由intfmgr和intforch进行处理。 vlanmgr该进程运行在swss容器中,用于订阅config_db的vlan键空间,响应vlan配置,解析后将其同步到内核和app_db。 该部分涉及文件: vlanmgrd.cpp vlanmgr.cpp vlanmgr.h class VlanMgr : public Orch{public: VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames); using Orch::doTask;private: ProducerStateTable m_appVlanTableProducer, m_appVlanMemberTableProducer; Table m_cfgVlanTable, m_cfgVlanMemberTable; Table m_statePortTable, m_stateLagTable; Table m_stateVlanTable; std::set<std::string> m_vlans; void doTask(Consumer &consumer);//主任务 void doVlanTask(Consumer &consumer);//处理vlan事件 void doVlanMemberTask(Consumer &consumer);//处理vlan成员事件 void processUntaggedVlanMembers(string vlan, const string &members);//处理vlan内untag成员,暂时没用 bool addHostVlan(int vlan_id);//将vlan同步到host,即创建vlan bool removeHostVlan(int vlan_id);//删除host中的vlan bool setHostVlanAdminState(int vlan_id, const string &admin_status);//设置hostvlan状态 bool setHostVlanMtu(int vlan_id, uint32_t mtu);//设置host vlan接口的mtu //添加成员 bool addHostVlanMember(int vlan_id, const string &port_alias, const string& tagging_mode); bool removeHostVlanMember(int vlan_id, const string &port_alias);//删除host vlan成员 bool isMemberStateOk(const string &alias);//从state db中获取成员端口是否ok bool isVlanStateOk(const string &alias);//从state db中获取vlan是否ok bool isVlanMacOk();//判断该vlan的mac地址是否ok};实现int main(int argc, char **argv){ Logger::linkToDbNative("vlanmgrd"); SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("--- Starting vlanmgrd ---"); try { vector<string> cfg_vlan_tables = {//订阅了两个config db table CFG_VLAN_TABLE_NAME, CFG_VLAN_MEMBER_TABLE_NAME, }; //连接了三个数据库 DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//读和订阅config-db数据 DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//将配置希尔app-db DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//获取vlan端口是否准备好 /* * swss service starts after interfaces-config.service which will have * switch_mac set. * Dynamic switch_mac update is not supported for now. * 获取全局的mac地址作为vlan-if的mac地址 */ Table table(&cfgDb, "DEVICE_METADATA"); std::vector<FieldValueTuple> ovalues; table.get("localhost", ovalues); auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); if ( it == ovalues.end() ) { throw runtime_error("couldn't find MAC address of the device from config DB"); } gMacAddress = MacAddress(it->second); //构造vlanmgr VlanMgr vlanmgr(&cfgDb, &appDb, &stateDb, cfg_vlan_tables); std::vector<Orch *> cfgOrchList = {&vlanmgr}; swss::Select s; for (Orch *o : cfgOrchList) { s.addSelectables(o->getSelectables()); } SWSS_LOG_NOTICE("starting main loop"); while (true) { Selectable *sel; int ret; ret = s.select(&sel, SELECT_TIMEOUT); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); continue; } if (ret == Select::TIMEOUT) { vlanmgr.doTask(); continue; } auto *c = (Executor *)sel; c->execute(); } } catch(const std::exception &e) { SWSS_LOG_ERROR("Runtime error: %s", e.what()); } return -1;}VlanMgr::doTask(Consumer &consumer)//vlanmgr主任务,vlan没有sync进程监听内核的netlink事件,内核与硬件的vlan事件都//由vlanmgr负责,vlanmgr负责在linux内核创建vlan和vlan-member,同时往app-db中写入//vlan事件。同时vlanif的IP地址在intfmgr中进行添加,并同步到内核void VlanMgr::doTask(Consumer &consumer){ SWSS_LOG_ENTER(); //获取事件表格名 string table_name = consumer.getTableName(); if (table_name == CFG_VLAN_TABLE_NAME) { doVlanTask(consumer);//vlan添加创建 } else if (table_name == CFG_VLAN_MEMBER_TABLE_NAME) { doVlanMemberTask(consumer);//vlan成员添加创建 } else { SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str()); throw runtime_error("VlanMgr doTask failure."); }}VlanMgr::doVlanTask(Consumer &consumer)//执行vlan事件void VlanMgr::doVlanTask(Consumer &consumer){ if (!isVlanMacOk())//vlanmac是否准备好了,没有的话等待,这里就是个判断,vlanmac在vlanmgr起来时已经获取了 { SWSS_LOG_DEBUG("VLAN mac not ready, delaying VLAN task"); return; } auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { auto &t = it->second; string key = kfvKey(t); /* Ensure the key starts with "Vlan" otherwise ignore */ if (strncmp(key.c_str(), VLAN_PREFIX, 4)) { SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str()); it = consumer.m_toSync.erase(it); continue; } int vlan_id; vlan_id = stoi(key.substr(4)); string vlan_alias, port_alias; string op = kfvOp(t); if (op == SET_COMMAND) { string admin_status; uint32_t mtu = 0; vector<FieldValueTuple> fvVector; string members; /* Add host VLAN when it has not been created. */ //添加vlan if (m_vlans.find(key) == m_vlans.end()) { addHostVlan(vlan_id); } /* set up host env .... */ //设置host vlan相关信息 for (auto i : kfvFieldsValues(t)) { /* Set vlan admin status 设置管理状态*/ if (fvField(i) == "admin_status") { admin_status = fvValue(i); setHostVlanAdminState(vlan_id, admin_status); fvVector.push_back(i); } /* Set vlan mtu 设置mtu */ else if (fvField(i) == "mtu") { mtu = (uint32_t)stoul(fvValue(i)); /* * TODO: support host VLAN mtu setting. * Host VLAN mtu should be set only after member configured * and VLAN state is not UNKNOWN. */ SWSS_LOG_DEBUG("%s mtu %u: Host VLAN mtu setting to be supported.", key.c_str(), mtu); fvVector.push_back(i); } else if (fvField(i) == "members@") { members = fvValue(i); } } /* fvVector should not be empty */ if (fvVector.empty()) { FieldValueTuple a("admin_status", "up"); fvVector.push_back(a); } //写入app-db m_appVlanTableProducer.set(key, fvVector); m_vlans.insert(key); //写入state-db(6) fvVector.clear(); FieldValueTuple s("state", "ok"); fvVector.push_back(s); m_stateVlanTable.set(key, fvVector); it = consumer.m_toSync.erase(it); /* * Members configured together with VLAN in untagged mode. * This is to be compatible with access VLAN configuration from minigraph. * untag模式配置vlan时,会跟随着vlan一起下发,所以在这里解决vlan成员添加问题。 * tag vlan则在单独的mem表中添加。 */ if (!members.empty()) { processUntaggedVlanMembers(key, members); } } else if (op == DEL_COMMAND)//删除 { if (m_vlans.find(key) != m_vlans.end()) { removeHostVlan(vlan_id);//找到删除 m_vlans.erase(key); m_appVlanTableProducer.del(key);//通知app-db m_stateVlanTable.del(key);//删除状态 } else { SWSS_LOG_ERROR("%s doesn't exist", key.c_str()); } SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); it = consumer.m_toSync.erase(it); } else { SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); it = consumer.m_toSync.erase(it); } }}void VlanMgr::doVlanMemberTask(Consumer &consumer)//处理vlan-member事件void VlanMgr::doVlanMemberTask(Consumer &consumer){ auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { auto &t = it->second; string key = kfvKey(t); /* Ensure the key starts with "Vlan" otherwise ignore */ if (strncmp(key.c_str(), VLAN_PREFIX, 4)) { SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str()); it = consumer.m_toSync.erase(it); continue; } key = key.substr(4); size_t found = key.find(CONFIGDB_KEY_SEPARATOR); int vlan_id; string vlan_alias, port_alias; if (found != string::npos) { vlan_id = stoi(key.substr(0, found)); port_alias = key.substr(found+1); } else { SWSS_LOG_ERROR("Invalid key format. No member port is presented: %s", kfvKey(t).c_str()); it = consumer.m_toSync.erase(it); continue; } //获取vlan名字 vlan_alias = VLAN_PREFIX + to_string(vlan_id); string op = kfvOp(t); // TODO: store port/lag/VLAN data in local data structure and perform more validations. if (op == SET_COMMAND) { /* Don't proceed if member port/lag is not ready yet */ if (!isMemberStateOk(port_alias) || !isVlanStateOk(vlan_alias)) { SWSS_LOG_DEBUG("%s not ready, delaying", kfvKey(t).c_str()); it++; continue; } string tagging_mode = "untagged"; for (auto i : kfvFieldsValues(t)) { if (fvField(i) == "tagging_mode") { tagging_mode = fvValue(i); } } if (tagging_mode != "untagged" && tagging_mode != "tagged" && tagging_mode != "priority_tagged") { SWSS_LOG_ERROR("Wrong tagging_mode '%s' for key: %s", tagging_mode.c_str(), kfvKey(t).c_str()); it = consumer.m_toSync.erase(it); continue; } //添加vlan成员 if (addHostVlanMember(vlan_id, port_alias, tagging_mode)) { key = VLAN_PREFIX + to_string(vlan_id); key += DEFAULT_KEY_SEPARATOR; key += port_alias;//写入app-db数据库 m_appVlanMemberTableProducer.set(key, kfvFieldsValues(t)); } it = consumer.m_toSync.erase(it); } else if (op == DEL_COMMAND) { removeHostVlanMember(vlan_id, port_alias); key = VLAN_PREFIX + to_string(vlan_id); key += DEFAULT_KEY_SEPARATOR; key += port_alias; m_appVlanMemberTableProducer.del(key); SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); it = consumer.m_toSync.erase(it); } else { SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); it = consumer.m_toSync.erase(it); } }}同步app_db数据到asic_dbapp_db的vlan事件由portsorch进行同步,该部分涉及文件: ...

June 27, 2019 · 8 min · jiezi

sonic管理口信息处理流程

sonic管理口信息处理流程管理接口信息配置文件格式管理信息使用MGMT_INTERFACE 表进行配置。对象的key由管理接口名字和IP前缀使用“|”连接而成。属性 gwaddr用于执行默认路由指向管理口,其值为默认网关。属性forced_mgmt_routes 用来强制添加一些路由到default路由表中。 "MGMT_INTERFACE": { "eth0|10.3.100.3/23": { "forced_mgmt_routes": [ "10.0.10.0/29", "10.0.20.5" ], "gwaddr": "10.3.100.1" } }, 管理信息处理流程可以使用config reload 命令重新加载配置文件config_db.json。这样所有配置信息将会被写入config_db(4)。然后会重新启动接口管理服务: Running command: service interfaces-config restart。 通过查看文件interfaces-config.service: admin@switch2:~$ vim /etc/systemd/system/interfaces-config.service[Unit]Description=Update interfaces configurationRequires=database.serviceAfter=database.service [Service]Type=oneshotExecStart=/usr/bin/interfaces-config.sh[Install]WantedBy=multi-user.target可以看出该服务的执行程序是脚本:/usr/bin/interfaces-config.sh 查看脚本/usr/bin/interfaces-config.sh: #!/bin/bashsonic-cfggen -d -t /usr/share/sonic/templates/interfaces.j2 > /etc/network/interfaces[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pidsystemctl restart networkingifdown lo && ifup lo从上面可以看出通过sonic-cfggen命令生成/etc/network/interfaces配置文件,然后重新启动networking即可让管理配置生效。 systemctl restart networking命令会重启网卡,让网卡down掉再up。

June 27, 2019 · 1 min · jiezi

当谈论迭代器时我谈些什么

花下猫语:之前说过,我对于编程语言跟其它学科的融合非常感兴趣,但我还说漏了一点,就是我对于 Python 跟其它编程语言的对比学习,也很感兴趣。所以,我一直希望能聚集一些有其它语言基础的同学,一起讨论共通的语言特性间的话题。不同语言的碰撞,常常能带给人更高维的视角,也能触及到语言的根基,这个过程是极有益的。 这篇文章是群内 樱雨楼 小姐姐的投稿,她是我们学习群里的真·大佬,说到对 Python 的研究以及高阶知识的水平,无人能出其右(群里很多同学都被她实力圈粉啦)。除了 Python,她对 C++、Perl、Go 与 Fortran 等语言都有涉猎,本文主要是对比了 Python 与 C++,来深入谈谈迭代器。话不多说,请看正文。 樱雨楼 | 原创作者 豌豆花下猫 | 编辑润色 本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Be... 0 前言迭代器(Iterator)是 Python 以及其他各种编程语言中的一个非常常见且重要,但又充满着神秘感的概念。无论是 Python 的基础内置函数,还是各类高级话题,都处处可见迭代器的身影。 那么,迭代器究竟是怎样的一个概念?其又为什么会广泛存在于各种编程语言中?本文将基于 C++ 与 Python,深入讨论这一系列问题。 1 什么是迭代器?我们为什么要使用迭代器?什么是迭代器?当我初学 Python 的时候,我将迭代器理解为一种能够放在“for xxx in ...”的“...”位置的东西;后来随着学习的深入,我了解到迭代器就是一种实现了迭代器协议的对象;学习 C++ 时,我了解到迭代器是一种行为和指针类似的对象... 事实上,迭代器是一个伴随着迭代器模式(Iterator Pattern)而生的抽象概念,其目的是分离并统一不同的数据结构访问其中数据的方式,从而使得各种需要访问数据结构的函数,对于不同的数据结构可以保持相同的接口。 在很多讨论 Python 迭代器的书籍与文章中,我看到这样两种观点:1. 迭代器是为了节约数据结构所产生的内存;2. 遍历迭代器效率更高。 这两点论断都是很不准确的:首先,除了某些不定义在数据结构上的迭代器(如文件句柄,itertools 模块的 count、cycle 等无限迭代器等),其他迭代器都定义在某种数据结构上,所以不存在节约内存的优势;其次,由于迭代器是一种高度泛化的实现,其需要在每一次迭代器移动时都做一些额外工作(如 Python 需要不断检测迭代器是否耗尽,并进行异常监测;C++ 的 deque 容器需要对其在堆上用于存储的多段不连续内存进行衔接等),故遍历迭代器的效率一定低于或几乎接近于直接遍历容器,而不太可能高于直接遍历原容器。 综上所述,迭代器存在的意义,不是为了空间换时间,也不是为了时间换空间,而是一种适配器(Adapter)。迭代器的存在,使得我们可以使用同样的 for 语句去遍历各种容器,或是像 C++ 的 algorithm 模块所示的那样,使用同样的接口去处理各种容器。 ...

June 27, 2019 · 2 min · jiezi