OpenCV(Open Source Computer Vision Library) is the world’s biggest computer vision library.
OpenCV is open source, contains over 2500 algorithms, and is operated by the non-profit Open Source Vision Foundation.

OpenCV是一个基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效:由一系列C 函数和少量C++类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV用C++语言编写,它具有C++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS,OpenCV主要倾向于实时视觉应用,并在可用时利用MMX和SSE指令。

阅读全文 »

图像处理(image processing),用计算机对图像进行分析,以达到所需结果的技术。又称影像处理。图像处理一般指数字图像处理。数字图像是指用工业相机、摄像机、扫描仪等设备经过拍摄得到的一个大的二维数组,该数组的元素称为像素,其值称为灰度值。图像处理技术一般包括图像压缩,增强和复原,匹配、描述和识别3个部分。

阅读全文 »

系统编程

系统编程是指编写系统软件,其代码在底层运行,直接跟内核和核心系统库对话。

书名ISBN作者出版社出版日期备注
Linux/UNIX系统编程手册 上下册 9787115328670 [德]Michael Kerrisk 人民邮电出版社 2022年04月 P
Linux系统编程 第2版 9787115346353 [美]Robert Love 人民邮电出版社 2022年03月 P
Linux系统编程 9787111716617 [瑞]Jack-Benny Persson 机械工业出版社 2023年01月
Linux内核编程指南 第三版 9787302092940 [美]拜克/鲍姆 清华大学出版社 2004年11月 P
Linux内核编程 9787115251947 [美]罗德里格斯 人民邮电出版社 2011年05月 P
Linux环境编程:从应用到内核 9787111536109 高峰 机械工业出版社 2016年06月 P

我们知道一个程序可以包含多个进程,每个进程中可以创建多个线程,在线程中又可以创建成千上万甚至更多个协程。进程和线程的创建以及调度需要在内核态和用户态之间切换;而协程的创建和调度都在用户态,不需要和内核态进行交互。所以这就注定创建和维持协程运行所牺牲的性能,要远小于进程和线程。另外,协程都是以一组的形态存在于一个特定的线程内,那么对于数据的共享,不必使用互斥锁或者条件变量,来保证互斥和同步,应用程序性能上也有了很大的提升。这就是我们使用协程的原因。

协程适用于IO密集型,而不适用于计算密集型的程序。对于IO密集型程序,无论是读取socket还是硬盘,这些操作基本上都是阻塞式调用,当协程遇到阻塞时,当前协程显式或者隐式主动放弃控制权,保存当前协程的硬件上下文和栈,然后调度器切换到其他就绪的协程继续执行,而当阻塞IO完成后,调度器获得通知,恢复原来协程的硬件上下文以及栈,再切换回来运行。而对于计算密集型的程序,当前协程除非显式切换协程或者设置定时器,由定时器主动引起切换,否则通常不会主动放弃控制权,其他协程可能会一直等待调度,得不到运行。

一组协程运行在一个线程内,它们是串行运行的,而非并行,即是运行在一个CPU核上,那么协程就无法利用多核CPU资源。如果我们既想使用协程,又想利用多核CPU,一般我们就采用”多进程+协程“的方式。

目前网上有很多协程的实现例子,本文主要分析云风的协程库,来探究协程的实现原理。大家也可以直接看协程库的注释版

阅读全文 »

信号是一种软件中断。常驻程序尤其需要注意处理这些信号,如果没有处理,同时也没有了解信号的默认动作,进程可能会莫名其妙的退出或者core。信号早已有之,但在老的操作系统中,可能会出现信号丢失。4.3BSD和SVR3之后增加了可靠信号机制,我们可以放心使用信号机制。本文根据阅读Nginx代码,参考其信号处理机制,总结了信号使用方法和注意事项。

阅读全文 »

关于pthread条件变量

1
$ man pthread_cond_init | col -b > pthread_cond.man

得到manual中的描述:

A condition (short for ‘’condition variable’’) is a synchronization device that allows threads to suspend execution and relinquish the processors until some predicate on shared data is satisfied. The basic operations on conditions are: signal the condition(when the predicate becomes true), and wait for the condition, suspending the thread execution until another thread signals the condition.

条件变量是同步线程的一种机制,它允许线程挂起,让出处理器等待其他线程向它发送信号,该线程收到该信号后被唤醒继续执行程序。对条件变量基本的操作就是:a)向条件变量发送信号,唤醒等待的线程;b)等待条件变量并挂起直至其他线程向该条件变量发送信号。为了防止竞争,条件变量总是和一个互斥锁同时使用。

阅读全文 »

关于Thrift和OpenSSL的安全通信,上篇我们描述了数字证书的生成方法,本文在此基础上编写单向验证的测试代码。

  • 01#编译OpenSSL库;
  • 02#编译Boost库;
  • 03#编译zlib库;
  • 04#编译libevent库;
  • 05#编译Thrift库;
  • 06#生成客户端和服务端通信所用的数字证书;
  • 07#编写基于Linux系统的测试代码(单向验证:客户端验证服务端);
  • 08#编写基于Windows系统的测试代码(单向验证:客户端验证服务端)
  • 09#编写基于Linux系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 10#编写基于Windows系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 11#自定义数字证书的验证策略;

阅读全文 »

关于Thrift和OpenSSL的安全通信,上篇我们描述了zlib的编译方法,本文编译libevent库。

  • 01#编译OpenSSL库;
  • 02#编译Boost库;
  • 03#编译zlib库;
  • 04#编译libevent库;
  • 05#编译Thrift库;
  • 06#生成客户端和服务端通信所用的数字证书
  • 07#编写基于Linux系统的测试代码(单向验证:客户端验证服务端);
  • 08#编写基于Windows系统的测试代码(单向验证:客户端验证服务端);
  • 09#编写基于Linux系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 10#编写基于Windows系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 11#自定义数字证书的验证策略;

我们知道x509数字证书有V1和V3版本,对于我们来说采用V1版本即可,考虑到证书生成的便捷性,推荐使用如下的第二种方法生成我们需要的证书。

阅读全文 »

关于Thrift和OpenSSL的安全通信,上篇我们描述了zlib的编译方法,本文编译libevent库。

  • 01#编译OpenSSL库;
  • 02#编译Boost库;
  • 03#编译zlib库;
  • 04#编译libevent库
  • 05#编译Thrift库;
  • 06#生成客户端和服务端通信所用的数字证书;
  • 07#编写基于Linux系统的测试代码(单向验证:客户端验证服务端);
  • 08#编写基于Windows系统的测试代码(单向验证:客户端验证服务端);
  • 09#编写基于Linux系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 10#编写基于Windows系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 11#自定义数字证书的验证策略;

阅读全文 »

关于Thrift和OpenSSL的安全通信,上篇我们描述了Boost的编译方法,本文编译zlib库。

  • 01#编译OpenSSL库;
  • 02#编译Boost库;
  • 03#编译zlib库
  • 04#编译Libevent库;
  • 05#编译Thrift库;
  • 06#生成客户端和服务端通信所用的数字证书;
  • 07#编写基于Linux系统的测试代码(单向验证:客户端验证服务端);
  • 08#编写基于Windows系统的测试代码(单向验证:客户端验证服务端);
  • 09#编写基于Linux系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 10#编写基于Windows系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 11#自定义数字证书的验证策略;

阅读全文 »

最近对基于Thrift和OpenSSL的安全通信比较感兴趣,根据自己的研究情况,我将其中的研究过程总结下来,作为备忘。

首先,我们明确一下为达成上述目标,需要做哪些工作?

  • 01#编译OpenSSL库
  • 02#编译Boost库;
  • 03#编译zlib库;
  • 04#编译Libevent库;
  • 05#编译Thrift库;
  • 06#生成客户端和服务端通信所用的数字证书;
  • 07#编写基于Linux系统的测试代码(单向验证:客户端验证服务端);
  • 08#编写基于Windows系统的测试代码(单向验证:客户端验证服务端);
  • 09#编写基于Linux系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 10#编写基于Windows系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
  • 11#自定义数字证书的验证策略;

明确了安全通信所需要的执行步骤,我们从OpenSSL的编译开始本系列之旅。

阅读全文 »

关于字节序(大端法、小端法)的定义

《UNXI网络编程》定义:术语“小端”和“大端”表示多字节值的哪一端(小端或大端)存储在该值的起始地址。小端存在起始地址,即是小端字节序(Little-Endian);大端存在起始地址,即是大端字节序(Big-Endian)。
也可以说:

  • 小端法就是低位字节排放在内存的低地址端即该值的起始地址,高位字节排放在内存的高地址端。
  • 大端法就是高位字节排放在内存的低地址端即该值的起始地址,低位字节排放在内存的高地址端。

举个简单的例子,对于整形0x12345678。它在大端法和小端法的系统内存中,分别如图1所示的方式存放。

阅读全文 »

头文件:

1
2
3
4
5
/usr/include/x86_64-linux-gnu/bits/sockaddr.h

/usr/include/x86_64-linux-gnu/bits/socket.h

/usr/include/x86_64-linux-gnu/bits/in.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* Protocol families.  */
#define PF_UNSPEC 0 /* Unspecified. */
#define PF_LOCAL 1 /* Local to host (pipes and file-domain). */
#define PF_UNIX PF_LOCAL /* POSIX name for PF_LOCAL. */
#define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL. */
#define PF_INET 2 /* IP protocol family. */
#define PF_AX25 3 /* Amateur Radio AX.25. */
#define PF_IPX 4 /* Novell Internet Protocol. */
#define PF_APPLETALK 5 /* Appletalk DDP. */
#define PF_NETROM 6 /* Amateur radio NetROM. */
#define PF_BRIDGE 7 /* Multiprotocol bridge. */
#define PF_ATMPVC 8 /* ATM PVCs. */
#define PF_X25 9 /* Reserved for X.25 project. */
#define PF_INET6 10 /* IP version 6. */
#define PF_ROSE 11 /* Amateur Radio X.25 PLP. */
#define PF_DECnet 12 /* Reserved for DECnet project. */
#define PF_NETBEUI 13 /* Reserved for 802.2LLC project. */
#define PF_SECURITY 14 /* Security callback pseudo AF. */
#define PF_KEY 15 /* PF_KEY key management API. */
#define PF_NETLINK 16
#define PF_ROUTE PF_NETLINK /* Alias to emulate 4.4BSD. */
#define PF_PACKET 17 /* Packet family. */
#define PF_ASH 18 /* Ash. */
#define PF_ECONET 19 /* Acorn Econet. */
#define PF_ATMSVC 20 /* ATM SVCs. */
#define PF_RDS 21 /* RDS sockets. */
#define PF_SNA 22 /* Linux SNA Project */
#define PF_IRDA 23 /* IRDA sockets. */
#define PF_PPPOX 24 /* PPPoX sockets. */
#define PF_WANPIPE 25 /* Wanpipe API sockets. */
#define PF_LLC 26 /* Linux LLC. */
#define PF_IB 27 /* Native InfiniBand address. */
#define PF_MPLS 28 /* MPLS. */
#define PF_CAN 29 /* Controller Area Network. */
#define PF_TIPC 30 /* TIPC sockets. */
#define PF_BLUETOOTH 31 /* Bluetooth sockets. */
#define PF_IUCV 32 /* IUCV sockets. */
#define PF_RXRPC 33 /* RxRPC sockets. */
#define PF_ISDN 34 /* mISDN sockets. */
#define PF_PHONET 35 /* Phonet sockets. */
#define PF_IEEE802154 36 /* IEEE 802.15.4 sockets. */
#define PF_CAIF 37 /* CAIF sockets. */
#define PF_ALG 38 /* Algorithm sockets. */
#define PF_NFC 39 /* NFC sockets. */
#define PF_VSOCK 40 /* vSockets. */
#define PF_KCM 41 /* Kernel Connection Multiplexor. */
#define PF_QIPCRTR 42 /* Qualcomm IPC Router. */
#define PF_SMC 43 /* SMC sockets. */
#define PF_XDP 44 /* XDP sockets. */
#define PF_MCTP 45 /* Management component transport protocol. */
#define PF_MAX 46 /* For now.. */

/* Address families. */
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
#define AF_ROSE PF_ROSE
#define AF_DECnet PF_DECnet
#define AF_NETBEUI PF_NETBEUI
#define AF_SECURITY PF_SECURITY
#define AF_KEY PF_KEY
#define AF_NETLINK PF_NETLINK
#define AF_ROUTE PF_ROUTE
#define AF_PACKET PF_PACKET
#define AF_ASH PF_ASH
#define AF_ECONET PF_ECONET
#define AF_ATMSVC PF_ATMSVC
#define AF_RDS PF_RDS
#define AF_SNA PF_SNA
#define AF_IRDA PF_IRDA
#define AF_PPPOX PF_PPPOX
#define AF_WANPIPE PF_WANPIPE
#define AF_LLC PF_LLC
#define AF_IB PF_IB
#define AF_MPLS PF_MPLS
#define AF_CAN PF_CAN
#define AF_TIPC PF_TIPC
#define AF_BLUETOOTH PF_BLUETOOTH
#define AF_IUCV PF_IUCV
#define AF_RXRPC PF_RXRPC
#define AF_ISDN PF_ISDN
#define AF_PHONET PF_PHONET
#define AF_IEEE802154 PF_IEEE802154
#define AF_CAIF PF_CAIF
#define AF_ALG PF_ALG
#define AF_NFC PF_NFC
#define AF_VSOCK PF_VSOCK
#define AF_KCM PF_KCM
#define AF_QIPCRTR PF_QIPCRTR
#define AF_SMC PF_SMC
#define AF_XDP PF_XDP
#define AF_MCTP PF_MCTP
#define AF_MAX PF_MAX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef unsigned short int sa_family_t;

struct sockaddr
{
sa_family_t sa_family;

char sa_data[14]; /* Address data. */
};

typedef uint32_t in_addr_t;

struct in_addr
{
in_addr_t s_addr;
};

struct sockaddr_in
{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof(in_port_t) - sizeof(struct in_addr)];
};


背景

1
2
3
4
#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect是socket套接口编程中非常重要的一个函数,它用于客户机连接使用TCP协议打开的服务机。

connect有同步连接和异步连接两种模式:

  • 同步连接指的是,我们设置socket套接字为阻塞模式,调用connect之后,程序一直等待,直到该函数返回成功或者失败。如果连接过程中,发生了超时重传,接口的耗时时间有可能达127秒之久。假如我们的服务器程序只有一个网络线程,同步connect会阻塞该网络线程较长时间,在这段时间内将不能给其他连接提供服务。
  • 异步连接指的是,我们设置socket套接字为非阻塞模式,调用connect之后,该函数会马上返回,如果连接立即成功,那么皆大欢喜,就不用进行下步操作了。如果连接没有立即成功,我们就用select或者epoll等待操作系统给我们通知,接到通知后,我们再判断连接成功与否。在高性能服务器程序中,我们优先使用异步连接这种模式。
阅读全文 »

参考图书

书名 ISBN 作者 出版社 出版日期 备注
UNIX网络编程 卷1 套接字联网API 第3版 9787115517791 [美]W.理查德.史蒂文斯 人民邮电出版社 2019-10-01 P
UNIX网络编程 卷2 进程间通信 第2版 9787115517807 [美]W.理查德.史蒂文斯 人民邮电出版社 2019-10-01 P
TCP/IP高效编程:改善网络程序的44个技巧 9787115249371 [美]斯纳德 人民邮电出版社 2011-04-01 P
Linux高性能服务器编程 9787111425199 游双 机械工业出版社 2013-06-01 P
阅读全文 »

编程时,有时需要打印版本号或者根据版本号来判断逻辑分支走向,前者一般是字符串形式的版本号,后者一般是整型数表达的版本号。

所以需要设计一个相对通用的版本号定义、升级和生成方法,来方便我们使用。

版本号定义

通常我们使用GNU版本号定义方法,如下所示:

主版本号.子版本号.修正版本号.编译版本号

版本号按如下规则升级:
1)当项目有重大修改或局部修正累积较多时,项目整体发生了全局变化,此时主版本号+1,其他版本号归零;
2)当项目增加功能或需求变更时,主版本号不变,子版本号+1,修正版本号归零;
3)当项目调整功能或缺陷修正时,主版本号不变,子版本号不变,修正版本号+1;
4)编译版本号是在迭代开发过程中进行累加,以供测试人员标识各修复缺陷的版本;

版本号实现

