diff --git a/Centos8-install/index.html b/2023/10/09/Centos8-install/index.html similarity index 97% rename from Centos8-install/index.html rename to 2023/10/09/Centos8-install/index.html index 6d4f292..353269f 100644 --- a/Centos8-install/index.html +++ b/2023/10/09/Centos8-install/index.html @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@ - + @@ -647,7 +647,7 @@
前面提到pollwake
会在设备驱动中被回调,下面简单梳理一下在设备驱动中的调用链,还是以socket中的tcp为例,当有数据包来临时函数的调用链为:tcp_data_queue
->sock_def_readable
->wake_up_interruptible_sync_poll
->__wake_up_sync_key
->__wake_up_common_lock
->__wake_up_common
后来查了一下资料,了解到CentOS 8操作系统已经结束了生命周期,社区已不再维护该操作系统版本。按照社区规则,CentOS 8的源地址内容已移除,目前第三方的镜像站中均已移除CentOS 8的源。但由于个人业务需要,仍需使用CentOS 8系统,因此我们需要换源在线安装。
由于安装过程是在线安装,因此需要确保设备连接了互联网,可使用ping操作验证:
@@ -40,7 +40,7 @@最近阅读Aravis 源码,其中大量运用了GObject,于是打算学习一下。
此系列笔记仅主要面向初学者,不会很深入探讨源码的细节,专注于介绍GObject的基本用法。
此系列笔记参考GObject Tutorial for beginners
@@ -111,7 +111,7 @@在上一节中我们介绍了GObject类型的类和实例变量的创建和使用。GObject是一个基本的可实例化类类型,是所有使用GObject系统的类型的基类,提供了继承、封装、多态等面向对象的核心特性。不过我们一般不直接使用GObject本身,而是通过继承GObject来创建新的类型。
本文主要是简单讨论select的特点并梳理一下select的调用链,不涉及具体的使用方法。
本文中涉及的源码均出自Linux内核5.4.0版本
select 是一种 IO 多路复用的技术,它允许单个进程或线程高效地管理多个输入/输出流,从而提高程序的性能和响应速度。
-select的核心功能都在do_select中,里面的关键操作有:
1、poll_initwait:初始化轮询等待队列结构体 (poll_wqueues
) 的所有字段,包括函数指针的初始化,后面用于驱动中回调__pollwait
。
2、for循环:遍历每个fd并调用vfs_poll
,vfs_poll
针对传入的fd,调用其各自的poll函数(由设备驱动程序实现,比如sock_poll
)。之后若满足一定条件则跳出循环。
3、poll_freewait:清理等待队列。
后面出一篇文章详细讨论do_select()
I/O多路复用学习(二)do_select分析
__pollwait的作用是在等待队列上注册一个新的节点(),并初始化poll_table_entry
的成员。
1、poll_get_entry:从轮询等待队列结构体 (poll_wqueues
) 中获取一个空闲的poll_table_entry
结构体实例
2、init_waitqueue_func_entry:初始化poll_table_entry
结构体实例,包括函数指针的初始化,后面用于驱动中回调pollwake。
3、add_wait_queue:向等待队列添加一个节点
当监视的文件描述符上发生了感兴趣的事件时(如可读,可写等),设备驱动将触发回调函数pollwake。pollwake会将进程或线程从睡眠状态唤醒。
-1、select默认支持的描述符有限,为1024(可以通过命令ulimit -n 2048临时修改,也可以修改Linux内核头文件posix_types.h修改__FD_SETSIZE的值,但是要重新编译内核);
2、每次调用需要把fd集合在内核态和用户态间拷贝传递两次,在fdset很大时效率低;
3、select内部监视fd是通过轮询的方式,时间复杂度高,效率低;
4、每次调用后需要重新设置fdset,麻烦。
前一篇笔记:I/O多路复用学习(一)select ,梳理了一下select的调用过程,这一篇笔记对其中的do_select函数的细节做一下简单的讨论。
本文中涉及的源码均出自Linux内核5.4.0版本
了解相关的结构体可以帮助我们更好地理解select内部的实现
相关结构体关系示意图:
poll_wqueues
结构体(轮询等待队列)用于维护若干特定于文件描述符的设备驱动等待队列。但是它并不直接存储设备驱动等待队列本身;它通过inline_entries[]
管理一个或多个poll_table_entry
项,每个项与一个特定的文件描述符和其等待事件相关联。
代码省略了一些,比如超时标志位timed_out
的置位操作等。
__pollwait
用于处理轮询时的等待队列。这个函数通过创建并初始化poll_table_entry
实例,将等待进程(或线程)添加到指定设备或文件的等待队列中。
POLL_TABLE_FULL
为true情况的示意图:
init_waitqueue_func_entry
用于初始化wait_queue_entry
实例的各个成员。
前面提到pollwake
会在设备驱动中被回调,下面简单梳理一下在设备驱动中的调用链,还是以socket中的tcp为例,当有数据包来临时函数的调用链为:tcp_data_queue
->sock_def_readable
->wake_up_interruptible_sync_poll
->__wake_up_sync_key
->__wake_up_common_lock
->__wake_up_common
pollwake
->__pollwake
->default_wake_function
->try_to_wake_up
。最终调用try_to_wake_up
唤醒当前进程。
本文讨论了do_select函数调用链上的一些实现细节,梳理了相关结构体之间的关系。有些地方可能解释的不是很详细,后面持续更新。
本文主要是简单讨论select的特点并梳理一下select的调用链,不涉及具体的使用方法。
本文中涉及的源码均出自Linux内核5.4.0版本
select 是一种 IO 多路复用的技术,它允许单个进程或线程高效地管理多个输入/输出流,从而提高程序的性能和响应速度。
+select的核心功能都在do_select中,里面的关键操作有:
1、poll_initwait:初始化轮询等待队列结构体 (poll_wqueues
) 的所有字段,包括函数指针的初始化,后面用于驱动中回调__pollwait
。
2、for循环:遍历每个fd并调用vfs_poll
,vfs_poll
针对传入的fd,调用其各自的poll函数(由设备驱动程序实现,比如sock_poll
)。之后若满足一定条件则跳出循环。
3、poll_freewait:清理等待队列。
后面出一篇文章详细讨论do_select()
I/O多路复用学习(二)do_select分析
__pollwait的作用是在等待队列上注册一个新的节点(),并初始化poll_table_entry
的成员。
1、poll_get_entry:从轮询等待队列结构体 (poll_wqueues
) 中获取一个空闲的poll_table_entry
结构体实例
2、init_waitqueue_func_entry:初始化poll_table_entry
结构体实例,包括函数指针的初始化,后面用于驱动中回调pollwake。
3、add_wait_queue:向等待队列添加一个节点
当监视的文件描述符上发生了感兴趣的事件时(如可读,可写等),设备驱动将触发回调函数pollwake。pollwake会将进程或线程从睡眠状态唤醒。
+1、select默认支持的描述符有限,为1024(可以通过命令ulimit -n 2048临时修改,也可以修改Linux内核头文件posix_types.h修改__FD_SETSIZE的值,但是要重新编译内核);
2、每次调用需要把fd集合在内核态和用户态间拷贝传递两次,在fdset很大时效率低;
3、select内部监视fd是通过轮询的方式,时间复杂度高,效率低;
4、每次调用后需要重新设置fdset,麻烦。
黑神话悟空昨天上线了,解个包looklook。
先看下咱猴哥的英俊面庞:
然后看下四大天王
持国天王:
多闻天王
广目天王
增长天王
最后再看个小狗:
通过解包可以发现,黑神话里面的资源命名都是使用汉语拼音而非英文单词,不知道这是不是国内行业普遍使用的规矩,希望有从事相关行业的大佬能解开我这个疑惑。
至于为啥不使用英文,我猜一是有些中文词汇很难用英语翻译,强行翻译可能会词不达意;二是有些翻译会导致命名长度太长。
但是哥们儿,下面这个首字母简写我个人认为确实有一点点抽象了,能全部猜出来的也算是神人了。
由于一周目才刚开始打,我现在只能猜出来一部分:GYCY(观音禅院),HFM(黑风庙?),HGS(花果山),HYS(火焰山),LSH(流沙河?),LYS(雷音寺),MGD(埋骨地?),PSD(盘丝洞)。
另外,通过解包我们还可以看见项目中有刘备,孙权,太史慈的人物建模,难道是《黑神话2:三国群英传》?😂
项目里甚至还有一个电饭煲的模型hhh。讲道理,我孙悟空用电饭煲封印个比克大魔王,也是很合理的好吧。
不过上述资源的存在让我想到了一张梗图:
问题描述:在设置UE引擎版本时,下拉菜单中可能会没有GAME_BlackMythWukong (UE 5.0)
这个选项。
解决方法:在settings的update mode选项中选择QA Testing
,然后软件会提示你更新,然后一路确定。待更新完成后重新打开软件,即可在UE Version
中看到GAME_BlackMythWukong (UE 5.0)
这个选项
由于映射文件生成比较复杂,在此提供现成的映射文件(伴随游戏更新映射文件可能失效,失效请联系我更换):
@@ -540,7 +540,7 @@这篇文章仅记录我作为初学者使用FModel工具提取某款游戏模型的过程。
FModel是一个开源软件,可以用于查看和提取UE4-5项目中的资产。它支持从.pak
和.uasset
文件中提取内容,如3D模型,纹理,音频等。
首先进入FModel官网 ,下载最新的发布版本。
@@ -548,44 +548,44 @@FModel还需要依赖.NET
,如果你的系统中没有,则会提示你安装:
安装好.NET
之后,重新打开FModel.exe
,此时软件可以正常打开。
①软件初次打开会弹出这个界面:
这里可以随便设置,之后进入软件还可以更改。
②点击ok进入软件
③设置填写AES key
这里填写你要解包的游戏对应的AES key
④设置选择UE版本和填写游戏路径
⑤解包
全选(我在使用时是这样,可能会因游戏的不同而不一样)然后load
然后就会自动跳到Folder下,此时我们就可以随便选择一个资产双击进行查看
注意:
如果你要解包的游戏是使用UE5开发的,那么在解包的时候可能会报错:
比如3D Viewer
的模型预览是这样
在Settings
的Models
中设置资产的导出格式(比如对于mesh
可以设置导出为.glb
,texture
可以设置导出为.png
)
然后在你要导出的资产上右键,选择要导出的类型即可:
对于UE5+的游戏来说,具有unversioned
属性的包是意料之中的。然而现在有一些解决方案可以让FModel支持对它们的解析。
当成功生成了*.usmap
文件后,你可以在FModel
软件的设置界面将其添加到Mapping File Path
中:
下面我将演示通过使用Dumper-7
生成*.usmap
的过程
为了向游戏中注入dll,我们要使用一个工具:DLL Injector
官网链接:DLL Injector官网
使用方法:
①打开DLL Injector
②选择要加载的DLL文件(有两种方法):
@@ -629,7 +629,7 @@④点击右上方的注入按钮将DLL注入目标进程,结束后会在指定文件夹生成usmap文件
设备通过监听GVCP端口(3956)等待GVCP数据包。当客户端(如:PC设备)向设备(如:相机)发送一个GVCP命令数据包时,设备会回复一个确认数据包。每个命令/确认对(command/acknowledge pair
)都使用一个16 bit的值来标识(就相当于ID),这样可以检查确认数据包是否与之前发送的命令对应。0是这个标识符的错误值。如果在给定的超时时间后没有收到确认,将再次发送命令数据包,直到达到最大重试次数。
ArvGvcp API提供了一系列处理GVCP包的函数。
GVCP数据包的内容由一个64 bit的头部组成,头部后面可能跟着一个数据字节数组(也可能没有,具体取决于GVCP数据包的类型)。多字节值采用大端编码。
@@ -933,7 +933,7 @@本文针对官方例程 中的:03-camera-api做简单的讲解。并介绍其中调用的arv_camera_get_region
,arv_camera_get_pixel_format_as_string
,arv_camera_get_pixel_format
,ARV_PIXEL_FORMAT_BIT_PER_PIXEL
。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
这段代码使用Aravis的API,获取相机的一些基本设置,如图像的宽度、高度和像素格式,主要操作步骤如下:
@@ -949,7 +949,7 @@运行结果:
简介:用于获取相机当前的感兴趣区域(ROI),此函数会将当前相机的ROI的位置坐标(x,y)和尺寸(width,height)通过指针返回,并记录错误信息。
@@ -974,7 +974,7 @@本文针对官方例程 中的:04-camera-features做简单的讲解。并介绍其中调用的arv_camera_get_integer
,arv_camera_get_string
。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
这段代码使用Aravis的API,获取相机的一些基本设置,如图像的宽度、高度和像素格式,主要操作步骤如下:
@@ -993,7 +993,7 @@运行结果:
简介:获取已连接相机的一个整数型特性的值
@@ -1012,7 +1012,7 @@本文针对官方例程 中的:05-chunk-parser做简单的讲解。并介绍其中调用的arv_camera_create_chunk_parser
,arv_camera_set_chunks
,arv_chunk_parser_get_integer_value
函数。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
这段代码使用Aravis的API,操作相机捕获图像并获取流数据中附加的块信息(例程中启用的块数据为图像的长和宽),主要操作步骤如下:
@@ -1056,7 +1056,7 @@最近在做采集软件的开发,由于我自己使用过Huaray和Basler两个品牌的相机,所以在设计软件时尝试设计统一的接口去控制不同品牌和型号相机的相同或类似的行为。当然,我的设计思路都是建立在调用各品牌SDK的基础上。
后来我去外网搜索,发现了一个开源项目Aravis,它通过提供一个通用的API,让我们能够不受相机的品牌或型号限制,自由地进行图像采集和相机控制,支持从简单的图像捕获到复杂的相机设置调整的一系列操作。
项目地址:https://github.com/AravisProject/aravis
Aravis是一个基于glib/gobject的库,允许开发者对遵循GenICam标准的网络相机进行通信和控制。它目前实现了工业相机使用的GigE和USB3协议。它还提供了一个简单的以太网相机模拟器和视频查看器。
@@ -1075,7 +1075,7 @@在github上找到所需的aravis的发行版本,并下载:https://github.com/AravisProject/aravis/releases
我选择的是0.8.31版本:
下载压缩包后解压即可
@@ -1087,13 +1087,13 @@找到Aravis双击打开,即可查看已连接的相机设备
我安装的aravis版本是0.8.31,这个版本aravis的编译依赖0.57.0及以上版本的meson,而我通过apt包管理器下载的meson版本为0.53.2,不满足需求。因此要对meson进行升级操作:
①首先安装或升级pip
@@ -1111,13 +1111,13 @@我的安装结果:
在构建项目的时候,提示了以下错误信息:
说明我系统中的CMake版本过低,需要升级。
@@ -1138,7 +1138,7 @@直接使用包管理器安装即可
@@ -1153,7 +1153,7 @@本文针对官方例程 中的:02-multiple-acquisition-callback做简单的讲解。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
这段代码使用Aravis的API,控制相机连续采集,并异步地在回调函数中获取10个有效图像,主要操作步骤如下:
@@ -1171,7 +1171,7 @@运行结果:
其中<>之间的是线程号。
@@ -1195,7 +1195,7 @@本文针对官方例程 中的:02-multiple-acquisition-main-thread做简单的讲解,并简单介绍其中调用的arv_camera_set_acquisition_mode
,arv_camera_create_stream
,arv_camera_get_payload
,arv_buffer_new
,arv_stream_push_buffer
,arv_camera_start_acquisition
,arv_stream_pop_buffer
,arv_camera_stop_acquisition
函数。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
这段代码使用Aravis的API,控制相机连续采集,并在主线程中从缓冲区获取前10帧图像(假设不丢帧),主要操作步骤如下:
@@ -1213,7 +1213,7 @@运行结果:
简介:设置相机的采集模式
@@ -1254,7 +1254,7 @@本文针对官方例程 中的:02-multiple-acquisition-signal做简单的讲解。并简单介绍其中调用的g_main_loop_new
,g_main_loop_run
,g_main_loop_quit
,g_signal_connect
,arv_stream_set_emit_signals
。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
这段代码使用Aravis的API,控制相机连续采集,并通过GLib的事件循环机制和GObject的信号系统异步地获取10个图像,主要操作步骤如下:
@@ -1274,7 +1274,7 @@运行结果:
其中<>之间的是线程号。
@@ -1296,7 +1296,7 @@观察程序运行时的日志,可以发现new_buffer_cb的运行并不是在主线程中。
但是按照g_signal_connect
的描述,回调函数应该是被同步调用,也就是说new_buffer_cb
理论上应该在主线程被调用。
后来查看文档发现,在GObject的信号系统中,处理器的调用是同步的。当信号发射时,其关联的所有处理器会都会在发射信号的线程中按照它们被连接的顺序依次执行。
在学习Aravis官方例程 的时候,有这么一个函数:arv_camera_get_pixel_format
,它的返回类型是ArvPixelFormat
(本质是个32位无符号整数)。这意味着对于每个图像数据格式,都有自己对应的唯一的编码。我比较好奇Aravis是通过什么规则对各种图像数据格式进行的编码,于是就查看了源码。
本文主要讨论Aravis中对不同图像数据格式的编码规则。
PixelFormat指的是图像中每个像素的数据格式,它定义了图像中像素的组成方式、颜色信息的存储方法和每个像素所占的位数等。这一格式对于图像的处理和显示是非常关键的,因为它直接影响到图像的质量和处理的效率。
@@ -1347,14 +1347,14 @@本文针对官方例程 中的第一个例程:single-acquisition做简单的讲解,并简单分析其中调用的arv_camera_new
,arv_camera_acquisition
,arv_camera_get_model_name
,arv_buffer_get_image_width
,arv_buffer_get_image_height
函数。
aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0
/* SPDX-License-Identifier:Unlicense */ |
此例程较为简单,每一步的细节查看注释即可,此处不过多讲解。
运行结果:
简介:创建一个ArvCamera对象,如果name是NULL,则连接第一个可用的相机。
@@ -1397,7 +1397,7 @@记录开发时自用的小轮子:单例模板。
提供了一个基于模板的单例模式的实现,代码如下:
|
记录开发时自用的小轮子:线程安全队列
|
《共享库链接和加载时的路径搜索优先级》 中提到,使用g++时,共享库在编译期链接时的库路径搜索优先级为:-L指定的路径
>LIBRARY_PATH记录的路径
>默认路径
。
本实验分三步验证上述结论
①单独测试每种方法指定的路径的可行性
②对比测试三种方法间的优先级
③使用DEBUG模式,查看链接器输出的详细信息,二次验证上述结论
值得注意的是,我看网上都说LIBRARY_PATH指定的路径
优先级要大于默认路径
的优先级,但是就我的测试结果来看,结论是相反的(可能是我使用了g++而不是直接使用底层的ld?)。
输出:
可以看到由于我们没有配置任何额外的搜索路径,并且没有在默认搜索路径下放置libhello.so
文件,链接器就找不到相应的共享库文件,就会链接失败。
输出:
没有报错。
然后使用readelf -d
查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。
创建路径/opt/hellolib
,将libhello.so.1.1.0
拷贝至/opt/hellolib
,并在/opt/hellolib
下创建一个软链接(libhello.so
)指向它。然后将/opt/hellolib
添加至LIBRARY_PATH
并进行main.o
和hello
的共享库文件的链接操作,查看是否可以链接成功。
输出:
没有报错。
然后使用readelf -d
查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。
创建路径/opt/hellolib
,将libhello.so.1.1.0
拷贝至/opt/hellolib
,并在/opt/hellolib
下创建一个软链接(libhello.so
)指向它,然后添加链接选项-L/opt/hellolib
并进行链接操作,查看是否可以将main.o
和hello
的共享库文件链接成功。
输出:
没有报错。
然后使用readelf -d
查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。
输出:
然后使用readelf -d
查看可执行文件的动态段信息,可见链接成功,并且链接的是默认路径下的共享库文件libhello.so.2.1.0
(其soname为libhello.so.2
)。因此可以得出结论:默认路径
搜索优先级要高于LIBRARY_PATH指定的路径
的搜索优先级。
对于上述结论,将会在后文的DEBUG模式中给出更详细的验证。
@@ -1535,14 +1535,14 @@输出:
然后使用readelf -d
查看可执行文件的动态段信息,可见链接成功,并且链接的是-L指定路径下的共享库文件libhello.so.2.1.0
(其soname为libhello.so.2
)。因此可以得出结论:-L指定路径
搜索优先级要高于默认搜索路径
的搜索优先级。
对于上述结论,将会在后文的DEBUG模式中给出更详细的验证。
@@ -1560,7 +1560,7 @@图片中的文字内容如下:
@@ -1575,7 +1575,7 @@图片中的文字内容如下(省略了一部分不需要关注的):
@@ -1588,7 +1588,7 @@然后我们再看链接器输出的详细信息:
图片中的文字内容如下:
@@ -1610,7 +1610,7 @@最后我们可以通过链接器在链接特定库(比如我们的libhello
)时的搜索过程验证上述结论:
可见链接器先是搜索我们使用-L
指定的路径/opt/hellolib_L
,然后搜索编译器配置的路径/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib/
(其本质就是默认路径/usr/lib/
),最后搜索LIBRARY_PATH指定的路径/opt/hellolib
。证明了编译过程中链接时库搜索路径的优先级为
《共享库链接和加载时的路径搜索优先级》 中提到,共享库在运行期加载时的路径搜索优先级为:RPATH
>LD_LIBRARY_PATH
>RUNPATH
>/etc/ld.so.conf
和/etc/ld.so.conf.d/*
>默认库路径
本实验分两步验证上述结论
①单独测试每种方法指定的路径的可行性
②查看链接器输出的详细信息,验证上述优先级顺序
操作系统:Ubuntu 20.04
编译器:g++-11.4.0
make:GNU Make 4.2.1
在开发一个新项目时遇到了共享库冲突的问题,因此在这里记录一下共享库的链接和加载过程中库路径的搜索优先级的相关知识。
现在有一个main.o
可重定位目标文件,其中需要用到开源库log4cpp。在链接的时候,我们可以这样链接:
g++ main.o -o a.out -L/path/to/libs -llog4cpp |
在使用第三方库时,我们会发现第三方库会提供一组文件,他们的后缀一般是.so
(如libname.so
),.so.x
和.so.x.y.z
。本文讨论他们之间的关系。
共享库一般会由于修复bug或增加接口等原因不断更新,有些更新是向下兼容的,有些则不是。一旦不向下兼容,那么当共享库更新后,依赖该库的程序将无法运行,需要重新编译。
为了避免上述情况,就要对共享库进行版本控制。根据更新内容的不同可以划分不同的版本号:
@@ -1787,7 +1787,7 @@readelf -d
命令查看其soname在上一篇文章中 我们提到了抽象工厂模式初版代码的一些缺点:①客户端违反开闭原则②提供方违反开闭原则。本文将针对这两点进行讨论
对于缺点①,我们可以使用简单工厂的思路来改进抽象工厂的初版代码。对于上一篇文章中的例子,我们去除CameraFactory
、BaslerCameraFactory
和SickCameraFactory
,取而代之的是SimpleFactory
类。
类图如下:
代码如下:
@@ -1837,12 +1837,12 @@现在我需要开发一个相机操作模块,它可能在Windows
下运行,也可能在Linux
下运行。由于在厂家提供的SDK中,Windows
下的SDK和Linux
下的SDK是有区别的,因此对于一个品牌的相机,我们要创建两个类去封装这两个不同平台下的API。
我们先使用工厂方法模式去设计(以Basler
相机为例),类图如下:
对应的代码(就不用智能指针了,要不然类图不好画):
@@ -1860,7 +1860,7 @@AbstractProductA
和AbstractProductB
是两个抽象产品,之所以为抽象,是因为他们可能有多种不同的实现,就刚才的例子来说,抽象产品就是BaslerCamera
和SickCamera
。ProductA1
,ProductA2
,ProductB1
,ProductB2
就是对两个抽象产品的具体分类的实现,对应例子中的LinuxBaslerCamera
,WindowsBaslerCamera
,LinuxSickCamera
,WindowsSickCamera
。
在前一篇笔记 中我们介绍了工厂方法模式,示例的类图如下:
前文中提到
@@ -1919,7 +1919,7 @@前一篇文章 介绍了简单工厂模式,提到了简单工厂模式的缺点(违反开闭原则,扩展困难),本文要介绍的工厂方法模式在一定程度上弥补了简单工厂模式的缺点。
工厂方法模式是创建型设计模式之一,它在抽象工厂类中声明创建对象的接口,在具体工厂类中实现具体的实例化过程。这个模式的核心思想是将对象的实例化延迟到子类中进行。
这样的话,当要添加一个具体产品时,我们不会修改原有的工厂类(对修改封闭),而是新创建一个关联于具体产品的具体工厂类(对扩展开放)。
@@ -1928,7 +1928,7 @@UML类图如下:
代码如下:
@@ -1956,7 +1956,7 @@工厂模式是一种常用的设计模式,属于创建型模式之一。它的主要目的是为了解耦组件之间的依赖关系。通过使用工厂模式,系统中的具体类的实例化过程可以被抽象出来,从而使得系统更加模块化,增强了系统的可维护性和可扩展性。
工厂模式可以分为三种类型:简单工厂模式,工厂方法模式和抽象工厂模式。本文先讨论简单工厂模式。
在工业相机领域,多个品牌如Basler、Sick、Huaray等,各自提供了控制相机的API。本文中我们按照提供方和使用方两个角色来进行分析,以便更好地理解工厂模式的优点:
@@ -1977,7 +1977,7 @@UML类图如下:
代码如下:
@@ -2006,7 +2006,7 @@因需迁移单一代码仓库至其他代码托管平台,要迁移的包括仓库内容以及所有历史记录和推送日志。
本文中的方法同样适用于在同一代码托管平台中克隆仓库。
1. 创建新仓库:
在目的平台的指定位置创建一个新的仓库(目的仓库),用于接收克隆的数据。
因需要,对Centos8操作系统的启动时间进行优化。
Centos8使用了systemd作为系统和服务管理器。systemd是现代Linux发行版中普遍采用的初始化系统,它负责在启动时初始化系统环境并管理系统服务。我们在分析操作系统的启动过程时可以使用systemd
和systemctl
相关的命令。
首先查看操作系统启动过程中各阶段的耗时信息:
@@ -2076,7 +2076,7 @@分析服务启动时间并禁用非必要的服务:使用systemd-analyze blame
查看启动过程中每个服务的耗时,根据具体情况确定哪些服务是非常耗时且非必要的,使用systemctl disable xxxservicenamexxx
禁用这些服务以加速启动过程。
当操作系统中存在多个版本的GCC时,可以使用使用update-alternatives
管理默认使用的编译器版本。
本文使用gcc-9
和gcc-11
做演示,操作系统为ubuntu-20.04
①使用以下命令确认gcc
已正确安装
没有配置过应该输出如下:
③添加版本到update-alternatives
@@ -2118,7 +2118,7 @@执行此命令后,系统将提供一个选择列表,我们可以选择默认的gcc
版本
我这里优先级最大的gcc-11
被自动选为了默认版本,我们直接输入回车选择它就可以(具体的要根据你的实际情况进行选择)
因需要,对主板两个网口进行桥接。
主板型号:Gigabyte GA-IMB370TN
操作系统:CentOS 8.2
先备份旧的网络配置文件,然后将其清除。
网络配置文件的位置如下:
网络配置文件:
nmcli con show |
我的两个网口名称分别为:eno1和enp3s0
创建一个新的桥接连接,其名为br0,设置其ipv4地址为:xxx.xxx.xxx.xxx(如192.168.23.23),子网掩码为:255.255.255.0,并设置为自动连接。
br0将eno1和enp3s0作为从属接口,这允许两个网络接口(eno1和enp3s0)在同一个桥接网络内可以相互通信,同时这个桥接还可以与其他设备或网络通信
操作结果:
此时,网络配置文件有三个:
nmcli con show |
当然也可以使用另一台设备去连接这两个网口,若使用两个网口都可以连接到设置的ip:xxx.xxx.xxx.xxx。则证明操作成功。
至此网络桥接已操作完成
@@ -2185,7 +2185,7 @@本文结合源码讨论std::shared_ptr和std::weak_ptr的部分底层实现,然后讨论引用计数,弱引用计数的创建和增减。
文章中尽可能的先阐述原理,然后再贴上代码。如果有不想看代码的,直接略过代码即可。
本文涉及的源码均出自gcc 9.4.0版本
控制块是shared_ptr
和weak_ptr
中的重要组成,主要用于管理资源的引用计数和生命周期。这个机制允许智能指针安全地共享和管理同一个对象,同时自动释放不再需要的资源。
控制块包含以下部分:
@@ -2200,7 +2200,7 @@示意图大概长这样:
在谈引用计数和弱引用计数的创建时,其实就是讨论控制块的创建。
@@ -2295,7 +2295,7 @@在《单例模式学习》 中提到了,在单例对象是通过new
关键字动态分配在堆上的情况下,当程序退出时,不会通过C++的RAII机制自动调用其析构函数。本文讨论一下这种现象的原因以及解决方法。
在DCLP(双检查锁模式)中,CSingleton中的instance
是一个静态指针变量,被分配在全局/静态存储区。而instance
所指向的CSingleton实例是通过new
创建在堆上的,只能手动调用delete来释放相关资源(对于单例模式这是无法实现的,因为析构函数私有),无法通过RAII释放相关资源。
在程序结束时,instance
这个指针变量被销毁了,但它所指向的内存空间中的CSingleton对象并没有被显式销毁,而是由操作系统去回收这一块内存(不会调用其析构函数)。然而依赖操作系统来清理资源并不是一个优雅的结束方式,可能会造成文件句柄未关闭、网络连接未断开等资源泄漏。
class CSingleton |
运行结果:
我们还可以利用智能指针引用计数机制,对资源自动管理:
@@ -2320,7 +2320,7 @@测试结果:
局部静态变量形式的单例模式也可以完成资源的释放,详见《单例模式学习》 。
@@ -2332,7 +2332,7 @@在《单例模式学习》 中曾提到懒汉式DCLP的单例模式实际也不是线程安全的,这是编译器的指令重排导致的,本文就简单讨论一下指令重排对单例模式的影响,以及对应的解决方法。
指令重排(Instruction Reordering)是编译器或处理器为了优化程序执行效率而对程序中的指令序列进行重新排序的过程。这种重排可以发生在编译时也可以发生在运行时,目的是为了减少指令的等待时间和提高执行的并行性。
@@ -2369,7 +2369,7 @@
单例模式,其核心目标是确保在程序运行的过程中,有且只有存在一个实例才能保证他们的逻辑正确性以及良好的效率。因此单例模式的实现思路就是确保一个类有且只有一个实例,并提供一个该实例的全局访问点。
单例模式设计要点:
测试结果:
饿汉式的缺点:
@@ -2398,7 +2398,7 @@测试结果:
但是上述代码有几个缺点:
@@ -2416,7 +2416,7 @@测试结果:
但是遗憾的是,这种方法其实也不是线程安全的,具体原因可见:补充-指令重排
@@ -2427,7 +2427,7 @@测试结果:
对于线程安全问题:在C++11及更高版本中,静态局部变量的初始化是线程安全的。即当多个线程同时首次访问局部静态变量,编译器可以保证其初始化代码仅执行一次,防止了任何可能的竞态条件或重复初始化。
@@ -2438,7 +2438,7 @@测试结果:
这种形式使用了奇异递归模板模式(Curiously Recurring Template Pattern, CRTP)。在使用时要注意,子类需要将自己作为模板参数传递给CSingleton模板进行模板类实例化,用做基类;同时需要将基类声明为友元,这样才能在通过CSingleton<T>::getInstance()
方法创建MyClass唯一实例时,调用到MyClass的私有构造函数。
本文操作均在ubuntu20.04下进行。
①使用包管理器安装
这种方法比较简单,直接使用apt包管理器安装jsoncpp:
apt install libjsoncpp-dev |
②查看头文件和库文件
安装完毕后我们可以去找一下jsoncpp的头文件和库文件
头文件位于/usr/include/jsoncpp/json/
库文件位于/usr/lib/x86_64-linux-gnu/
但是我们通过头文件的version.h和动态库文件的realname,可以知道使用apt包管理器安装的jsoncpp的版本号为1.7.4
那如果我需要安装的是jsoncpp的其他版本,就需要下载源代码并编译安装。
@@ -2492,7 +2492,7 @@
|
本文的操作均在ubuntu20.04下进行
本文仅介绍从源码编译安装log4cpp的过程。
①在开始编译前,首先要确保系统中安装了g++
,make
,autoconf
和libtool
④查看头文件和库文件
默认配置安装的log4cpp,其头文件位于/usr/local/include/log4cpp/
,库文件位于/usr/local/lib/
。
|
输出结果:
Linux user-GA-IMB410TN 5.15.0-122-generic #132~20.04.1-Ubuntu SMP Fri Aug 30 15:50:07 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux |
gcc版本: