C 语言静态库与动态库的生成和使用

在 YouTube 上找到一个视频 动态链接库静态链接库的生成和使用,它把用 GCC 生成静态库和动态库,以及如何使用他们说的很明白,有条件的可以直接看那个视频。本文就是一个观后的实操和笔记,加添了更多如何查看动态库,静态库,目标文件,执行文件的过程。

为什么要了解静态库和动态库呢?这有助于我们理解多模块的 C/C++ 代码是如何联合工作的。我们多数时候使用的 IDE, 一个 Build 帮我们做了太多的事情,反而使我们眼前一抹黑,这背后有怎么把一个个源文件编译成目标文件(*.o) 文件,或生成静态库/动态库,又如何连接静态库/动态库生成可执行文件,等等。

试验中使用的平台是 Linux, 如果没有 Linux 可通过 Docker 容器得到一个,如

$ docker run -it -v $(pwd):/work -w /work rust:1.78-buster bash

为什么使用 rust:1.78 镜像,其实也没什么特别的,因为当前在学习 Rust, 而正好该镜像中有 GCC 编译器。启动该容器后,为编辑需要,最好安装一个 vim,在容器中运行 阅读全文 >>

Rust 调用 C/Rust 生成的动态库

在始终是 C/C++ 有着更优越性能的情况下,因而之前介绍过多种 其他不同的语言如何加载使用 C/C++ 写的动态库,有 Go, Python, Java 和 C#。在学习 Rust 之时也有类似的需求。本文的做法是要用到第三方库 libloading,这里将参考官方的例子。

先来创建一个动态库,使用和 Go 调用 C 写的动态库完整例子(Linux版) 一文中相同的例子,add.c 代码内容如下

在 Linux 中使用如下命令编译出 libadd.so 动态库文件 阅读全文 >>

Java 线程池有限大小工作队列 - 不丢弃任务的实现

我们在创建 Java 线程池,无论是用 Executors, ThreadPoolExecutor, 还是 Spring 的 ThreadPoolTaskExecutor, 如果不指定工作队列的大小的话,默认为 Integer.MAX_VALUE(2147483647), 基本不会把它爆满,但是在许多的任务要执行时大量 Runnable 对象的创建却足以把内存撑爆掉。所以才有必要使用一个有限大小的工作队列,如 5000, 再配上 RejectedExecutionHandler(DiscardOldestPolicy, DiscardPolicy, 或 CallerRunsPolicy)。前两种策略会主动放弃最旧最新的任务,一般不是我们想要的,CallerRunsPolicy 还能主动发挥任务提交者的计算能力,是一种不错的选择(只可能会发生工作队列太小且提交者执行的任务太忙时产生线程池一时的空闲。

所以总结起来我们可以有以下几种实现

直接使用 CallerRunsPolicy

在工作队列满时有效利用提交任务的线程,不让它闲着,这种实现最简单, 像下面那样声明线程池

阅读全文 >>

JDBC 设置 PostgreSQL 查询中 any(?) 的参数

这段时间都纠缠于 Java 如何操作 PostgreSQL 数据库上,千方百计的为求得更好的性能。为此我们用上了 Batch, 或用 id = any(?) 这种更 PostgreSQL 化的数组参数操作。其实它还有更多数组方面的花样可以玩,毕竟 PostgreSQL 数据库有一种广纳百川的胸怀,总有好的新特性能在 PostgreSQL 中首先体验到。

回到之前的一篇 postgres in (?,?) 和 =any(?) 用法/性能对比,其中关于如何向查询语句中 id = any(?) 占位符传入数组参数的代码是

在 PreparedStatement(PgPreparedStatement) 中设置数组参数的函数是用 阅读全文 >>

JDBC 批量调用数据库 SQL, 函数与存储过程

继续上一篇数据库相关操作的话题,在有大量的数据操作时(如增删改,甚至调用函数或存储过程),我们应该尽可能的采用批量化操作(先摆下结论,后面我们会看到原由)。想像一下我们要向数据库插入 10 万条记录,如果逐条插入的话,客户端与数据库之间将会有 10 万网络请求响应来回; 而假如以 1000 条记录为一个 batch, 客户端与数据库之间的网络请求响应次数将缩小到 100。 业务数据的内容总量未变,但 Batch 操作除了可重用预编译的 Statement 外还, 可避免每次请求中重复的元数据,所以从 100,000 到 100 的缩减在时效上的表现是非常可观的,有时就是 60 分钟与 1 分钟的区别(在最后面测试结果显示这一差异更为恐怖)。

当然, JDBC 的批处理功能具体还要相应驱动的支持,通过数据库连接的 conn.getMetaData().supportsBatchUpdates() 可探知是否支持批量操作。

API 方面, 在  Statement 接口中定义了如下 batch 相关的操作方法

  1. void addBatch(String sql): 将显式的 SQL 语句编入到当前 Batch 中
  2. void clearBatch(): 清除当前 Batch 列表,以便于建立新的 Batch
  3. int[] executeBatch(): 执行当前 Batch 列表中的语句,返回每条语句受影响行数组成的数组。0 可能表示执行语句无法确知受影响的行
  4. long[] executeLargeBatch(): 当 Batch 中语句受影响行数可能会超过整数最大值时用这个

阅读全文 >>

PostgreSQL 函数与存储过程及调用

PostgreSQL 随着云服务的盛行,越发被广泛的应用,免费开源且有丰富的特性支持,加上性能也很不错,因而备受青睐。PostgreSQL 的函数与存储过程区别并不太大,不像某些数据库的函数与存储过程必须是无副作用或有副作用,在 PostgreSQL 的函数和存储过程中可以进行任何的 SQL 操作。简单列举下 PostgreSQL 的函数与存储过程的区别主要如下:

函数

  1. return 或 out 参数返回值,return 可返回单个值或一系列值(return setof 或 return table), 或返回光标(cursor). 函数 return void 就和存储过程差不多了
  2. 函数因其有返回值,所以可通过 select, insert, updata 或 delete 语句来调用,如 select fn1(), delete * from test1 where fn2(c1)=0
  3. 可以用 execute 执行动态 sql, 如 execute 'delete * from ' || 't1'

存储过程

  1. IN, OUT 或 INOUT 参数,但不直接返回值
  2. 不能用 select, insert 等语句使用
  3. 不能用 execute 执行动态 sql

接下来我们来体验一下 PostgreSQL 的函数与存储过程 阅读全文 >>

Java 9 前/后使用 JAXB (包括支持 javax.* 或 jakarta.*)

使用 Java 处理 Object 与 XML 之间的转换时 JAXB(Java Architecture for XML Binding) 仍然被广泛使用。但随着 Java 9 模块化后把 JAXB 从标准 JDK 中移除后,和 Java EE 8(Jakarta EE 8) 到 Jakarta EE 9 的变迁时命名空间由 javax.* 变成了 jakarta.*,我们在使用 JAXB 时需作出相应的适配。

本文分别使用 Java 1.8, 17,通过 Maven 插件 jaxb2-maven-plugin 的 xjc (从 xsd 文件生成 Java 类), 和如何切换 jakarta.* 命名空间,由此可给我们对使用了 JAXB 的项目升级 JDK 时指明方向。从而不致于因不了解每部分组件的具体功效而在 pom.xml 中胡乱配置,比如之前对 jaxb2-maven-plugin 插件本身配置了多余的 org.glassfish.jaxb:jaxb-xjc 和 org.glassfish.jaxb:jaxb-runtime 依赖,也未能理解 jaxb2-maven-plugin 与 org.glassfish.jaxb:jaxb-runtime 之间的版本对应关系。

 实验准备,创建简单的 Maven 项目,并在路径 src/main/xsd/ 下新建 Schema 文件 sample.xsd,内容为 阅读全文 >>

Windows 安装使用 GCC(mingw-w64)

本人近十来年来本地用 Mac OS 开发, 服务器为 Linux, 为什么又要涉及到 Windows 的 GCC 呢?因为有个跨平台的东西用的是 C++, 需要分别编译出目标平台为 Linux 和 Windows 的二进制文件. 然而 C++ 并没有像 Rust 那样一出生就含着 Cargo 那样的工具链,完美的支持跨平台开发,构建。对于 C++ 代码不得不在 Linux 下用 GCC 编译器(Makefile), 而 Windows 下使用的 Visual Studio 的 MSBuild, 为了能统一用 Makefile 文件 + GCC 的方式编译 C++ 项目, 可选择 Windows 平台下也安装 GCC。

GCC 又是什么呢?它是 GNU 的编译工具集,包括对 C, C++, Objective-C, Foratran, Ada, Go 和 D 等一众语言的支持, 和它类似的工具集有 LLVM。GCC 支持多操作系统平台,怎么找到它的各种二进制安装包呢?我们循着官网去找, 打开 GCC 首页 https://gcc.gnu.org/,从页面的右边栏可找到 阅读全文 >>

Rust 语言学习笔记(五)

终于来到了 Rust 的精髓所在了,那就是使之不依赖于垃圾回收又能保障内存安全且高效运行的所有权系统(Ownership System)。想要用 Rust 做一个稍显规模项目必定绕不过它,所有权系统包括所有权(Ownership), 借用(Borrowing), 生命周期(Lifetimes)。

以下概念的复述基本是从 《Rust编程: 入门, 实战与进阶》一书中而来,那里面有些内容是来自于官方的 The Rust Programming Languge - Understanding Ownership

所有权系统的基本概念

Rust 的编程语法很快就能上手,让学习 Rust 曲线陡然大增的也就是这个所有权系统。所有权检测在编译期完成,Rust 能编译出来的代码就是安全高效的。要理解 Rust 的所有权系统必须首先明白以下两组概念:

  1. 栈内存(Stack),值语义(Value Semantic),按位复制(浅复制)(Shallow Copy),复制语义(Copy Semantic)
  2. 堆内存(Heap), 引用语义(Reference Semantic), 深复制(Deep Copy),移动语义(Move Semantic), 借助(Borrowing)

和其他语言一样,大小固定的所有基本类型都可以存储在栈上,栈上存取数据总是在栈顶操作,很快,而访问堆内存需要搜索内存地址。所有权系统的主要任务是用来跟踪堆上的数据,即引用语义的数据。 阅读全文 >>

从 Rust 官方文档理解 Ownership

Rust 的 Ownership 感觉仍然很复杂,但 Rust 官方文档 The Rust Programming Language - Understanding Ownership 所费篇幅似乎并不多。下面就阅读该文档并记录下来对 Rust Ownership 的理解,相信官方的文档会表述的比准确而清晰。

本文中对 Ownership, Move, Reference, Dereference, Mutable, Immutable, Borrow, Owner, Stack, Heap, Scope 等词不进行翻译,以免走样。同时在阅读过程中不进行过度的联想,不与 C/C++ 的引用, 指针, 指针的指针进行关联,力求做一个完全不会 C/C++ 的 Rust 初学者。

Ownership 是 Rust 独一无二的特性。内存管理一般是两种,显式分配与释放和 GC, 这两种的弊端无需多说。Rust 另辟溪径,用 Owership 的一系列的规则来指导怎么管理内存,编译期保证程序运行期的内存安全性,不影响运行时性能。学习 Rust 的过程中需要很长时间去适应 Ownership, 从 Rust 开发者(Rustacean) 的经验来说是:随着对 Ownership 的掌握,越来轻松自然的写出安全高效的代码(希望如此)。 阅读全文 >>