广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >什么是C语言动态库
  • 702
分享到

什么是C语言动态库

2024-04-02 19:04:59 702人浏览 薄情痞子
摘要

这篇文章主要讲解了“什么是C语言动态库”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是C语言动态库”吧!什么? 这里竟然躺着一个高性能 base64 库

这篇文章主要讲解了“什么是C语言动态库”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是C语言动态库”吧!

什么? 这里竟然躺着一个高性能 base64 库?

我们以这个 repo 为例: https:// GitHub.com/aklomp/base6 4 . 这是一个 C 编写的 Base64 编码/解码库, 而且支持SIMD.

可以简单运行下这个库的 benchmark:

karminski@router02:/data/works/base64$ make clean && SSSE3_CFLAGS=-mssse3 AVX2_CFLAGS=-mavx2 make && make -C test ... Testing with buffer size 100 KB, fastest of 10 * 100 AVX2    encode  12718.47 MB/sec AVX2    decode  14542.81 MB/sec plain   encode  3657.40 MB/sec plain   decode  3433.23 MB/sec SSSE3   encode  7269.55 MB/sec SSSE3   decode  8173.10 MB/sec ...

我的 CPU 是 Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz, 可以看到CPU如果支持 AVX2 的话, 可以达到 12GB/s 以上, 这个性能非常强悍, 甚至连普通的SSD都跟不上了.

我们需要的第一步是把这个 repo 编译为动态库. 但是这个 repo 并没有提供动态库的编译选项, 所以我们魔改下这个项目的 Makefile.

CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic  # Set OBJCOPY if not defined by environment: OBJCOPY ?= objcopy  OBjs = \   lib/arch/avx2/codec.o \   lib/arch/generic/codec.o \   lib/arch/neon32/codec.o \   lib/arch/neon64/codec.o \   lib/arch/ssse3/codec.o \   lib/arch/sse41/codec.o \   lib/arch/sse42/codec.o \   lib/arch/avx/codec.o \   lib/lib.o \   lib/codec_choose.o \   lib/tables/tables.o  SOOBJS = \   lib/arch/avx2/codec.so \   lib/arch/generic/codec.so \   lib/arch/neon32/codec.so \   lib/arch/neon64/codec.so \   lib/arch/ssse3/codec.so \   lib/arch/sse41/codec.so \   lib/arch/sse42/codec.so \   lib/arch/avx/codec.so \   lib/lib.so \   lib/codec_choose.so \   lib/tables/tables.so  HAVE_AVX2   = 0 HAVE_NEON32 = 0 HAVE_NEON64 = 0 HAVE_SSSE3  = 0 HAVE_SSE41  = 0 HAVE_SSE42  = 0 HAVE_AVX    = 0  # The user should supply compiler flags for the codecs they want to build. # Check which codecs we're Going to include: ifdef AVX2_CFLAGS   HAVE_AVX2 = 1 endif ifdef NEON32_CFLAGS   HAVE_NEON32 = 1 endif ifdef NEON64_CFLAGS   HAVE_NEON64 = 1 endif ifdef SSSE3_CFLAGS   HAVE_SSSE3 = 1 endif ifdef SSE41_CFLAGS   HAVE_SSE41 = 1 endif ifdef SSE42_CFLAGS   HAVE_SSE42 = 1 endif ifdef AVX_CFLAGS   HAVE_AVX = 1 endif ifdef OPENMP   CFLAGS += -fopenmp endif   .PHONY: all analyze clean  all: bin/base64 lib/libbase64.o lib/libbase64.so  bin/base64: bin/base64.o lib/libbase64.o lib/libbase64.so     $(CC) $(CFLAGS) -o $@ $^  lib/libbase64.o: $(OBJS)     $(LD) -r -o $@ $^     $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@  lib/libbase64.so: $(SOOBJS)     $(LD) -shared -fPIC -o $@ $^     $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@  lib/config.h:     @echo "#define HAVE_AVX2   $(HAVE_AVX2)"    > $@     @echo "#define HAVE_NEON32 $(HAVE_NEON32)" >> $@     @echo "#define HAVE_NEON64 $(HAVE_NEON64)" >> $@     @echo "#define HAVE_SSSE3  $(HAVE_SSSE3)"  >> $@     @echo "#define HAVE_SSE41  $(HAVE_SSE41)"  >> $@     @echo "#define HAVE_SSE42  $(HAVE_SSE42)"  >> $@     @echo "#define HAVE_AVX    $(HAVE_AVX)"    >> $@  $(OBJS): lib/config.h  $(SOOBJS): lib/config.h  # o lib/arch/avx2/codec.o:   CFLAGS += $(AVX2_CFLAGS) lib/arch/neon32/codec.o: CFLAGS += $(NEON32_CFLAGS) lib/arch/neon64/codec.o: CFLAGS += $(NEON64_CFLAGS) lib/arch/ssse3/codec.o:  CFLAGS += $(SSSE3_CFLAGS) lib/arch/sse41/codec.o:  CFLAGS += $(SSE41_CFLAGS) lib/arch/sse42/codec.o:  CFLAGS += $(SSE42_CFLAGS) lib/arch/avx/codec.o:    CFLAGS += $(AVX_CFLAGS) # so lib/arch/avx2/codec.so:   CFLAGS += $(AVX2_CFLAGS) lib/arch/neon32/codec.so: CFLAGS += $(NEON32_CFLAGS) lib/arch/neon64/codec.so: CFLAGS += $(NEON64_CFLAGS) lib/arch/ssse3/codec.so:  CFLAGS += $(SSSE3_CFLAGS) lib/arch/sse41/codec.so:  CFLAGS += $(SSE41_CFLAGS) lib/arch/sse42/codec.so:  CFLAGS += $(SSE42_CFLAGS) lib/arch/avx/codec.so:    CFLAGS += $(AVX_CFLAGS)  %.o: %.c     $(CC) $(CFLAGS) -o $@ -c $<  %.so: %.c     $(CC) $(CFLAGS) -shared -fPIC -o $@ -c $<  analyze: clean     scan-build --use-analyzer=`which clang` --status-bugs make  clean:     rm -f bin/base64 bin/base64.o lib/libbase64.o lib/libbase64.so lib/config.h $(OBJS)

看不懂没关系, Makefile 是如此的复杂, 我也看不懂, 仅仅是凭着感觉修改的, 然后他就恰好能运行了... 注意 Makefile 的缩进一定要用 "\t", 否则不符合语法会报错.

然后我们进行编译:

AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.so

这样我们就得到了libbase64.so 动态库 (在 lib 里面). 这里还顺便开启了各种 SIMD 选项. 如果不需要的话可以关闭.

魔改开始

当然这只是魔法, 不是炼金术, 所以是需要付出努力的, 我们要手动实现动态库的桥接, 首先我们需要查看我们要调用的函数需要什么参数. 这两个定义很简单, 我们需要传入:

const char *src size_t srclen char *out size_t *outlen int flags
void base64_encode(const char *src, size_t srclen, char *out, size_t *outlen, int flags); int  base64_decode(const char *src, size_t srclen, char *out, size_t *outlen, int flags);

然后我们就可以开始编写 ffi 桥接程序了. 首先把需要的库全都包含进来, 注意, 多用 local 没坏处, 使用 local 可以有效从局部查询, 避免低效的全局查询. 甚至其他包中的函数都可以 local 一下来提升性能.

动态库的话用专用的 ffi.load 来引用.

然后定义一个 _M 用来包裹我们的库. 这里跟 javascript 很像, JavaScript 在浏览器里有 window, lua 有 _G. 我们要尽可能避免封装好的库直接扔给全局, 因此封装起来是个好办法.

-- init local ffi        = require "ffi" local floor      = math.floor local ffi_new    = ffi.new local ffi_str    = ffi.string local ffi_typeof = ffi.typeof local C          = ffi.C local libbase64  = ffi.load("./libbase64.so") -- change this path when needed.  local _M = { _VERSioN = '0.0.1' }

然后是用 ffi.cdef 声明 ABI 接口, 这里更简单, 直接把源代码的头文件中的函数声明拷过来就完事了:

-- cdef ffi.cdef[[ void base64_encode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); int  base64_decode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); ]]

接下来是最重要的类型转换:

-- define types local uint8t    = ffi_typeof("uint8_t[?]") -- uint8_t * local psizet    = ffi_typeof("size_t[1]")  -- size_t *  -- package function function _M.base64_encode(src, flags)     local dlen   = floor((#src * 8 + 4) / 6)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_encode(src, #src, out, outlen, flags)     return ffi_str(out, outlen[0])  end   function _M.base64_decode(src, flags)     local dlen   = floor((#src + 1) * 6 / 8)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_decode(src, #src, out, outlen, flags)     return ffi_str(out, outlen[0]) end

我们用 ffi_typeof 来定义需要映射的数据类型, 然后用 ffi_new 来将其实例化, 分配内存空间. 具体来讲:

我们定义了2种数据类型, 其中, local uint8t = ffi_typeof("uint8_t[?]") 类型用来传输字符串, 后面的问号是给 local out = ffi_new(uint8t, dlen) 中的 ffi_new 函数准备的, 它的第二个参数可以指定实例化该数据类型时的长度. 这样我们就得到了一个空的字符串数组, 用来装 C 函数返回的结果. 这里的 dlen 计算出了源字符串 base64 encode 之后的长度, 分配该长度即可.

同样, local psizet = ffi_typeof("size_t[1]") 指定了一个 size_t * 类型. C 语言里面数组就是指针, 即 size_t[0] 与 site_t* 是等价的. 因此我们分只有一个元素的 size_t 数组就得到了指向 size_t 类型的指针. 然后在 local outlen = ffi_new(psizet, 1) 的时候后面的参数写的也是1, 不过这里写什么已经无所谓了, 它只是不支持传进去空, 所以我们相当于传了个 placeholder.

在使用这个值的时候, 我们也是按照数组的模式去使用的: return ffi_str(out, outlen[0]) .

需要注意的是, 一定要将 require "ffi" 以及 ffi.load 放在代码最底层, 否则会出现 table overflow 的情况.

最后, 这个文件是这样子的:

--[[       ffi-base64.lua          @version    20201228:1     @author     karminski <code.karminski@outlook.com>  ]]--  -- init local ffi        = require "ffi" local floor      = math.floor local ffi_new    = ffi.new local ffi_str    = ffi.string local ffi_typeof = ffi.typeof local C          = ffi.C local libbase64  = ffi.load("./libbase64.so") -- change this path when needed.  local _M = { _VERSION = '0.0.1' }   -- cdef ffi.cdef[[ void base64_encode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); int  base64_decode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); ]]  -- define types local uint8t    = ffi_typeof("uint8_t[?]") -- uint8_t * local psizet    = ffi_typeof("size_t[1]")  -- size_t *  -- package function function _M.base64_encode(src, flags)     local dlen   = floor((#src * 8 + 4) / 6)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_encode(src, #src, out, outlen, flags)     return ffi_str(out, outlen[0])  end   function _M.base64_decode(src, flags)     local dlen   = floor((#src + 1) * 6 / 8)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_decode(src, #src, out, outlen, flags)     return ffi_str(out, outlen[0]) end   return _M

好了, 大功告成, 我们写个 demo 调用一下试试:

-- main.lua local ffi_base64 = require "ffi-base64"   local target = "Https://example.com"  local r = ffi_base64.base64_encode(target, 0) print("base64 encode result: \n"..r)  local r = ffi_base64.base64_decode(r, 0) print("base64 decode result: \n"..r)
root@router02:/data/works/libbase64-ffi# luajit -v LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2020 Mike Pall. https://luajit.org/ root@router02:/data/works/libbase64-ffi# luajit ./main.lua  base64 encode result:  aHR0cHM6Ly9leGFtcGxlLmNvbQ== base64 decode result:  https://example.com

感谢各位的阅读,以上就是“什么是C语言动态库”的内容了,经过本文的学习后,相信大家对什么是C语言动态库这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 什么是C语言动态库

本文链接: https://www.lsjlt.com/news/84145.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 什么是C语言动态库
    这篇文章主要讲解了“什么是C语言动态库”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是C语言动态库”吧!什么 这里竟然躺着一个高性能 base64 库我...
    99+
    2022-10-19
  • c语言动态规划算法是什么
    C语言动态规划算法是一种用于解决优化问题的算法。它通过将问题划分为子问题,并保存子问题的解来避免重复计算,从而提高算法的效率。动态规...
    99+
    2023-08-18
    c语言
  • C语言的动态内存管理是什么意思
    这篇文章将为大家详细讲解有关C语言的动态内存管理是什么意思,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、动态内存分配(1)用malloc类的函数分配内存;(2)用这些内存支持应用程序;(3)用free...
    99+
    2023-06-29
  • C语言动态内存管理的方法是什么
    本文小编为大家详细介绍“C语言动态内存管理的方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言动态内存管理的方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.为什么需要动态内存分配关于这个...
    99+
    2023-06-29
  • C语言中的状态机是什么
    本篇内容主要讲解“C语言中的状态机是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言中的状态机是什么”吧! 前言本文不是关于软件状态机的最佳设计分解实践的教程。我将重点关注状态...
    99+
    2023-06-15
  • C语言静态与动态通讯录的实现方法是什么
    这篇文章主要讲解了“C语言静态与动态通讯录的实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言静态与动态通讯录的实现方法是什么”吧!静态通讯录在我们学习完C语言的结构体、指针...
    99+
    2023-06-25
  • go语言是不是动态语言
    这篇文章主要介绍了go语言是不是动态语言的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇go语言是不是动态语言文章都会有所收获,下面我们一起来看看吧。go不是动态语言。go语言是Google开发的一种静态强类型、...
    99+
    2023-07-04
  • go语言是动态语言吗
    go不是动态语言。go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,其变量(variable)是有明确类型的,编译器也会检查变量类型的正确性;因此在使用变量之前必须声明数据类型需要,语法“var 变量...
    99+
    2023-05-14
    动态语言 go语言 Golang
  • C++是什么语言
    这篇文章主要介绍“C++是什么语言”,在日常操作中,相信很多人在C++是什么语言问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++是什么语言”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!C++是一种面向...
    99+
    2023-06-17
  • golang调用c语言动态库方式实现
    下面我们自己在 Linux 下做一个动态库(.so 文件 - Shared Object),然在用 Go 来使用它。本文所用的操作系统为 Ubuntu18.04, 以 gcc 作为编...
    99+
    2022-11-12
  • C#的动态类型语法是什么
    这篇文章主要介绍了C#的动态类型语法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#的动态类型语法是什么文章都会有所收获,下面我们一起来看看吧。动态(Dynamic)类型您可以存储任何类型的值在动态数据...
    99+
    2023-06-17
  • go语言和c语言是什么
    这篇文章主要介绍了go语言和c语言是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇go语言和c语言是什么文章都会有所收获,下面我们一起来看看吧。Go(又称Golang)是Google开发的一种静态强类型、编...
    99+
    2023-07-04
  • c语言graphics库的用法是什么
    C语言中的Graphics库是一个用于图形绘制的库。它提供了一系列函数和数据类型,可以在图形窗口中绘制各种图形、文本和图像。 Gra...
    99+
    2023-10-23
    c语言 graphics
  • C++调用动态库和Python调用C++动态库的方法是什么
    这篇文章主要介绍“C++调用动态库和Python调用C++动态库的方法是什么”,在日常操作中,相信很多人在C++调用动态库和Python调用C++动态库的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答...
    99+
    2023-07-05
  • java动态和静态语言的区别是什么
    今天就跟大家聊聊有关java动态和静态语言的区别是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1、动态语言在运行中可以改变结构的语言。例如,新函数、对象、代码可以引进,现有函数...
    99+
    2023-06-15
  • C语言动态数组详解
    目录内存分配函数malloc calloc realloc free内存操作函数 memset memcpy memmove二维动态数组的建立和释放总结内存分配函数malloc ca...
    99+
    2022-11-12
  • C语言中怎么动态扩容String
    这篇文章给大家介绍C语言中怎么动态扩容String,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。首先定义一个string的句柄,相当于C++中的实例struct c_string; typedef&...
    99+
    2023-06-16
  • javascript是不是动态语言
    这篇文章主要介绍“javascript是不是动态语言”,在日常操作中,相信很多人在javascript是不是动态语言问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”javasc...
    99+
    2022-10-19
  • C++中Cmake的构建静态库和动态库是什么
    这篇文章主要为大家展示了“C++中Cmake的构建静态库和动态库是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C++中Cmake的构建静态库和动态库是什么”这篇文章吧。静态库和动态库的区别...
    99+
    2023-06-29
  • c语言怎么动态创建结构体
    在C语言中,可以使用动态内存分配函数`malloc()`来动态创建结构体。首先,需要定义一个结构体类型,如下所示:```ctyped...
    99+
    2023-10-10
    c语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作