一般,我们把软件的版本号定义在一个独立的文件中,比如叫做version.h,然后在其中定义各级版本号,其他文件引用version.h即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef __XHELLO_VERSION_H__
#define __XHELLO_VERSION_H__

#define _SX_N2S_(num) #num
#define _SX_N2T_(major,minor,patch,build) _SX_N2S_(major) "." _SX_N2S_(minor) "." _SX_N2S_(patch) "." _SX_N2S_(build)
#define _SX_N2N_(major,minor,patch,build) ((major)*1000000 + (minor)*10000 + patch*100 + build)

#define XHELLO_VER_MAJOR 1
#define XHELLO_VER_MINOR 3
#define XHELLO_VER_PATCH 25
#define XHELLO_VER_BUILD 1

#define XHELLO_VERSION _SX_N2T_(XHELLO_VER_MAJOR,XHELLO_VER_MINOR,XHELLO_VER_PATCH,XHELLO_VER_BUILD)
#define XHELLO_VERSION_NUMBER _SX_N2N_(XHELLO_VER_MAJOR,XHELLO_VER_MINOR,XHELLO_VER_PATCH,XHELLO_VER_BUILD)

#endif

按上述版本号定义规则,可知示例代码所表达的含义如下:

  • XHELLO表示软件名称,此处替换成你自己的软件名称;
  • XHELLO软件的版本号,用字符串来表达就是 1.3.25.1,用整型数来表达就是 1032501;
  • 变更版本号时,仅调整XHELLO_VER_MAJOR、XHELLO_VER_MINOR、XHELLO_VER_PATCH、XHELLO_VER_BUILD,其他宏保持不动;

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include "version.h"

int main(int argc, char **argv)
{
std::cout << "version:" << XHELLO_VERSION << std::endl;

if (XHELLO_VERSION_NUMBER > 1031901)
{
// TO DO SOMETHING
}
else
{
// TO DO SOMETHING
}

return 0;
}

(extern 变量) (extern 函数) 解析

这种情况下的extern说明变量或者函数声明在其他的源文件里,而不用include头文件的方式来引用该函数,在链接时,链接器在各个模块中搜索这个变量或者函数来进行最终链接。

(extern “C”) 解析

使用这种extern的情况多发生在使用C++调用由C写成的函数库时,此时编译过程中常发生编译器找不到C函数的问题,从而导致链接失败。为了解决这种情况,才引用了extern “C”这种用法。

那为什么又会出现这种链接失败的情况呢?简单来是由于g++和gcc生成函数名称方式的不同造成的。C++语言在编译的时候为了解决函数的多态问题,不会直接使用程序中书写的函数名称,而会使用一种特别的方法经过中间变换过程生成一个全局唯一函数名。C库函数是没有经过函数名称变换得来的,当C++使用经过变换的函数名称去调没有变换过的函数时,肯定会出现链接失败的情况。

这种特殊的转换方法叫做“名称的特殊处理(Name Mangling)”,比如C++将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,这是一个用C写成的库文件,请用C的方式来链接它们。

阅读全文 »

printf是一套家族函数,是C语言标准库函数,定义于头文件stdio.h,其定义如下所示:

1
2
3
4
5
6
7
#include <stdio.h>

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

printf的format参数是格式控制字符串,它包含两种字段:一是普通字符串,打印时原样输出;二是控制字段,打印时使用可变变量…指定的参数数据,来替换对应的控制字段。两种字段相结合,形成结果字符串,然后printf将结果字符串打印到标准输出。

阅读全文 »

0x01 时间函数之间的关系

1
2
3
4
5
6
7
8
9
10
11
12
struct tm
{
int tm_sec; // 代表目前秒数,正常范围0-59,但允许至61秒;
int tm_min; // 代表目前分数,范围为0-59。
int tm_hour; // 从午夜算起的小时数,范围为0-23。
int tm_mday; // 目前月份的日数,范围为1-31。
int tm_mon; // 代表目前月份,从一月算起,范围为0-11。
int tm_year; // 从1900年算起至今的年数。比如(时间戳1970-01-02 10:23:09,该值为70)。
int tm_wday; // 一星期中的日数,从星期日算起,范围为0-6。
int tm_yday; // 从1月1日算起至今的天数,范围为:0-365。
int tm_isdst; // 日光节约时间的旗标。
};
阅读全文 »