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 @@

  • - 链接: https://paw5zx.github.io/Centos8-install/ + 链接: https://paw5zx.github.io/2023/10/09/Centos8-install/
  • @@ -673,13 +673,13 @@

    推荐阅读 -
    +
    开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + jsoncpp的安装及使用 jsoncpp的安装及使用 - + 内存布局分析工具pahole的安装和使用 内存布局分析工具pahole的安装和使用
    @@ -688,10 +688,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + jsoncpp的安装及使用 jsoncpp的安装及使用
    @@ -704,7 +704,7 @@

    网络配置文件
    网络配置文件

    查看网口名称

    1
    nmcli con show

    我的两个网口名称分别为:eno1和enp3s0

    网口名称
    网口名称

    建立网桥

    创建一个新的桥接连接,其名为br0,设置其ipv4地址为:xxx.xxx.xxx.xxx(如192.168.23.23),子网掩码为:255.255.255.0,并设置为自动连接。
    br0将eno1和enp3s0作为从属接口,这允许两个网络接口(eno1和enp3s0)在同一个桥接网络内可以相互通信,同时这个桥接还可以与其他设备或网络通信

    @@ -628,19 +628,19 @@

    桥接操作结果
    桥接操作结果

    此时,网络配置文件有三个:
    网络配置文件2
    网络配置文件2

    查看是否桥接成功

    1
    nmcli con show

    在这里插入图片描述
    在这里插入图片描述

    当然也可以使用另一台设备去连接这两个网口,若使用两个网口都可以连接到设置的ip:xxx.xxx.xxx.xxx。则证明操作成功。

    至此网络桥接已操作完成

    @@ -662,7 +662,7 @@

    推荐阅读

    -
    +
    CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI) - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + Centos8系统启动时间优化 Centos8系统启动时间优化
    @@ -703,10 +703,10 @@

    推荐阅读

    -
    +
    CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI) - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装
    @@ -719,7 +719,7 @@

    简介调用过程

    do_select

    select调用链
    select调用链

    select的核心功能都在do_select中,里面的关键操作有:
    1、poll_initwait:初始化轮询等待队列结构体 (poll_wqueues) 的所有字段,包括函数指针的初始化,后面用于驱动中回调__pollwait
    2、for循环:遍历每个fd并调用vfs_pollvfs_poll针对传入的fd,调用其各自的poll函数(由设备驱动程序实现,比如sock_poll)。之后若满足一定条件则跳出循环。
    3、poll_freewait:清理等待队列。

    @@ -616,7 +616,7 @@

    __pollwait

    __pollwait调用链
    __pollwait调用链

    __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:向等待队列添加一个节点

    @@ -641,7 +641,7 @@

    推荐阅读

    -
    +
    I/O多路复用学习(二)do_select分析 I/O多路复用学习(二)do_select分析 - + Centos8系统启动时间优化 Centos8系统启动时间优化 - + GObject学习笔记(二)类型创建与注册 GObject学习笔记(二)类型创建与注册
    @@ -682,10 +682,10 @@

    推荐阅读

    -
    +
    I/O多路复用学习(二)do_select分析 I/O多路复用学习(二)do_select分析 - + Centos8系统启动时间优化 Centos8系统启动时间优化
    @@ -698,7 +698,7 @@

    前言相关结构体

    了解相关的结构体可以帮助我们更好地理解select内部的实现
    相关结构体关系示意图:

    结构体关系图
    结构体关系图

    struct poll_wqueues

    poll_wqueues结构体(轮询等待队列)用于维护若干特定于文件描述符的设备驱动等待队列。但是它并不直接存储设备驱动等待队列本身;它通过inline_entries[]管理一个或多个poll_table_entry项,每个项与一个特定的文件描述符和其等待事件相关联。

    @@ -630,7 +630,7 @@

    do_select

    do_select流程图
    do_select流程图

    代码省略了一些,比如超时标志位timed_out的置位操作等。

    @@ -666,7 +666,7 @@

    vf

    __pollwait

    pollwait
    pollwait

    __pollwait用于处理轮询时的等待队列。这个函数通过创建并初始化poll_table_entry实例,将等待进程(或线程)添加到指定设备或文件的等待队列中。

    @@ -687,7 +687,7 @@

    POLL_TABLE_FULL为真
    POLL_TABLE_FULL为真

    init_waitqueue_func_entry

    init_waitqueue_func_entry用于初始化wait_queue_entry实例的各个成员。

    @@ -701,7 +701,7 @@

    pollwake

    pollwake
    pollwake

    回调过程

    前面提到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

    @@ -727,7 +727,7 @@

    推荐阅读

    -
    +
    I/O多路复用学习(一)select I/O多路复用学习(一)select - + C++自用小轮子——线程安全队列 C++自用小轮子——线程安全队列 - + 内存布局分析工具pahole的安装和使用 内存布局分析工具pahole的安装和使用
    @@ -768,10 +768,10 @@

    推荐阅读

    -
    +
    I/O多路复用学习(一)select I/O多路复用学习(一)select - + C++自用小轮子——线程安全队列 C++自用小轮子——线程安全队列
    @@ -784,7 +784,7 @@

    测试结果:

    饿汉式的缺点:

    @@ -634,7 +634,7 @@

    测试结果:

    但是上述代码有几个缺点:

    @@ -652,7 +652,7 @@

    但是遗憾的是,这种方法其实也不是线程安全的,具体原因可见:补充-指令重排

    @@ -663,7 +663,7 @@

    对于线程安全问题:在C++11及更高版本中,静态局部变量的初始化是线程安全的。即当多个线程同时首次访问局部静态变量,编译器可以保证其初始化代码仅执行一次,防止了任何可能的竞态条件或重复初始化。

    @@ -674,7 +674,7 @@

    这种形式使用了奇异递归模板模式(Curiously Recurring Template Pattern, CRTP)。在使用时要注意,子类需要将自己作为模板参数传递给CSingleton模板进行模板类实例化,用做基类;同时需要将基类声明为友元,这样才能在通过CSingleton<T>::getInstance()方法创建MyClass唯一实例时,调用到MyClass的私有构造函数。

    @@ -697,7 +697,7 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(一)单例模式补充——指令重排 设计模式学习(一)单例模式补充——指令重排 - + C++自用小轮子——单例模板 C++自用小轮子——单例模板
    @@ -738,10 +738,10 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(一)单例模式补充——指令重排 设计模式学习(一)单例模式补充——指令重排
    @@ -754,7 +754,7 @@

    智能指针

    我们还可以利用智能指针引用计数机制,对资源自动管理:

    @@ -627,7 +627,7 @@

    局部静态变量

    局部静态变量形式的单例模式也可以完成资源的释放,详见《单例模式学习》

    @@ -649,7 +649,7 @@

  • - 链接: https://paw5zx.github.io/singleton-destruct/ + 链接: https://paw5zx.github.io/2024/03/19/singleton-destruct/
  • @@ -675,13 +675,13 @@

    推荐阅读

  • -
    +
    设计模式学习(一)单例模式的几种实现方式 设计模式学习(一)单例模式的几种实现方式 - + 设计模式学习(一)单例模式补充——指令重排 设计模式学习(一)单例模式补充——指令重排 - + C++自用小轮子——单例模板 C++自用小轮子——单例模板
    @@ -690,10 +690,10 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式的几种实现方式 设计模式学习(一)单例模式的几种实现方式 - + 设计模式学习(一)单例模式补充——指令重排 设计模式学习(一)单例模式补充——指令重排
    @@ -706,7 +706,7 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(一)单例模式的几种实现方式 设计模式学习(一)单例模式的几种实现方式 - + C++自用小轮子——单例模板 C++自用小轮子——单例模板
    @@ -687,10 +687,10 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(一)单例模式的几种实现方式 设计模式学习(一)单例模式的几种实现方式
    @@ -703,7 +703,7 @@

    https://github.com/AravisProject/aravis/releases
    我选择的是0.8.31版本:

    下载压缩包后解压即可

    @@ -637,13 +637,13 @@

    视频查看器

    找到Aravis双击打开,即可查看已连接的相机设备

    安装过程中遇到的问题

    meson版本过低

    我安装的aravis版本是0.8.31,这个版本aravis的编译依赖0.57.0及以上版本的meson,而我通过apt包管理器下载的meson版本为0.53.2,不满足需求。因此要对meson进行升级操作:

    ①首先安装或升级pip

    @@ -661,13 +661,13 @@

    CMake版本过低

    在构建项目的时候,提示了以下错误信息:

    说明我系统中的CMake版本过低,需要升级。

    @@ -688,7 +688,7 @@

    缺少GStreamer组件

    直接使用包管理器安装即可

    @@ -713,7 +713,7 @@

    推荐阅读

    -
    +
    CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI) - + jsoncpp的安装及使用 jsoncpp的安装及使用 - + 使用update-alternatives管理GCC版本 使用update-alternatives管理GCC版本
    @@ -754,10 +754,10 @@

    推荐阅读

    -
    +
    CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI) - + jsoncpp的安装及使用 jsoncpp的安装及使用
    @@ -770,7 +770,7 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式的几种实现方式 设计模式学习(一)单例模式的几种实现方式 - + 设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(一)单例模式补充——指令重排 设计模式学习(一)单例模式补充——指令重排
    @@ -664,10 +664,10 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式的几种实现方式 设计模式学习(一)单例模式的几种实现方式 - + 设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构
    @@ -680,7 +680,7 @@

  • - 链接: https://paw5zx.github.io/cpp-development-components-threadsafe-queue/ + 链接: https://paw5zx.github.io/2024/04/03/cpp-development-components-threadsafe-queue/
  • @@ -646,13 +646,13 @@

    推荐阅读

  • -
    +
    设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(二)工厂模式——工厂方法模式+注册表 设计模式学习(二)工厂模式——工厂方法模式+注册表 - + C++自用小轮子——单例模板 C++自用小轮子——单例模板
    @@ -661,10 +661,10 @@

    推荐阅读

    -
    +
    设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(二)工厂模式——工厂方法模式+注册表 设计模式学习(二)工厂模式——工厂方法模式+注册表
    @@ -677,7 +677,7 @@

    引用计数与弱引用计数创建过程

    在谈引用计数和弱引用计数的创建时,其实就是讨论控制块的创建。

    @@ -721,7 +721,7 @@

    推荐阅读

    -
    +
    GObject学习笔记(一)类和实例 GObject学习笔记(一)类和实例 - + 设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构 - + 设计模式学习(二)工厂模式——工厂方法模式+注册表 设计模式学习(二)工厂模式——工厂方法模式+注册表
    @@ -762,10 +762,10 @@

    推荐阅读

    -
    +
    GObject学习笔记(一)类和实例 GObject学习笔记(一)类和实例 - + 设计模式学习(一)单例模式补充——单例模式析构 设计模式学习(一)单例模式补充——单例模式析构
    @@ -778,7 +778,7 @@

    源码此例程较为简单,每一步的细节查看注释即可,此处不过多讲解。
    运行结果:

    函数说明

    arv_camera_new

    简介:创建一个ArvCamera对象,如果name是NULL,则连接第一个可用的相机。

    @@ -661,7 +661,7 @@

  • - 链接: https://paw5zx.github.io/aravis-single-acquisition/ + 链接: https://paw5zx.github.io/2024/04/10/aravis-single-acquisition/
  • @@ -687,13 +687,13 @@

    推荐阅读

  • -
    +
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features - + 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback
    @@ -702,10 +702,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features
    @@ -718,7 +718,7 @@

    其中<>之间的是线程号。

    @@ -653,7 +653,7 @@

  • - 链接: https://paw5zx.github.io/aravis-multiple-acquisition-callback/ + 链接: https://paw5zx.github.io/2024/04/15/aravis-multiple-acquisition-callback/
  • @@ -679,13 +679,13 @@

    推荐阅读

  • -
    +
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition
    @@ -694,10 +694,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal
    @@ -710,7 +710,7 @@

    函数说明

    arv_camera_set_acquisition_mode

    简介:设置相机的采集模式

    @@ -670,7 +670,7 @@

  • - 链接: https://paw5zx.github.io/aravis-multiple-acquisition-main-thread/ + 链接: https://paw5zx.github.io/2024/04/15/aravis-multiple-acquisition-main-thread/
  • @@ -696,13 +696,13 @@

    推荐阅读

  • -
    +
    开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition - + 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal - + 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback
    @@ -711,10 +711,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition - + 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal
    @@ -727,7 +727,7 @@

    其中<>之间的是线程号。

    @@ -644,7 +644,7 @@

    Q&A

    回调函数的同步调用与异步调用

    观察程序运行时的日志,可以发现new_buffer_cb的运行并不是在主线程中。

    但是按照g_signal_connect的描述,回调函数应该是被同步调用,也就是说new_buffer_cb理论上应该在主线程被调用。
    后来查看文档发现,在GObject的信号系统中,处理器的调用是同步的。当信号发射时,其关联的所有处理器会都会在发射信号的线程中按照它们被连接的顺序依次执行。

    @@ -670,7 +670,7 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition
    @@ -711,10 +711,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback 开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback
    @@ -727,7 +727,7 @@

    函数说明

    arv_camera_get_region

    简介:用于获取相机当前的感兴趣区域(ROI),此函数会将当前相机的ROI的位置坐标(x,y)和尺寸(width,height)通过指针返回,并记录错误信息。

    @@ -652,7 +652,7 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition - + 开源相机管理库Aravis学习——PixelFormat编码规则 开源相机管理库Aravis学习——PixelFormat编码规则
    @@ -693,10 +693,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition
    @@ -709,7 +709,7 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(五)——camera-api 开源相机管理库Aravis例程学习(五)——camera-api - + 开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features - + 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread
    @@ -685,10 +685,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(五)——camera-api 开源相机管理库Aravis例程学习(五)——camera-api - + 开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features
    @@ -701,7 +701,7 @@

    函数说明

    arv_camera_get_integer

    简介:获取已连接相机的一个整数型特性的值

    @@ -649,7 +649,7 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(五)——camera-api 开源相机管理库Aravis例程学习(五)——camera-api - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition - + 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread
    @@ -690,10 +690,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(五)——camera-api 开源相机管理库Aravis例程学习(五)——camera-api - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition
    @@ -706,7 +706,7 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features - + 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - + 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition
    @@ -696,10 +696,10 @@

    推荐阅读

    -
    +
    开源相机管理库Aravis例程学习(六)——camera-features 开源相机管理库Aravis例程学习(六)——camera-features - + 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread
    @@ -712,7 +712,7 @@

    总结
  • - 链接: https://paw5zx.github.io/git-code-migration/ + 链接: https://paw5zx.github.io/2024/05/10/git-code-migration/
  • @@ -647,13 +647,13 @@

    总结 推荐阅读

  • -
    +
    设计模式学习(二)工厂模式——抽象工厂模式 设计模式学习(二)工厂模式——抽象工厂模式 - + CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI) - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装
    @@ -662,10 +662,10 @@

    总结 推荐阅读

    -
    +
    设计模式学习(二)工厂模式——抽象工厂模式 设计模式学习(二)工厂模式——抽象工厂模式 - + CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI)
    @@ -678,7 +678,7 @@

    总结

    总结

    示例UML类图如下:

    代码如下:

    @@ -661,7 +661,7 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式 - + 设计模式学习(二)工厂模式——工厂方法模式+注册表 设计模式学习(二)工厂模式——工厂方法模式+注册表 - + 设计模式学习(二)工厂模式——抽象工厂模式+注册表 设计模式学习(二)工厂模式——抽象工厂模式+注册表
    @@ -702,10 +702,10 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式 - + 设计模式学习(二)工厂模式——工厂方法模式+注册表 设计模式学习(二)工厂模式——工厂方法模式+注册表
    @@ -718,7 +718,7 @@

  • @@ -681,7 +681,7 @@

    总结
  • - 链接: https://paw5zx.github.io/linux-centos-boot-time-optimization/ + 链接: https://paw5zx.github.io/2024/05/16/linux-centos-boot-time-optimization/
  • @@ -707,13 +707,13 @@

    总结 推荐阅读 -
    +
    I/O多路复用学习(一)select I/O多路复用学习(一)select - + 共享库链接和加载时的路径搜索优先级 共享库链接和加载时的路径搜索优先级 - + 共享库soname机制 共享库soname机制
    @@ -722,10 +722,10 @@

    总结 推荐阅读

    -
    +
    I/O多路复用学习(一)select I/O多路复用学习(一)select - + 共享库链接和加载时的路径搜索优先级 共享库链接和加载时的路径搜索优先级
    @@ -738,7 +738,7 @@

    总结

    总结

    示例UML类图如下:

    代码如下:

    @@ -648,7 +648,7 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——简单工厂模式 设计模式学习(二)工厂模式——简单工厂模式 - + 设计模式学习(二)工厂模式——抽象工厂模式+注册表 设计模式学习(二)工厂模式——抽象工厂模式+注册表 - + 设计模式学习(二)工厂模式——工厂方法模式+注册表 设计模式学习(二)工厂模式——工厂方法模式+注册表
    @@ -689,10 +689,10 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——简单工厂模式 设计模式学习(二)工厂模式——简单工厂模式 - + 设计模式学习(二)工厂模式——抽象工厂模式+注册表 设计模式学习(二)工厂模式——抽象工厂模式+注册表
    @@ -705,7 +705,7 @@

    工厂方法模式的瑕疵

    前一篇笔记 中我们介绍了工厂方法模式,示例的类图如下:

    前文中提到

    @@ -640,7 +640,7 @@

  • - 链接: https://paw5zx.github.io/design-pattern-2-factory-method-reflect/ + 链接: https://paw5zx.github.io/2024/06/03/design-pattern-2-factory-method-reflect/
  • @@ -666,13 +666,13 @@

    推荐阅读

  • -
    +
    设计模式学习(二)工厂模式——简单工厂模式 设计模式学习(二)工厂模式——简单工厂模式 - + 设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式 - + 设计模式学习(二)工厂模式——抽象工厂模式+注册表 设计模式学习(二)工厂模式——抽象工厂模式+注册表
    @@ -681,10 +681,10 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——简单工厂模式 设计模式学习(二)工厂模式——简单工厂模式 - + 设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式
    @@ -697,7 +697,7 @@

    安装②查看头文件和库文件
    安装完毕后我们可以去找一下jsoncpp的头文件和库文件
    头文件位于/usr/include/jsoncpp/json/

    库文件位于/usr/lib/x86_64-linux-gnu/

    但是我们通过头文件的version.h和动态库文件的realname,可以知道使用apt包管理器安装的jsoncpp的版本号为1.7.4

    那如果我需要安装的是jsoncpp的其他版本,就需要下载源代码并编译安装。

    @@ -648,7 +648,7 @@

    使用

    编写示例代码

    写JSON

    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
    #include <json/json.h>
    #include <iostream>

    int main()
    {
    // 创建一个Json::Value对象
    Json::Value root;

    // 向对象中添加数据
    root["name"] = "John Doe";
    root["age"] = 30;
    root["isAlive"] = true;
    root["address"]["city"] = "New York";
    root["address"]["state"] = "NY";

    // 创建一个Json::StreamWriterBuilder
    Json::StreamWriterBuilder writer;

    // 将Json::Value对象转换为字符串
    std::string output = Json::writeString(writer, root);

    // 打印输出
    std::cout << output << std::endl;

    return 0;
    }
    @@ -658,13 +658,13 @@

    输出

    写JSON

    读JSON

    @@ -685,7 +685,7 @@

  • - 链接: https://paw5zx.github.io/thirdlib-jsoncpp/ + 链接: https://paw5zx.github.io/2024/06/13/thirdlib-jsoncpp/
  • @@ -711,13 +711,13 @@

    推荐阅读
    -
    +
    开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI) - + log4cpp的安装及使用 log4cpp的安装及使用
    @@ -726,10 +726,10 @@

    推荐阅读
    -
    +
    开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI)
    @@ -742,7 +742,7 @@

    背景我们先使用工厂方法模式去设计(以Basler相机为例),类图如下:

    对应的代码(就不用智能指针了,要不然类图不好画):

    @@ -625,7 +625,7 @@

    AbstractProductAAbstractProductB是两个抽象产品,之所以为抽象,是因为他们可能有多种不同的实现,就刚才的例子来说,抽象产品就是BaslerCameraSickCameraProductA1ProductA2ProductB1ProductB2就是对两个抽象产品的具体分类的实现,对应例子中的LinuxBaslerCameraWindowsBaslerCameraLinuxSickCameraWindowsSickCamera

    @@ -665,7 +665,7 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——抽象工厂模式+注册表 设计模式学习(二)工厂模式——抽象工厂模式+注册表 - + 设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式 - + 设计模式学习(二)工厂模式——简单工厂模式 设计模式学习(二)工厂模式——简单工厂模式
    @@ -706,10 +706,10 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——抽象工厂模式+注册表 设计模式学习(二)工厂模式——抽象工厂模式+注册表 - + 设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式
    @@ -722,7 +722,7 @@

    代码如下:

    @@ -639,7 +639,7 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——抽象工厂模式 设计模式学习(二)工厂模式——抽象工厂模式 - + 设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式 - + 设计模式学习(二)工厂模式——简单工厂模式 设计模式学习(二)工厂模式——简单工厂模式
    @@ -680,10 +680,10 @@

    推荐阅读

    -
    +
    设计模式学习(二)工厂模式——抽象工厂模式 设计模式学习(二)工厂模式——抽象工厂模式 - + 设计模式学习(二)工厂模式——工厂方法模式 设计模式学习(二)工厂模式——工厂方法模式
    @@ -696,7 +696,7 @@

  • - 链接: https://paw5zx.github.io/cpp-shared-lib-search-priority/ + 链接: https://paw5zx.github.io/2024/07/15/cpp-shared-lib-search-priority/
  • @@ -661,13 +661,13 @@

    推荐阅读

  • -
    +
    编译期链接时共享库路径搜索优先级实验 编译期链接时共享库路径搜索优先级实验 - + 共享库soname机制 共享库soname机制 - + 运行期加载时共享库路径搜索优先级实验 运行期加载时共享库路径搜索优先级实验
    @@ -676,10 +676,10 @@

    推荐阅读

    -
    +
    编译期链接时共享库路径搜索优先级实验 编译期链接时共享库路径搜索优先级实验 - + 共享库soname机制 共享库soname机制
    @@ -692,7 +692,7 @@

    安装④查看头文件和库文件
    默认配置安装的log4cpp,其头文件位于/usr/local/include/log4cpp/,库文件位于/usr/local/lib/

    使用

    示例代码

    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
    #include <string>
    #include <iomanip>
    #include "log4cpp/PropertyConfigurator.hh"
    #include "log4cpp/Category.hh"

    int main()
    {
    try
    {
    log4cpp::PropertyConfigurator::configure("./cfg/log4cfg");
    }
    catch (log4cpp::ConfigureFailure& f)
    {
    std::cerr << "configure problem " << f.what() << std::endl;

    }
    log4cpp::Category & log = log4cpp::Category::getRoot();

    std::string s = __FILE__;
    s += " : ";
    std::ostringstream line;
    line << std::setw(4) << std::setfill('0') << __LINE__;
    s += line.str();
    s += " : ";
    std::ostringstream buf;
    buf<< "test message"; ;
    s += buf.str();

    log.info(s);
    }
    @@ -628,7 +628,7 @@

    输出

    输出结果:

    @@ -649,7 +649,7 @@

    输出
  • - 链接: https://paw5zx.github.io/thirdlib-log4cpp/ + 链接: https://paw5zx.github.io/2024/07/15/thirdlib-log4cpp/
  • @@ -675,13 +675,13 @@

    输出 推荐阅读

  • -
    +
    jsoncpp的安装及使用 jsoncpp的安装及使用 - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI)
    @@ -690,10 +690,10 @@

    输出 推荐阅读

    -
    +
    jsoncpp的安装及使用 jsoncpp的安装及使用 - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装
    @@ -706,7 +706,7 @@

    输出

    输出

    安装好.NET之后,重新打开FModel.exe,此时软件可以正常打开。

    FModel使用

    初次使用

    ①软件初次打开会弹出这个界面

    这里可以随便设置,之后进入软件还可以更改。
    ②点击ok进入软件
    ③设置填写AES key
    这里填写你要解包的游戏对应的AES key

    ④设置选择UE版本和填写游戏路径

    解包
    全选(我在使用时是这样,可能会因游戏的不同而不一样)然后load

    然后就会自动跳到Folder下,此时我们就可以随便选择一个资产双击进行查看

    注意:
    如果你要解包的游戏是使用UE5开发的,那么在解包的时候可能会报错:

    @@ -669,14 +669,14 @@

    资产导出

    SettingsModels中设置资产的导出格式(比如对于mesh可以设置导出为.glbtexture可以设置导出为.png

    然后在你要导出的资产上右键,选择要导出的类型即可:

    附录

    dumper

    对于UE5+的游戏来说,具有unversioned属性的包是意料之中的。然而现在有一些解决方案可以让FModel支持对它们的解析。

    @@ -686,7 +686,7 @@

    附录当成功生成了*.usmap文件后,你可以在FModel软件的设置界面将其添加到Mapping File Path中:

    下面我将演示通过使用Dumper-7生成*.usmap的过程

    @@ -694,7 +694,7 @@

    向游戏中注入dll

    为了向游戏中注入dll,我们要使用一个工具:DLL Injector
    官网链接:DLL Injector官网
    使用方法:
    ①打开DLL Injector

    ②选择要加载的DLL文件(有两种方法):

    @@ -703,7 +703,7 @@

  • @@ -713,14 +713,14 @@

    ④点击右上方的注入按钮将DLL注入目标进程,结束后会在指定文件夹生成usmap文件

    @@ -741,7 +741,7 @@

    推荐阅读 -
    +
    使用FModel提取《黑神话:悟空》的资产 使用FModel提取《黑神话:悟空》的资产 - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装 - + CentOS8系统换源安装(带GUI) CentOS8系统换源安装(带GUI)
    @@ -782,10 +782,10 @@

    推荐阅读

    -
    +
    使用FModel提取《黑神话:悟空》的资产 使用FModel提取《黑神话:悟空》的资产 - + 开源相机管理库Aravis学习——安装 开源相机管理库Aravis学习——安装
    @@ -798,7 +798,7 @@

    soname对于一个共享库文件,我们可以通过readelf -d命令查看其soname
    @@ -668,7 +668,7 @@

    推荐阅读

    -
    +
    共享库链接和加载时的路径搜索优先级 共享库链接和加载时的路径搜索优先级 - + 编译期链接时共享库路径搜索优先级实验 编译期链接时共享库路径搜索优先级实验 - + 运行期加载时共享库路径搜索优先级实验 运行期加载时共享库路径搜索优先级实验
    @@ -709,10 +709,10 @@

    推荐阅读

    -
    +
    共享库链接和加载时的路径搜索优先级 共享库链接和加载时的路径搜索优先级 - + 编译期链接时共享库路径搜索优先级实验 编译期链接时共享库路径搜索优先级实验
    @@ -725,7 +725,7 @@

    可以看到由于我们没有配置任何额外的搜索路径,并且没有在默认搜索路径下放置libhello.so文件,链接器就找不到相应的共享库文件,就会链接失败。

    @@ -652,14 +652,14 @@

    没有报错。

    然后使用readelf -d查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。

    LIBRARY_PATH

    创建路径/opt/hellolib,将libhello.so.1.1.0拷贝至/opt/hellolib,并在/opt/hellolib下创建一个软链接(libhello.so)指向它。然后将/opt/hellolib添加至LIBRARY_PATH并进行main.ohello的共享库文件的链接操作,查看是否可以链接成功。

    @@ -669,14 +669,14 @@

    没有报错。

    然后使用readelf -d查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。

    -L

    创建路径/opt/hellolib,将libhello.so.1.1.0拷贝至/opt/hellolib,并在/opt/hellolib下创建一个软链接(libhello.so)指向它,然后添加链接选项-L/opt/hellolib并进行链接操作,查看是否可以将main.ohello的共享库文件链接成功。

    @@ -686,14 +686,14 @@

    -L

    创建路径

    输出:

    没有报错。

    然后使用readelf -d查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。

    优先级测试

    默认路径和LIBRARY_PATH

    @@ -862,7 +862,7 @@

    - 阅读全文内存布局分析工具pahole的安装和使用  + 阅读全文内存布局分析工具pahole的安装和使用  @@ -929,14 +929,14 @@

    - + 使用FModel提取《黑神话:悟空》的资产
    - 阅读全文使用FModel提取《黑神话:悟空》的资产  + 阅读全文使用FModel提取《黑神话:悟空》的资产  @@ -1002,7 +1002,7 @@

    - 阅读全文运行期加载时共享库路径搜索优先级实验  + 阅读全文运行期加载时共享库路径搜索优先级实验  @@ -1068,7 +1068,7 @@

    - 阅读全文编译期链接时共享库路径搜索优先级实验  + 阅读全文编译期链接时共享库路径搜索优先级实验  @@ -1134,7 +1134,7 @@

    - 阅读全文使用update-alternatives管理GCC版本  + 阅读全文使用update-alternatives管理GCC版本  @@ -1200,7 +1200,7 @@

    - + 共享库soname机制

    @@ -1251,7 +1251,7 @@

    - 阅读全文共享库soname机制  + 阅读全文共享库soname机制  @@ -1266,7 +1266,7 @@

    - 阅读全文使用FModel提取UE4/5游戏资产  + 阅读全文使用FModel提取UE4/5游戏资产  diff --git a/page/2/index.html b/page/2/index.html index fbd2a66..f5cd77a 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -583,7 +583,7 @@

    - + log4cpp的安装及使用

    @@ -634,7 +634,7 @@

    - 阅读全文log4cpp的安装及使用  + 阅读全文log4cpp的安装及使用  @@ -649,7 +649,7 @@

    - 阅读全文共享库链接和加载时的路径搜索优先级  + 阅读全文共享库链接和加载时的路径搜索优先级  @@ -715,7 +715,7 @@

    - 阅读全文设计模式学习(二)工厂模式——抽象工厂模式+注册表  + 阅读全文设计模式学习(二)工厂模式——抽象工厂模式+注册表  @@ -781,7 +781,7 @@

    - 阅读全文设计模式学习(二)工厂模式——抽象工厂模式  + 阅读全文设计模式学习(二)工厂模式——抽象工厂模式  @@ -847,7 +847,7 @@

    - + jsoncpp的安装及使用

    @@ -898,7 +898,7 @@

    - 阅读全文jsoncpp的安装及使用  + 阅读全文jsoncpp的安装及使用  @@ -913,7 +913,7 @@

    - 阅读全文设计模式学习(二)工厂模式——工厂方法模式+注册表  + 阅读全文设计模式学习(二)工厂模式——工厂方法模式+注册表  @@ -979,7 +979,7 @@

    - 阅读全文设计模式学习(二)工厂模式——工厂方法模式  + 阅读全文设计模式学习(二)工厂模式——工厂方法模式  @@ -1045,7 +1045,7 @@

    - 阅读全文Centos8系统启动时间优化  + 阅读全文Centos8系统启动时间优化  @@ -1111,7 +1111,7 @@

    - 阅读全文设计模式学习(二)工厂模式——简单工厂模式  + 阅读全文设计模式学习(二)工厂模式——简单工厂模式  @@ -1177,7 +1177,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(七)——chunk-parser  + 阅读全文开源相机管理库Aravis例程学习(七)——chunk-parser  diff --git a/page/3/index.html b/page/3/index.html index 33b510c..23d5bef 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -583,7 +583,7 @@

    - 阅读全文Git学习——迁移单一仓库至其他代码托管平台  + 阅读全文Git学习——迁移单一仓库至其他代码托管平台  @@ -639,7 +639,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(六)——camera-features  + 阅读全文开源相机管理库Aravis例程学习(六)——camera-features  @@ -705,7 +705,7 @@

    - 阅读全文开源相机管理库Aravis学习——PixelFormat编码规则  + 阅读全文开源相机管理库Aravis学习——PixelFormat编码规则  @@ -771,7 +771,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(五)——camera-api  + 阅读全文开源相机管理库Aravis例程学习(五)——camera-api  @@ -837,7 +837,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal  + 阅读全文开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal  @@ -903,7 +903,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback  + 阅读全文开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback  @@ -969,7 +969,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread  + 阅读全文开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread  @@ -1035,7 +1035,7 @@

    - 阅读全文开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition  + 阅读全文开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition  @@ -1101,7 +1101,7 @@

    - 阅读全文C++智能指针学习——小谈引用计数  + 阅读全文C++智能指针学习——小谈引用计数  @@ -1167,7 +1167,7 @@

    - 阅读全文C++自用小轮子——单例模板  + 阅读全文C++自用小轮子——单例模板  diff --git a/page/4/index.html b/page/4/index.html index ab4e8a6..4167c3b 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -583,7 +583,7 @@

    - 阅读全文C++自用小轮子——线程安全队列  + 阅读全文C++自用小轮子——线程安全队列  @@ -649,7 +649,7 @@

    - 阅读全文开源相机管理库Aravis学习——安装  + 阅读全文开源相机管理库Aravis学习——安装  @@ -715,7 +715,7 @@

    - 阅读全文设计模式学习(一)单例模式补充——指令重排  + 阅读全文设计模式学习(一)单例模式补充——指令重排  @@ -781,7 +781,7 @@

    - 阅读全文设计模式学习(一)单例模式补充——单例模式析构  + 阅读全文设计模式学习(一)单例模式补充——单例模式析构  @@ -847,7 +847,7 @@

    - 阅读全文设计模式学习(一)单例模式的几种实现方式  + 阅读全文设计模式学习(一)单例模式的几种实现方式  @@ -913,7 +913,7 @@

    - 阅读全文I/O多路复用学习(二)do_select分析  + 阅读全文I/O多路复用学习(二)do_select分析  @@ -979,7 +979,7 @@

    - 阅读全文I/O多路复用学习(一)select  + 阅读全文I/O多路复用学习(一)select  @@ -1045,7 +1045,7 @@

    - + CentOS8系统网口桥接

    @@ -1096,7 +1096,7 @@

    - 阅读全文CentOS8系统网口桥接  + 阅读全文CentOS8系统网口桥接  @@ -1111,7 +1111,7 @@

    - 阅读全文CentOS8系统换源安装(带GUI)  + 阅读全文CentOS8系统换源安装(带GUI)  diff --git a/search.xml b/search.xml index 000b461..917503e 100644 --- a/search.xml +++ b/search.xml @@ -2,7 +2,7 @@ CentOS8系统换源安装(带GUI) - /Centos8-install/ + /2023/10/09/Centos8-install/ 最近在制作系统盘时发现使用ISO镜像文件安装CentOS 8系统时,在选择安装源时总是报错,显示错误的安装源。

    后来查了一下资料,了解到CentOS 8操作系统已经结束了生命周期,社区已不再维护该操作系统版本。按照社区规则,CentOS 8的源地址内容已移除,目前第三方的镜像站中均已移除CentOS 8的源。但由于个人业务需要,仍需使用CentOS 8系统,因此我们需要换源在线安装。

    系统安装方法

    确保网络连接

    由于安装过程是在线安装,因此需要确保设备连接了互联网,可使用ping操作验证:

    @@ -40,7 +40,7 @@
    GObject学习笔记(一)类和实例 - /GObject-tutorial-beginner-01/ + /2024/11/02/GObject-tutorial-beginner-01/ 前言

    最近阅读Aravis 源码,其中大量运用了GObject,于是打算学习一下。

    此系列笔记仅主要面向初学者,不会很深入探讨源码的细节,专注于介绍GObject的基本用法。

    此系列笔记参考GObject Tutorial for beginners

    @@ -111,7 +111,7 @@ GObject学习笔记(二)类型创建与注册 - /GObject-tutorial-beginner-02/ + /2024/11/05/GObject-tutorial-beginner-02/ 前言

    上一节中我们介绍了GObject类型的类和实例变量的创建和使用。GObject是一个基本的可实例化类类型,是所有使用GObject系统的类型的基类,提供了继承、封装、多态等面向对象的核心特性。不过我们一般不直接使用GObject本身,而是通过继承GObject来创建新的类型。

    @@ -296,43 +296,14 @@ GObject - - I/O多路复用学习(一)select - /IO-Multiplexing-select/ - 前言

    本文主要是简单讨论select的特点并梳理一下select的调用链,不涉及具体的使用方法。
    本文中涉及的源码均出自Linux内核5.4.0版本

    -

    简介

    select 是一种 IO 多路复用的技术,它允许单个进程或线程高效地管理多个输入/输出流,从而提高程序的性能和响应速度。

    -

    调用过程

    do_select

    select调用链
    select调用链

    -

    select的核心功能都在do_select中,里面的关键操作有:
    1、poll_initwait:初始化轮询等待队列结构体 (poll_wqueues) 的所有字段,包括函数指针的初始化,后面用于驱动中回调__pollwait
    2、for循环:遍历每个fd并调用vfs_pollvfs_poll针对传入的fd,调用其各自的poll函数(由设备驱动程序实现,比如sock_poll)。之后若满足一定条件则跳出循环。
    3、poll_freewait:清理等待队列。

    -

    后面出一篇文章详细讨论do_select()
    I/O多路复用学习(二)do_select分析

    -

    __pollwait

    __pollwait调用链
    __pollwait调用链

    -

    __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。pollwake会将进程或线程从睡眠状态唤醒。

    -

    select缺点

    1、select默认支持的描述符有限,为1024(可以通过命令ulimit -n 2048临时修改,也可以修改Linux内核头文件posix_types.h修改__FD_SETSIZE的值,但是要重新编译内核);
    2、每次调用需要把fd集合在内核态和用户态间拷贝传递两次,在fdset很大时效率低;
    3、select内部监视fd是通过轮询的方式,时间复杂度高,效率低;
    4、每次调用后需要重新设置fdset,麻烦。

    -

    参考文章

    1.select 机制 - 访问方式(三)

    -]]> - - Linux - Linux内核源码 - - I/O多路复用学习(二)do_select分析 - /IO-Multiplexing-do-select/ + /2024/03/08/IO-Multiplexing-do-select/ 前言

    前一篇笔记:I/O多路复用学习(一)select ,梳理了一下select的调用过程,这一篇笔记对其中的do_select函数的细节做一下简单的讨论。
    本文中涉及的源码均出自Linux内核5.4.0版本

    相关结构体

    了解相关的结构体可以帮助我们更好地理解select内部的实现
    相关结构体关系示意图:

    结构体关系图
    结构体关系图

    struct poll_wqueues

    poll_wqueues结构体(轮询等待队列)用于维护若干特定于文件描述符的设备驱动等待队列。但是它并不直接存储设备驱动等待队列本身;它通过inline_entries[]管理一个或多个poll_table_entry项,每个项与一个特定的文件描述符和其等待事件相关联。

    @@ -352,7 +323,7 @@

    do_select

    do_select流程图
    do_select流程图

    代码省略了一些,比如超时标志位timed_out的置位操作等。

    @@ -388,7 +359,7 @@

    __pollwait

    pollwait
    pollwait

    __pollwait用于处理轮询时的等待队列。这个函数通过创建并初始化poll_table_entry实例,将等待进程(或线程)添加到指定设备或文件的等待队列中。

    @@ -409,7 +380,7 @@

    POLL_TABLE_FULL为true情况的示意图:

    POLL_TABLE_FULL为真
    POLL_TABLE_FULL为真

    init_waitqueue_func_entry

    init_waitqueue_func_entry用于初始化wait_queue_entry实例的各个成员。

    @@ -423,7 +394,7 @@

    pollwake

    pollwake
    pollwake

    回调过程

    前面提到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

    @@ -431,6 +402,35 @@

    pollwake调用链

    pollwake->__pollwake->default_wake_function->try_to_wake_up。最终调用try_to_wake_up唤醒当前进程。

    总结

    本文讨论了do_select函数调用链上的一些实现细节,梳理了相关结构体之间的关系。有些地方可能解释的不是很详细,后面持续更新。

    参考文章

    1.select 机制 - 访问方式(三)

    +]]> + + Linux + Linux内核源码 + + + + I/O多路复用学习(一)select + /2024/03/05/IO-Multiplexing-select/ + 前言

    本文主要是简单讨论select的特点并梳理一下select的调用链,不涉及具体的使用方法。
    本文中涉及的源码均出自Linux内核5.4.0版本

    +

    简介

    select 是一种 IO 多路复用的技术,它允许单个进程或线程高效地管理多个输入/输出流,从而提高程序的性能和响应速度。

    +

    调用过程

    do_select

    select调用链
    select调用链

    +

    select的核心功能都在do_select中,里面的关键操作有:
    1、poll_initwait:初始化轮询等待队列结构体 (poll_wqueues) 的所有字段,包括函数指针的初始化,后面用于驱动中回调__pollwait
    2、for循环:遍历每个fd并调用vfs_pollvfs_poll针对传入的fd,调用其各自的poll函数(由设备驱动程序实现,比如sock_poll)。之后若满足一定条件则跳出循环。
    3、poll_freewait:清理等待队列。

    +

    后面出一篇文章详细讨论do_select()
    I/O多路复用学习(二)do_select分析

    +

    __pollwait

    __pollwait调用链
    __pollwait调用链

    +

    __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。pollwake会将进程或线程从睡眠状态唤醒。

    +

    select缺点

    1、select默认支持的描述符有限,为1024(可以通过命令ulimit -n 2048临时修改,也可以修改Linux内核头文件posix_types.h修改__FD_SETSIZE的值,但是要重新编译内核);
    2、每次调用需要把fd集合在内核态和用户态间拷贝传递两次,在fdset很大时效率低;
    3、select内部监视fd是通过轮询的方式,时间复杂度高,效率低;
    4、每次调用后需要重新设置fdset,麻烦。

    +

    参考文章

    1.select 机制 - 访问方式(三)

    ]]> Linux @@ -439,7 +439,7 @@ 使用FModel提取《黑神话:悟空》的资产 - /UE-fmodel-blackmyth-wukong/ + /2024/08/21/UE-fmodel-blackmyth-wukong/ 前言

    黑神话悟空昨天上线了,解个包looklook。

    • 本文内容比较简洁,仅介绍解包黑神话所需的专项配置,关于FModel的基础使用流程,请见《使用FModel提取UE4/5游戏资产》
    • @@ -460,75 +460,75 @@

      先看下咱猴哥的英俊面庞:

      然后看下四大天王

      持国天王:

      多闻天王

      广目天王

      增长天王

      最后再看个小狗:

      闲聊

      通过解包可以发现,黑神话里面的资源命名都是使用汉语拼音而非英文单词,不知道这是不是国内行业普遍使用的规矩,希望有从事相关行业的大佬能解开我这个疑惑。

      至于为啥不使用英文,我猜一是有些中文词汇很难用英语翻译,强行翻译可能会词不达意;二是有些翻译会导致命名长度太长。

      但是哥们儿,下面这个首字母简写我个人认为确实有一点点抽象了,能全部猜出来的也算是神人了。

      由于一周目才刚开始打,我现在只能猜出来一部分:GYCY(观音禅院),HFM(黑风庙?),HGS(花果山),HYS(火焰山),LSH(流沙河?),LYS(雷音寺),MGD(埋骨地?),PSD(盘丝洞)。

      另外,通过解包我们还可以看见项目中有刘备,孙权,太史慈的人物建模,难道是《黑神话2:三国群英传》?😂

      项目里甚至还有一个电饭煲的模型hhh。讲道理,我孙悟空用电饭煲封印个比克大魔王,也是很合理的好吧。

      不过上述资源的存在让我想到了一张梗图:

      可能遇到的问题

      没有相应的UE引擎版本选项

      问题描述:在设置UE引擎版本时,下拉菜单中可能会没有GAME_BlackMythWukong (UE 5.0)这个选项。
      解决方法:在settings的update mode选项中选择QA Testing,然后软件会提示你更新,然后一路确定。待更新完成后重新打开软件,即可在UE Version中看到GAME_BlackMythWukong (UE 5.0)这个选项

      映射文件生成失败

      由于映射文件生成比较复杂,在此提供现成的映射文件(伴随游戏更新映射文件可能失效,失效请联系我更换):

      @@ -540,7 +540,7 @@ 使用FModel提取UE4/5游戏资产 - /UE-fmodel-usage/ + /2024/07/16/UE-fmodel-usage/ 前言

      这篇文章仅记录我作为初学者使用FModel工具提取某款游戏模型的过程。

      FModel简介

      FModel是一个开源软件,可以用于查看和提取UE4-5项目中的资产。它支持从.pak.uasset文件中提取内容,如3D模型,纹理,音频等。

      FModel安装

      首先进入FModel官网 ,下载最新的发布版本。

      @@ -548,44 +548,44 @@

      FModel还需要依赖.NET,如果你的系统中没有,则会提示你安装:

      安装好.NET之后,重新打开FModel.exe,此时软件可以正常打开。

      FModel使用

      初次使用

      ①软件初次打开会弹出这个界面

      这里可以随便设置,之后进入软件还可以更改。
      ②点击ok进入软件
      ③设置填写AES key
      这里填写你要解包的游戏对应的AES key

      ④设置选择UE版本和填写游戏路径

      解包
      全选(我在使用时是这样,可能会因游戏的不同而不一样)然后load

      然后就会自动跳到Folder下,此时我们就可以随便选择一个资产双击进行查看

      注意:
      如果你要解包的游戏是使用UE5开发的,那么在解包的时候可能会报错:

      @@ -595,14 +595,14 @@

      比如3D Viewer的模型预览是这样

      资产导出

      SettingsModels中设置资产的导出格式(比如对于mesh可以设置导出为.glbtexture可以设置导出为.png

      然后在你要导出的资产上右键,选择要导出的类型即可:

      附录

      dumper

      对于UE5+的游戏来说,具有unversioned属性的包是意料之中的。然而现在有一些解决方案可以让FModel支持对它们的解析。

      @@ -612,7 +612,7 @@

      当成功生成了*.usmap文件后,你可以在FModel软件的设置界面将其添加到Mapping File Path中:

      下面我将演示通过使用Dumper-7生成*.usmap的过程

      @@ -620,7 +620,7 @@

      向游戏中注入dll

      为了向游戏中注入dll,我们要使用一个工具:DLL Injector
      官网链接:DLL Injector官网
      使用方法:
      ①打开DLL Injector

      ②选择要加载的DLL文件(有两种方法):

      @@ -629,7 +629,7 @@
    • 拖拽你的dll文件到DLL injector软件的界面中
    @@ -639,14 +639,14 @@
  • 如果使用筛选功能最后只筛选出一个进程,则此进程会被自动选中
  • ④点击右上方的注入按钮将DLL注入目标进程,结束后会在指定文件夹生成usmap文件

    ]]>
    @@ -657,7 +657,7 @@
    GVCP数据包 - /aravis-GVCP-packets/ + /2024/11/01/aravis-GVCP-packets/ 简述

    设备通过监听GVCP端口(3956)等待GVCP数据包。当客户端(如:PC设备)向设备(如:相机)发送一个GVCP命令数据包时,设备会回复一个确认数据包。每个命令/确认对(command/acknowledge pair)都使用一个16 bit的值来标识(就相当于ID),这样可以检查确认数据包是否与之前发送的命令对应。0是这个标识符的错误值。如果在给定的超时时间后没有收到确认,将再次发送命令数据包,直到达到最大重试次数。

    ArvGvcp API提供了一系列处理GVCP包的函数。

    GVCP数据包的内容由一个64 bit的头部组成,头部后面可能跟着一个数据字节数组(也可能没有,具体取决于GVCP数据包的类型)。多字节值采用大端编码。

    @@ -933,7 +933,7 @@
    开源相机管理库Aravis例程学习(五)——camera-api - /aravis-camera-api/ + /2024/04/28/aravis-camera-api/ 简介

    本文针对官方例程 中的:03-camera-api做简单的讲解。并介绍其中调用的arv_camera_get_regionarv_camera_get_pixel_format_as_stringarv_camera_get_pixel_formatARV_PIXEL_FORMAT_BIT_PER_PIXEL

    aravis版本:0.8.31
    操作系统:ubuntu-20.04
    gcc版本:9.4.0

    例程代码

    这段代码使用Aravis的API,获取相机的一些基本设置,如图像的宽度、高度和像素格式,主要操作步骤如下:

    @@ -949,7 +949,7 @@

    运行结果:

    函数说明

    arv_camera_get_region

    简介:用于获取相机当前的感兴趣区域(ROI),此函数会将当前相机的ROI的位置坐标(x,y)和尺寸(width,height)通过指针返回,并记录错误信息。

    @@ -974,7 +974,7 @@
    开源相机管理库Aravis例程学习(六)——camera-features - /aravis-camera-features/ + /2024/05/01/aravis-camera-features/ 简介

    本文针对官方例程 中的:04-camera-features做简单的讲解。并介绍其中调用的arv_camera_get_integerarv_camera_get_string

    aravis版本:0.8.31
    操作系统:ubuntu-20.04
    gcc版本:9.4.0

    例程代码

    这段代码使用Aravis的API,获取相机的一些基本设置,如图像的宽度、高度和像素格式,主要操作步骤如下:

    @@ -993,7 +993,7 @@

    运行结果:

    函数说明

    arv_camera_get_integer

    简介:获取已连接相机的一个整数型特性的值

    @@ -1012,7 +1012,7 @@
    开源相机管理库Aravis例程学习(七)——chunk-parser - /aravis-chunk-parser/ + /2024/05/10/aravis-chunk-parser/ 简介

    本文针对官方例程 中的:05-chunk-parser做简单的讲解。并介绍其中调用的arv_camera_create_chunk_parserarv_camera_set_chunksarv_chunk_parser_get_integer_value函数。

    aravis版本:0.8.31
    操作系统:ubuntu-20.04
    gcc版本:9.4.0

    例程代码

    这段代码使用Aravis的API,操作相机捕获图像并获取流数据中附加的块信息(例程中启用的块数据为图像的长和宽),主要操作步骤如下:

    @@ -1056,7 +1056,7 @@
    开源相机管理库Aravis学习——安装 - /aravis-install/ + /2024/03/30/aravis-install/ 前言

    最近在做采集软件的开发,由于我自己使用过Huaray和Basler两个品牌的相机,所以在设计软件时尝试设计统一的接口去控制不同品牌和型号相机的相同或类似的行为。当然,我的设计思路都是建立在调用各品牌SDK的基础上。
    后来我去外网搜索,发现了一个开源项目Aravis,它通过提供一个通用的API,让我们能够不受相机的品牌或型号限制,自由地进行图像采集和相机控制,支持从简单的图像捕获到复杂的相机设置调整的一系列操作。

    项目地址:https://github.com/AravisProject/aravis

    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双击打开,即可查看已连接的相机设备

    安装过程中遇到的问题

    meson版本过低

    我安装的aravis版本是0.8.31,这个版本aravis的编译依赖0.57.0及以上版本的meson,而我通过apt包管理器下载的meson版本为0.53.2,不满足需求。因此要对meson进行升级操作:

    ①首先安装或升级pip

    @@ -1111,13 +1111,13 @@

    我的安装结果:

    CMake版本过低

    在构建项目的时候,提示了以下错误信息:

    说明我系统中的CMake版本过低,需要升级。

    @@ -1138,7 +1138,7 @@

    缺少GStreamer组件

    直接使用包管理器安装即可

    @@ -1153,7 +1153,7 @@
    开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback - /aravis-multiple-acquisition-callback/ + /2024/04/15/aravis-multiple-acquisition-callback/ 简介

    本文针对官方例程 中的: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 @@
    开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread - /aravis-multiple-acquisition-main-thread/ + /2024/04/15/aravis-multiple-acquisition-main-thread/ 简介

    本文针对官方例程 中的:02-multiple-acquisition-main-thread做简单的讲解,并简单介绍其中调用的arv_camera_set_acquisition_mode arv_camera_create_streamarv_camera_get_payloadarv_buffer_new arv_stream_push_bufferarv_camera_start_acquisitionarv_stream_pop_bufferarv_camera_stop_acquisition函数。

    aravis版本:0.8.31
    操作系统:ubuntu-20.04
    gcc版本:9.4.0

    例程代码

    这段代码使用Aravis的API,控制相机连续采集,并在主线程中从缓冲区获取前10帧图像(假设不丢帧),主要操作步骤如下:

    @@ -1213,7 +1213,7 @@

    运行结果:

    函数说明

    arv_camera_set_acquisition_mode

    简介:设置相机的采集模式

    @@ -1254,7 +1254,7 @@
    开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal - /aravis-multiple-acquisition-signal/ + /2024/04/25/aravis-multiple-acquisition-signal/ 简介

    本文针对官方例程 中的:02-multiple-acquisition-signal做简单的讲解。并简单介绍其中调用的g_main_loop_newg_main_loop_rung_main_loop_quitg_signal_connectarv_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 @@

    Q&A

    回调函数的同步调用与异步调用

    观察程序运行时的日志,可以发现new_buffer_cb的运行并不是在主线程中。

    但是按照g_signal_connect的描述,回调函数应该是被同步调用,也就是说new_buffer_cb理论上应该在主线程被调用。
    后来查看文档发现,在GObject的信号系统中,处理器的调用是同步的。当信号发射时,其关联的所有处理器会都会在发射信号的线程中按照它们被连接的顺序依次执行。

    @@ -1312,7 +1312,7 @@
    开源相机管理库Aravis学习——PixelFormat编码规则 - /aravis-pixelformat/ + /2024/04/28/aravis-pixelformat/ 前言

    在学习Aravis官方例程 的时候,有这么一个函数:arv_camera_get_pixel_format,它的返回类型是ArvPixelFormat(本质是个32位无符号整数)。这意味着对于每个图像数据格式,都有自己对应的唯一的编码。我比较好奇Aravis是通过什么规则对各种图像数据格式进行的编码,于是就查看了源码。

    本文主要讨论Aravis中对不同图像数据格式的编码规则。

    前置知识

    PixelFormat

    PixelFormat指的是图像中每个像素的数据格式,它定义了图像中像素的组成方式、颜色信息的存储方法和每个像素所占的位数等。这一格式对于图像的处理和显示是非常关键的,因为它直接影响到图像的质量和处理的效率。

    @@ -1347,14 +1347,14 @@
    开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition - /aravis-single-acquisition/ + /2024/04/10/aravis-single-acquisition/ 简介

    本文针对官方例程 中的第一个例程:single-acquisition做简单的讲解,并简单分析其中调用的arv_camera_new arv_camera_acquisitionarv_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 */

    /* Aravis header */

    #include <arv.h>

    /* Standard headers */

    #include <stdlib.h>
    #include <stdio.h>

    /*
    * Connect to the first available camera, then acquire a single buffer.
    */

    int main (int argc, char **argv)
    {
    ArvCamera *camera;
    ArvBuffer *buffer;
    GError *error = NULL;

    /* Connect to the first available camera */
    camera = arv_camera_new (NULL, &error);
    //camera = arv_camera_new ("192.168.6.23", &error);

    if (ARV_IS_CAMERA (camera))
    {
    printf ("Found camera '%s'\n", arv_camera_get_model_name (camera, NULL));

    /* Acquire a single buffer */
    buffer = arv_camera_acquisition (camera, 0, &error);

    if (ARV_IS_BUFFER (buffer))
    {
    /* Display some informations about the retrieved buffer */
    printf ("Acquired %d×%d buffer\n",
    arv_buffer_get_image_width (buffer),
    arv_buffer_get_image_height (buffer));
    /* Destroy the buffer */
    g_clear_object (&buffer);
    }

    /* Destroy the camera instance */
    g_clear_object (&camera);
    }

    if (error != NULL)
    {
    /* An error happened, display the correspdonding message */
    printf ("Error: %s\n", error->message);
    return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
    }

    此例程较为简单,每一步的细节查看注释即可,此处不过多讲解。
    运行结果:

    函数说明

    arv_camera_new

    简介:创建一个ArvCamera对象,如果name是NULL,则连接第一个可用的相机。

    @@ -1397,7 +1397,7 @@
    C++自用小轮子——单例模板 - /cpp-development-components-singleton-CRTP/ + /2024/04/03/cpp-development-components-singleton-CRTP/ 简介

    记录开发时自用的小轮子:单例模板。

    单例模板

    提供了一个基于模板的单例模式的实现,代码如下:

    #ifndef SINGLETON_H
    #define SINGLETON_H

    template <typename T>
    class CSingleton
    {
    public:
    static T& GetInstance()
    {
    static T instance;
    return instance;
    }
    protected:
    CSingleton() {}

    ~CSingleton() {}
    CSingleton(const CSingleton&) = delete;
    CSingleton& operator=(const CSingleton&) = delete;
    };

    #endif // SINGLETON_H
    @@ -1411,7 +1411,7 @@
    C++自用小轮子——线程安全队列 - /cpp-development-components-threadsafe-queue/ + /2024/04/03/cpp-development-components-threadsafe-queue/ 简介

    记录开发时自用的小轮子:线程安全队列

    线程安全队列

    #ifndef THREADSAFEQUEUE_H
    #define THREADSAFEQUEUE_H

    #include <iostream>
    #include <queue>
    #include <mutex>
    #include <condition_variable>
    #include <memory>

    template<typename T>
    class ThreadSafeQueue
    {
    public:
    ThreadSafeQueue();
    ThreadSafeQueue(const ThreadSafeQueue& other);
    ThreadSafeQueue& operator=(const ThreadSafeQueue& other);
    ~ThreadSafeQueue();
    public:
    void push(T new_value);

    //对于pop方法,std::queue中的pop只负责弹出元素,不返回元素
    //这里为了接口简化,设计为在pop的同时返回弹出的元素
    //timeout为超时时间,单位为毫秒
    //默认阻塞pop
    bool wait_and_pop(T& value, std::chrono::milliseconds timeout_millisecond = std::chrono::milliseconds::max());
    std::shared_ptr<T> wait_and_pop(std::chrono::milliseconds timeout_millisecond = std::chrono::milliseconds::max());

    bool empty() const;
    size_t size() const;

    T& front();
    const T& front() const;
    std::shared_ptr<T> front_ptr();

    private:
    mutable std::mutex mtx_;
    std::queue<T> data_queue_;
    std::condition_variable cv_data_;
    };

    template<typename T>
    ThreadSafeQueue<T>::ThreadSafeQueue()
    {
    }

    template<typename T>
    ThreadSafeQueue<T>::~ThreadSafeQueue()
    {
    }

    template<typename T>
    ThreadSafeQueue<T>::ThreadSafeQueue(const ThreadSafeQueue& other)
    {
    std::lock_guard<std::mutex> lock(other.mtx_);
    data_queue_ = other.data_queue_;
    }

    template<typename T>
    ThreadSafeQueue<T>& ThreadSafeQueue<T>::operator=(const ThreadSafeQueue& other)
    {
    if (this == &other)
    {
    return *this;
    }
    //同时锁定两个互斥量,防止死锁
    std::lock(mtx_, other.mtx_);
    //领养锁,因为已经锁定了
    std::lock_guard<std::mutex> self_lock(mtx_, std::adopt_lock);
    std::lock_guard<std::mutex> other_lock(other.mtx_, std::adopt_lock);
    data_queue_ = other.data_queue_;
    return *this;
    }

    template<typename T>
    void ThreadSafeQueue<T>::push(T new_value)
    {
    std::lock_guard<std::mutex> lock(mtx_);
    data_queue_.push(std::move(new_value));
    cv_data_.notify_one();
    }

    template<typename T>
    bool ThreadSafeQueue<T>::wait_and_pop(T& value, std::chrono::milliseconds timeout_millisecond)
    {
    std::unique_lock<std::mutex> lock(mtx_);
    if (data_queue_.empty())
    {
    if (timeout_millisecond == std::chrono::milliseconds::max())
    {
    cv_data_.wait(lock, [this] {return !data_queue_.empty();});
    }
    else
    {
    if (!cv_data_.wait_for(lock, timeout_millisecond, [this] {return !data_queue_.empty();}))
    return false;
    }
    }
    value = std::move(data_queue_.front());
    data_queue_.pop();
    return true;
    }

    template<typename T>
    std::shared_ptr<T> ThreadSafeQueue<T>::wait_and_pop(std::chrono::milliseconds timeout_millisecond)
    {
    std::unique_lock<std::mutex> lock(mtx_);
    if (data_queue_.empty())
    {
    if (timeout_millisecond == std::chrono::milliseconds::max())
    {
    cv_data_.wait(lock, [this] {return !data_queue_.empty();});
    }
    else
    {
    if (!(cv_data_.wait_for(lock, timeout_millisecond, [this] {return !data_queue_.empty();})))
    return std::shared_ptr<T>();
    }
    }
    std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue_.front())));
    data_queue_.pop();
    return res;
    }

    template<typename T>
    bool ThreadSafeQueue<T>::empty() const
    {
    std::lock_guard<std::mutex> lock(mtx_);
    return data_queue_.empty();
    }

    template<typename T>
    size_t ThreadSafeQueue<T>::size() const
    {
    std::lock_guard<std::mutex> lock(mtx_);
    return data_queue_.size();
    }

    template<typename T>
    T& ThreadSafeQueue<T>::front()
    {
    std::lock_guard<std::mutex> lock(mtx_);
    if(data_queue_.empty())
    throw std::runtime_error("queue is empty");
    return data_queue_.front();
    }

    template<typename T>
    const T& ThreadSafeQueue<T>::front() const
    {
    std::lock_guard<std::mutex> lock(mtx_);
    if(data_queue_.empty())
    throw std::runtime_error("queue is empty");
    return data_queue_.front();
    }

    template<typename T>
    std::shared_ptr<T> ThreadSafeQueue<T>::front_ptr()
    {
    std::lock_guard<std::mutex> lock(mtx_);
    if(data_queue_.empty())
    return std::shared_ptr<T>();
    return std::make_shared<T>(data_queue_.front());
    }

    #endif // THREADSAFEQUEUE_H
    ]]>
    @@ -1422,7 +1422,7 @@
    编译期链接时共享库路径搜索优先级实验 - /cpp-shared-lib-search-priority-test-linking/ + /2024/07/29/cpp-shared-lib-search-priority-test-linking/ 前言

    《共享库链接和加载时的路径搜索优先级》 中提到,使用g++时,共享库在编译期链接时的库路径搜索优先级为:-L指定的路径>LIBRARY_PATH记录的路径>默认路径

    本实验分三步验证上述结论
    ①单独测试每种方法指定的路径的可行性
    ②对比测试三种方法间的优先级
    ③使用DEBUG模式,查看链接器输出的详细信息,二次验证上述结论

    值得注意的是,我看网上都说LIBRARY_PATH指定的路径优先级要大于默认路径的优先级,但是就我的测试结果来看,结论是相反的(可能是我使用了g++而不是直接使用底层的ld?)。

    @@ -1447,7 +1447,7 @@

    输出:

    可以看到由于我们没有配置任何额外的搜索路径,并且没有在默认搜索路径下放置libhello.so文件,链接器就找不到相应的共享库文件,就会链接失败。

    @@ -1459,14 +1459,14 @@

    输出:

    没有报错。

    然后使用readelf -d查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。

    LIBRARY_PATH

    创建路径/opt/hellolib,将libhello.so.1.1.0拷贝至/opt/hellolib,并在/opt/hellolib下创建一个软链接(libhello.so)指向它。然后将/opt/hellolib添加至LIBRARY_PATH并进行main.ohello的共享库文件的链接操作,查看是否可以链接成功。

    @@ -1476,14 +1476,14 @@

    输出:

    没有报错。

    然后使用readelf -d查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。

    -L

    创建路径/opt/hellolib,将libhello.so.1.1.0拷贝至/opt/hellolib,并在/opt/hellolib下创建一个软链接(libhello.so)指向它,然后添加链接选项-L/opt/hellolib并进行链接操作,查看是否可以将main.ohello的共享库文件链接成功。

    @@ -1493,14 +1493,14 @@

    输出:

    没有报错。

    然后使用readelf -d查看可执行文件的动态段信息,可见链接成功,共享库的soname已经被写入到可执行文件的动态段信息中了。

    优先级测试

    默认路径和LIBRARY_PATH

      @@ -1514,13 +1514,13 @@

      输出:

      然后使用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。证明了编译过程中链接时库搜索路径的优先级为

      @@ -1634,7 +1634,7 @@ 运行期加载时共享库路径搜索优先级实验 - /cpp-shared-lib-search-priority-test-loading/ + /2024/08/01/cpp-shared-lib-search-priority-test-loading/ 前言

      《共享库链接和加载时的路径搜索优先级》 中提到,共享库在运行期加载时的路径搜索优先级为:
      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

      @@ -1726,7 +1726,7 @@
      共享库链接和加载时的路径搜索优先级 - /cpp-shared-lib-search-priority/ + /2024/07/15/cpp-shared-lib-search-priority/ 前言

      在开发一个新项目时遇到了共享库冲突的问题,因此在这里记录一下共享库的链接和加载过程中库路径的搜索优先级的相关知识。

      共享库的链接

      现在有一个main.o可重定位目标文件,其中需要用到开源库log4cpp。在链接的时候,我们可以这样链接:

      g++ main.o -o a.out -L/path/to/libs -llog4cpp
      @@ -1752,7 +1752,7 @@
      共享库soname机制 - /cpp-shared-lib-soname/ + /2024/07/16/cpp-shared-lib-soname/ 前言

      在使用第三方库时,我们会发现第三方库会提供一组文件,他们的后缀一般是.so(如libname.so),.so.x.so.x.y.z。本文讨论他们之间的关系。

      共享库版本号

      共享库一般会由于修复bug或增加接口等原因不断更新,有些更新是向下兼容的,有些则不是。一旦不向下兼容,那么当共享库更新后,依赖该库的程序将无法运行,需要重新编译。

      为了避免上述情况,就要对共享库进行版本控制。根据更新内容的不同可以划分不同的版本号:

      @@ -1787,7 +1787,7 @@
    • 对于一个共享库文件,我们可以通过readelf -d命令查看其soname
    @@ -1809,13 +1809,13 @@
    设计模式学习(二)工厂模式——抽象工厂模式+注册表 - /design-pattern-2-abstract-factory-reflect/ + /2024/07/09/design-pattern-2-abstract-factory-reflect/ 前言

    上一篇文章中 我们提到了抽象工厂模式初版代码的一些缺点:①客户端违反开闭原则②提供方违反开闭原则。本文将针对这两点进行讨论

    使用简单工厂改进

    对于缺点①,我们可以使用简单工厂的思路来改进抽象工厂的初版代码。对于上一篇文章中的例子,我们去除CameraFactoryBaslerCameraFactorySickCameraFactory,取而代之的是SimpleFactory类。

    类图如下:

    代码如下:

    @@ -1837,12 +1837,12 @@
    设计模式学习(二)工厂模式——抽象工厂模式 - /design-pattern-2-abstract-factory/ + /2024/07/05/design-pattern-2-abstract-factory/ 背景

    现在我需要开发一个相机操作模块,它可能在Windows下运行,也可能在Linux下运行。由于在厂家提供的SDK中,Windows下的SDK和Linux下的SDK是有区别的,因此对于一个品牌的相机,我们要创建两个类去封装这两个不同平台下的API。

    我们先使用工厂方法模式去设计(以Basler相机为例),类图如下:

    对应的代码(就不用智能指针了,要不然类图不好画):

    @@ -1860,7 +1860,7 @@

    AbstractProductAAbstractProductB是两个抽象产品,之所以为抽象,是因为他们可能有多种不同的实现,就刚才的例子来说,抽象产品就是BaslerCameraSickCameraProductA1ProductA2ProductB1ProductB2就是对两个抽象产品的具体分类的实现,对应例子中的LinuxBaslerCameraWindowsBaslerCameraLinuxSickCameraWindowsSickCamera

    @@ -1890,11 +1890,11 @@
    设计模式学习(二)工厂模式——工厂方法模式+注册表 - /design-pattern-2-factory-method-reflect/ + /2024/06/03/design-pattern-2-factory-method-reflect/ 工厂方法模式的瑕疵

    前一篇笔记 中我们介绍了工厂方法模式,示例的类图如下:

    前文中提到

    @@ -1919,7 +1919,7 @@
    设计模式学习(二)工厂模式——工厂方法模式 - /design-pattern-2-factory-method/ + /2024/05/17/design-pattern-2-factory-method/ 前言

    前一篇文章 介绍了简单工厂模式,提到了简单工厂模式的缺点(违反开闭原则,扩展困难),本文要介绍的工厂方法模式在一定程度上弥补了简单工厂模式的缺点。

    工厂方法模式

    简介

    工厂方法模式是创建型设计模式之一,它在抽象工厂类中声明创建对象的接口,在具体工厂类中实现具体的实例化过程。这个模式的核心思想是将对象的实例化延迟到子类中进行。

    这样的话,当要添加一个具体产品时,我们不会修改原有的工厂类(对修改封闭),而是新创建一个关联于具体产品的具体工厂类(对扩展开放)。

    @@ -1928,7 +1928,7 @@

    UML类图如下:

    代码如下:

    @@ -1956,7 +1956,7 @@
    设计模式学习(二)工厂模式——简单工厂模式 - /design-pattern-2-simple-factory/ + /2024/05/14/design-pattern-2-simple-factory/ 前言

    工厂模式是一种常用的设计模式,属于创建型模式之一。它的主要目的是为了解耦组件之间的依赖关系。通过使用工厂模式,系统中的具体类的实例化过程可以被抽象出来,从而使得系统更加模块化,增强了系统的可维护性和可扩展性。

    工厂模式可以分为三种类型:简单工厂模式,工厂方法模式和抽象工厂模式。本文先讨论简单工厂模式。

    背景

    在工业相机领域,多个品牌如Basler、Sick、Huaray等,各自提供了控制相机的API。本文中我们按照提供方和使用方两个角色来进行分析,以便更好地理解工厂模式的优点:

    @@ -1977,7 +1977,7 @@

    UML类图如下:

    代码如下:

    @@ -2006,7 +2006,7 @@
    Git学习——迁移单一仓库至其他代码托管平台 - /git-code-migration/ + /2024/05/10/git-code-migration/ 简介

    因需迁移单一代码仓库至其他代码托管平台,要迁移的包括仓库内容以及所有历史记录和推送日志。

    本文中的方法同样适用于在同一代码托管平台中克隆仓库。

    流程

    1. 创建新仓库:
    在目的平台的指定位置创建一个新的仓库(目的仓库),用于接收克隆的数据。

    @@ -2027,7 +2027,7 @@
    Centos8系统启动时间优化 - /linux-centos-boot-time-optimization/ + /2024/05/16/linux-centos-boot-time-optimization/ 前言

    因需要,对Centos8操作系统的启动时间进行优化。

    初始化系统

    Centos8使用了systemd作为系统和服务管理器。systemd是现代Linux发行版中普遍采用的初始化系统,它负责在启动时初始化系统环境并管理系统服务。我们在分析操作系统的启动过程时可以使用systemdsystemctl相关的命令。

    启动阶段

    首先查看操作系统启动过程中各阶段的耗时信息:

    @@ -2076,7 +2076,7 @@
  • 分析服务启动时间并禁用非必要的服务:使用systemd-analyze blame查看启动过程中每个服务的耗时,根据具体情况确定哪些服务是非常耗时且非必要的,使用systemctl disable xxxservicenamexxx禁用这些服务以加速启动过程。

  • @@ -2097,7 +2097,7 @@
    使用update-alternatives管理GCC版本 - /linux-update-alternatives/ + /2024/07/29/linux-update-alternatives/ 简介

    当操作系统中存在多个版本的GCC时,可以使用使用update-alternatives管理默认使用的编译器版本。

    本文使用gcc-9gcc-11做演示,操作系统为ubuntu-20.04

    操作过程

    ①使用以下命令确认gcc已正确安装

    @@ -2107,7 +2107,7 @@

    没有配置过应该输出如下:

    ③添加版本到update-alternatives

    @@ -2118,7 +2118,7 @@

    执行此命令后,系统将提供一个选择列表,我们可以选择默认的gcc版本

    我这里优先级最大的gcc-11被自动选为了默认版本,我们直接输入回车选择它就可以(具体的要根据你的实际情况进行选择)

    @@ -2127,7 +2127,7 @@

    ]]>
    @@ -2138,7 +2138,7 @@
    CentOS8系统网口桥接 - /network-bridging/ + /2024/03/04/network-bridging/ 简介

    因需要,对主板两个网口进行桥接。

    操作环境

    主板型号:Gigabyte GA-IMB370TN
    操作系统:CentOS 8.2

    桥接步骤

    清除旧配置

    先备份旧的网络配置文件,然后将其清除。
    网络配置文件的位置如下:

    @@ -2146,14 +2146,14 @@

    网络配置文件:

    网络配置文件
    网络配置文件

    查看网口名称

    nmcli con show

    我的两个网口名称分别为:eno1和enp3s0

    网口名称
    网口名称

    建立网桥

    创建一个新的桥接连接,其名为br0,设置其ipv4地址为:xxx.xxx.xxx.xxx(如192.168.23.23),子网掩码为:255.255.255.0,并设置为自动连接。
    br0将eno1和enp3s0作为从属接口,这允许两个网络接口(eno1和enp3s0)在同一个桥接网络内可以相互通信,同时这个桥接还可以与其他设备或网络通信

    @@ -2161,19 +2161,19 @@

    操作结果:

    桥接操作结果
    桥接操作结果

    此时,网络配置文件有三个:
    网络配置文件2
    网络配置文件2

    查看是否桥接成功

    nmcli con show

    在这里插入图片描述
    在这里插入图片描述

    当然也可以使用另一台设备去连接这两个网口,若使用两个网口都可以连接到设置的ip:xxx.xxx.xxx.xxx。则证明操作成功。

    至此网络桥接已操作完成

    @@ -2185,7 +2185,7 @@
    C++智能指针学习——小谈引用计数 - /reference-counting/ + /2024/04/07/reference-counting/ 前言

    本文结合源码讨论std::shared_ptr和std::weak_ptr的部分底层实现,然后讨论引用计数,弱引用计数的创建和增减。
    文章中尽可能的先阐述原理,然后再贴上代码。如果有不想看代码的,直接略过代码即可。
    本文涉及的源码均出自gcc 9.4.0版本

    控制块简介

    控制块是shared_ptrweak_ptr中的重要组成,主要用于管理资源的引用计数和生命周期。这个机制允许智能指针安全地共享和管理同一个对象,同时自动释放不再需要的资源。

    控制块包含以下部分:

    @@ -2200,7 +2200,7 @@

    示意图大概长这样:

    引用计数与弱引用计数创建过程

    在谈引用计数和弱引用计数的创建时,其实就是讨论控制块的创建。

    @@ -2295,7 +2295,7 @@
    设计模式学习(一)单例模式补充——单例模式析构 - /singleton-destruct/ + /2024/03/19/singleton-destruct/ 前言

    《单例模式学习》 中提到了,在单例对象是通过new关键字动态分配在堆上的情况下,当程序退出时,不会通过C++的RAII机制自动调用其析构函数。本文讨论一下这种现象的原因以及解决方法。

    无法调用析构函数的原因

    在DCLP(双检查锁模式)中,CSingleton中的instance是一个静态指针变量,被分配在全局/静态存储区。而instance所指向的CSingleton实例是通过new创建在堆上的,只能手动调用delete来释放相关资源(对于单例模式这是无法实现的,因为析构函数私有),无法通过RAII释放相关资源。
    在程序结束时,instance这个指针变量被销毁了,但它所指向的内存空间中的CSingleton对象并没有被显式销毁,而是由操作系统去回收这一块内存(不会调用其析构函数)。然而依赖操作系统来清理资源并不是一个优雅的结束方式,可能会造成文件句柄未关闭、网络连接未断开等资源泄漏。

    class CSingleton
    {
    public:
    static CSingleton* getInstance();
    static std::mutex mtx;
    private:
    CSingleton(){}
    ~CSingleton(){}
    CSingleton(const CSingleton&) = delete;
    CSingleton& operator=(const CSingleton&) = delete;

    static CSingleton* instance;
    };

    CSingleton* CSingleton::instance;

    CSingleton* CSingleton::getInstance()
    {
    if(nullptr == instance)
    {
    mtx.lock();
    if(nullptr == instance)
    {
    instance = new CSingleton();
    }
    mtx.unlock();
    }
    return instance;
    }
    @@ -2309,7 +2309,7 @@

    运行结果:

    智能指针

    我们还可以利用智能指针引用计数机制,对资源自动管理:

    @@ -2320,7 +2320,7 @@

    测试结果:

    局部静态变量

    局部静态变量形式的单例模式也可以完成资源的释放,详见《单例模式学习》

    @@ -2332,7 +2332,7 @@
    设计模式学习(一)单例模式补充——指令重排 - /singleton-reorder/ + /2024/03/19/singleton-reorder/ 前言

    《单例模式学习》 中曾提到懒汉式DCLP的单例模式实际也不是线程安全的,这是编译器的指令重排导致的,本文就简单讨论一下指令重排对单例模式的影响,以及对应的解决方法。

    指令重排简介

    指令重排(Instruction Reordering)是编译器或处理器为了优化程序执行效率而对程序中的指令序列进行重新排序的过程。这种重排可以发生在编译时也可以发生在运行时,目的是为了减少指令的等待时间和提高执行的并行性。

    @@ -2369,7 +2369,7 @@ 设计模式学习(一)单例模式的几种实现方式 - /singleton/ + /2024/03/18/singleton/ 前言

    单例模式,其核心目标是确保在程序运行的过程中,有且只有存在一个实例才能保证他们的逻辑正确性以及良好的效率。因此单例模式的实现思路就是确保一个类有且只有一个实例,并提供一个该实例的全局访问点。
    单例模式设计要点:

    • 私有构造、析构
    • @@ -2385,7 +2385,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的私有构造函数。

      @@ -2451,7 +2451,7 @@ jsoncpp的安装及使用 - /thirdlib-jsoncpp/ + /2024/06/13/thirdlib-jsoncpp/ 前言

      本文操作均在ubuntu20.04下进行。

      安装

      方法一:apt包管理器

      ①使用包管理器安装
      这种方法比较简单,直接使用apt包管理器安装jsoncpp:

      apt install libjsoncpp-dev
      @@ -2459,19 +2459,19 @@

      ②查看头文件和库文件
      安装完毕后我们可以去找一下jsoncpp的头文件和库文件
      头文件位于/usr/include/jsoncpp/json/

      库文件位于/usr/lib/x86_64-linux-gnu/

      但是我们通过头文件的version.h和动态库文件的realname,可以知道使用apt包管理器安装的jsoncpp的版本号为1.7.4

      那如果我需要安装的是jsoncpp的其他版本,就需要下载源代码并编译安装。

      @@ -2492,7 +2492,7 @@

      使用

      编写示例代码

      写JSON

      #include <json/json.h>
      #include <iostream>

      int main()
      {
      // 创建一个Json::Value对象
      Json::Value root;

      // 向对象中添加数据
      root["name"] = "John Doe";
      root["age"] = 30;
      root["isAlive"] = true;
      root["address"]["city"] = "New York";
      root["address"]["state"] = "NY";

      // 创建一个Json::StreamWriterBuilder
      Json::StreamWriterBuilder writer;

      // 将Json::Value对象转换为字符串
      std::string output = Json::writeString(writer, root);

      // 打印输出
      std::cout << output << std::endl;

      return 0;
      }
      @@ -2502,13 +2502,13 @@

      输出

      写JSON

      读JSON

      ]]>
      @@ -2519,7 +2519,7 @@
      log4cpp的安装及使用 - /thirdlib-log4cpp/ + /2024/07/15/thirdlib-log4cpp/ 前言

      本文的操作均在ubuntu20.04下进行

      安装

      本文仅介绍从源码编译安装log4cpp的过程。

      ①在开始编译前,首先要确保系统中安装了g++makeautoconflibtool

      @@ -2530,12 +2530,12 @@

      ④查看头文件和库文件
      默认配置安装的log4cpp,其头文件位于/usr/local/include/log4cpp/,库文件位于/usr/local/lib/

      使用

      示例代码

      #include <string>
      #include <iomanip>
      #include "log4cpp/PropertyConfigurator.hh"
      #include "log4cpp/Category.hh"

      int main()
      {
      try
      {
      log4cpp::PropertyConfigurator::configure("./cfg/log4cfg");
      }
      catch (log4cpp::ConfigureFailure& f)
      {
      std::cerr << "configure problem " << f.what() << std::endl;

      }
      log4cpp::Category & log = log4cpp::Category::getRoot();

      std::string s = __FILE__;
      s += " : ";
      std::ostringstream line;
      line << std::setw(4) << std::setfill('0') << __LINE__;
      s += line.str();
      s += " : ";
      std::ostringstream buf;
      buf<< "test message"; ;
      s += buf.str();

      log.info(s);
      }
      @@ -2544,7 +2544,7 @@

      输出

      输出结果:

      ]]>
      @@ -2555,7 +2555,7 @@
      内存布局分析工具pahole的安装和使用 - /tools-pahole/ + /2024/10/11/tools-pahole/ 本文相关测试的机器环境:

      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版本: