【libffi】动态调用&定义C函数

libffi浅析 最近调试了weston的一个coredump,对libffi有了一些了解,在此记录下,使用的是arm处理器,32位,soft float,libffi3.1,使用的abi是SYSV。 libffi简介和使用示例:http://www.atmark-techno.com/~yashi/libffi.html,建议先看完,有所了解再继续看本文。大体意思就是libffi用于高级语言之间的相互调 阅读详情

一、Calling Convention
高级语言编译器将代码编译成相应汇编指令时都会依据一系列的规则,这些规则十分必要,特别是对独立编译来说。其中之一是“调用约定” (Calling Convention),它包含了编译器关于函数入口处的函数参数、函数返回值的一系列假设。它有时也被称作“ABI”(Application Binary Interface)。调用约定(Calling Conventions)定义了程序中调用函数的方式,它决定了在函数调用的时候数据(比如说参数)在堆栈中的组织方式。

编译器按照调用规则去编译,把数据放到相应的堆栈中,那么意味着函数的调用方和被调用方(函数本身)也要遵循这个统一的约定,不然函数执行过程中,会因为取不到相应类型参数和无法正确返回而崩溃。

下面看个例子:

int testFunc(int n, int m) {
  return n+m;
}

int main() {
  // (1)
  int (*funcPointer)(int, int) = dlsym(RTLD_DEFAULT, "testFunc");
  funcPointer(1, 2);

  // (2)
  void (*funcPointer)() = dlsym(RTLD_DEFAULT, "testFunc");
  funcPointer(1, 2); //error

  return 0;
}

运行上面代码发现,(1)处正常执行,(2)处崩溃,原因就是因为(2)处funcPointer的定义没有遵循调用规则,和原函数本身的定义不符,而编译器编译的时候认为funcPointer是个无参数函数,则不会在执行栈上分配两个int型的内存空间用来存储实参1,2;这样当exp指针跳转到代码区执行原函数,去取对应参数时,会出现取不到或取到的内容有误的情况,从而导致崩溃。

可见我们在函数调用前,需要明确的告诉编译器这个函数的参数和返回值类型是什么,函数才能正常执行。

那这样来说动态的调用一个C函数是不可能实现了,因为我们在编译前,就要将遵循调用规则的函数调用写在需要调用的地方,然后通过编译器编译生成对应的汇编代码,将相应的栈和寄存器状态准备好。如果想在运行时动态去调用的话,将没有人为我们做这一系列的处理。

所以我们要解决的问题是:当我们在运行时动态调用一个函数时,自己要先将相应栈和寄存器状态准备好,然后生成相应的汇编指令。这也正是libffi所做的。

二、libffi
FFI(Foreign Function Interface)允许以一种语言编写的代码调用另一种语言的代码,而libffi库提供了最底层的、与架构相关的、完整的FFI。libffi的作用就相当于编译器,它为多种调用规则提供了一系列高级语言编程接口,然后通过相应接口完成函数调用,底层会根据对应的规则,完成数据准备,生成相应的汇编指令代码。

那么这样我们就可以通过libffi动态的调用任意C函数,那libffi 具体怎么使用呢?详细文档请看:这里

三、动态调用C函数
使用libffi提供接口动态调用流程如下:

  1. 准备好参数数据及其对应ffi_type数组、返回值内存指针、函数指针
  2. 创建与函数特征相匹配的函数原型:ffi_cif对象
  3. 使用“ffi_call”来完成函数调用

需使用的libffi API:

/* 封装函数原型
ffi_prep_cif returns a libffi status code, of type ffi_status. This will be either FFI_OK if everything worked properly; FFI_BAD_TYPEDEF if one of the ffi_type objects is incorrect; or FFI_BAD_ABI if the abi parameter is invalid.
*/
ffi_status ffi_prep_cif(ffi_cif *cif,
            ffi_abi abi,                  //abi is the ABI to use; normally FFI_DEFAULT_ABI is what you want. Multiple ABIs for more information.
            unsigned int nargs,           //nargs is the number of arguments that this function accepts. ‘libffi’ does not yet handle varargs functions; see Missing Features for more information.
            ffi_type *rtype,              //rtype is a pointer to an ffi_type structure that describes the return type of the function. See Types.
            ffi_type **atypes);           //argtypes is a vector of ffi_type pointers. argtypes must have nargs elements. If nargs is 0, this argument is ignored.

/*  调用指定函数
This calls the function fn according to the description given in cif. cif must have already been prepared using ffi_prep_cif.
*/
void ffi_call(ffi_cif *cif,
          void (*fn)(void),
          void *rvalue,                   //rvalue is a pointer to a chunk of memory that will hold the result of the function call. This must be large enough to hold the result and must be suitably aligned; it is the caller's responsibility to ensure this. If cif declares that the function returns void (using ffi_type_void), then rvalue is ignored. If rvalue is ‘NULL’, then the return value is discarded.
          void **avalue);                 //avalues is a vector of void * pointers that point to the memory locations holding the argument values for a call. If cif declares that the function has no arguments (i.e., nargs was 0), then avalues is ignored. Note that argument values may be modified by the callee (for instance, structs passed by value); the burden of copying pass-by-value arguments is placed on the caller.

下面看一个简单的例子:

int testFunc(int m, int n) {
    printf("params: %d %d \n", m, n);
    return m+n;
}

+ (void)testCall {
    testFunc(1, 2);

    //拿函数指针
    void* functionPtr = &testFunc;
    int argCount = 2;

    //参数类型数组
    ffi_type **ffiArgTypes = alloca(sizeof(ffi_type *) *argCount);
    ffiArgTypes[0] = &ffi_type_sint;
    ffiArgTypes[1] = &ffi_type_sint;

    //参数数据数组
    void **ffiArgs = alloca(sizeof(void *) *argCount);
    void *ffiArgPtr = alloca(ffiArgTypes[0]->size);
    int *argPtr = ffiArgPtr;
    *argPtr = 5;
    ffiArgs[0] = ffiArgPtr;

    void *ffiArgPtr2 = alloca(ffiArgTypes[1]->size);
    int *argPtr2 = ffiArgPtr2;
    *argPtr2 = 3;
    ffiArgs[1] = ffiArgPtr2;

    //生成函数原型 ffi_cfi 对象
    ffi_cif cif;
    ffi_type *returnFfiType = &ffi_type_sint;
    ffi_status ffiPrepStatus = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, (unsigned int)argCount, returnFfiType, ffiArgTypes);

    if (ffiPrepStatus == FFI_OK) {
        //生成用于保存返回值的内存
        void *returnPtr = NULL;
        if (returnFfiType->size) {
            returnPtr = alloca(returnFfiType->size);
        }
        //根据cif函数原型,函数指针,返回值内存指针,函数参数数据调用这个函数
        ffi_call(&cif, functionPtr, returnPtr, ffiArgs);

        //拿到返回值
        int returnValue = *(int *)returnPtr;
        printf("ret: %d \n", returnValue);
    }
}

执行结果:

params: 1 2 
params: 5 3 
ret: 8 

可见使用ffi,只要有函数原型cif对象,函数实现指针,返回值内存指针和函数参数数组,我们就可以实现在运行时动态调用任意C函数。

所以如果想实现其他语言(譬如JS),执行过程中动态调用C函数,只需在调用过程中加一层转换,将参数及返回值类型转换成libffi对应类型,并封装成函数原型cif对象,准备好参数数据,找到对应函数指针,然后调用即可。

四、动态定义C函数
libffi还有一个特别强大的函数,通过它我们可以将任意参数和返回值类型的函数指针,绑定到一个函数实体上。那么这样我们就可以很方便的实现动态定义一个C函数了!同时这个函数在编写解释器或提供任意函数的包装器(通用block)时非常有用,此函数是:

ffi_status ffi_prep_closure_loc (ffi_closure *closure,  //闭包,一个ffi_closure对象
       ffi_cif *cif,  //函数原型
       void (*fun) (ffi_cif *cif, void *ret, void **args, void*user_data), //函数实体
       void *user_data, //函数上下文,函数实体实参
       void *codeloc)   //函数指针,指向函数实体

来看下函数各参数详细说明:

Prepare a closure function.

参数 closure is the address of a ffi_closure object; this is the writable address returned by ffi_closure_alloc.

参数 cif is the ffi_cif describing the function parameters.

参数 user_data is an arbitrary datum that is passed, uninterpreted, to your closure function.

参数 codeloc is the executable address returned by ffi_closure_alloc.

函数实体 fun is the function which will be called when the closure is invoked. It is called with the arguments:

函数实体参数 cif
The ffi_cif passed to ffi_prep_closure_loc. 
函数实体参数 ret
A pointer to the memory used for the function's return value. fun must fill this, unless the function is declared as returning void. 
函数实体参数 args
A vector of pointers to memory holding the arguments to the function. 
函数实体参数 user_data
The same user_data that was passed to ffi_prep_closure_loc.
ffi_prep_closure_loc will return FFI_OK if everything went ok, and something else on error.

After calling ffi_prep_closure_loc, you can cast codeloc to the appropriate pointer-to-function type.

You may see old code referring to ffi_prep_closure. This function is deprecated, as it cannot handle the need for separate writable and executable addresses.

下面通过一个简单的例子,看下如何将一个函数指针绑定到一个函数实体上:

#include <stdio.h>
#include <ffi.h>

/* Acts like puts with the file given at time of enclosure. */
// 函数实体
void puts_binding(ffi_cif *cif, unsigned int *ret, void* args[],
                  FILE *stream)
{
    *ret = fputs(*(char **)args[0], stream);
}

int main()
{
    ffi_cif cif;
    ffi_type *args[1];
    ffi_closure *closure;

    int (*bound_puts)(char *);  //声明一个函数指针
    int rc;

    /* Allocate closure and bound_puts */  //创建closure
    closure = ffi_closure_alloc(sizeof(ffi_closure), &bound_puts);

    if (closure)
    {
        /* Initialize the argument info vectors */
        args[0] = &ffi_type_pointer;

        /* Initialize the cif */  //生成函数原型
        if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1,
                         &ffi_type_uint, args) == FFI_OK)
        {
            /* Initialize the closure, setting stream to stdout */
            // 通过 ffi_closure 把 函数原型_cifPtr / 函数实体JPBlockInterpreter / 上下文对象self / 函数指针blockImp 关联起来
            if (ffi_prep_closure_loc(closure, &cif, puts_binding,
                                     stdout, bound_puts) == FFI_OK)
            {
                rc = bound_puts("Hello World!");
                /* rc now holds the result of the call to fputs */
            }
        }
    }

    /* Deallocate both closure, and bound_puts */
    ffi_closure_free(closure);   //释放闭包

    return 0;
}

上述步骤大致分为:

  1. 准备一个函数实体
  2. 声明一个函数指针
  3. 根据函数参数个数/参数及返回值类型生成一个函数原型
  4. 创建一个ffi_closure对象,并用其将函数原型、函数实体、函数上下文、函数指针关联起来
  5. 释放closure

通过以上这5步,我们就可以在执行过程中将一个函数指针,绑定到一个函数实体上,从而轻而易举的实现动态定义一个C函数。

由上可知:如果我们利用好user_data,用其传入我们想要的函数实现,将函数实体变成一个通用的函数实体,然后将函数指针改为void*,通过结构体创建一个block保存函数指针并返回,那么我们就可以实现JS调用含有任意类型block参数的OC方法了(后续文章会简要概述说明)

到这我们已经清楚的了解了libffi的秒用,以后实际应用中,我们可以利用它轻松实现多种语言之间的互相调用。

转自:https://blog.youkuaiyun.com/JaimeCool/article/details/76332930

windows 上使用 msvc 编译 libffi 打开wsl git clone https://github.com/libffi/libffi.git ./autogen.sh 1.git bash(mingw64)上没有 autoreconf 1.git bash(mingw64)下载的仓库wsl上执行需要dos2unix 通过x64 Native Tools Command Prompt for VS 2019 执行 “C:\Program Files\Git\git-bash.exe” 1.直接启动git-bash,无法识别cl.exe,即使加入 阅读详情

相关推荐

C++ libtiff库VC++版本编译使用方法x86,x64均有效

VS2015 x86,x64亲测有效。 一、编译libtiff库 1、github下载libtiff 2、修改mirrors-libtiff-master\libtiff\libtiff下的tif_config.vc.h文件,把下面这些编译不过报错的都开放出来,原来是屏蔽的; 3、使用VS的命令行工具,nmake命令编译makefile.vc:注意想编译什么版本就使用什么命令行工具 4、我选的是VS2015 x64的,打开后输入下面的命令 5、出现下面结果就是成功编译了 6、.

h200863057的博客 2954

深入理解libffi:可移植的外函数接口库

深入理解libffi:可移植的外函数接口库 什么是libffi? libffi是一个提供可移植外函数接口(Foreign Function Interface,FFI)的库。它允许程序在运行时动态调用编译好的函数,而无需在编译时知道这些函数的具体签名。 调用约定与ABI 高级语言编译器生成的代码遵循特定的调用约定(calling convention),这是编译器对函数入口时参数存放位置的一组假设...

gitblog_00278的博客 754

告别objdump:用pwntools asm实现汇编指令一键转机器码

在二进制安全、逆向工程和底层开发中,汇编语言与机器码的转换是基础且关键的一环。传统方法依赖objdump等工具链,流程繁琐且难以集成。其原理是通过汇编器将人类可读的指令转换为处理器可执行的二进制编码,这一过程对于漏洞利用、Shellcode生成和固件分析等技术场景具有核心价值。pwntools库的asm函数封装了多架构汇编引擎,提供了纯Python API,使得汇编到机器码的转换能够无缝嵌入自动化脚本,极大提升了安全研究和CTF竞赛中的工作效率。本文聚焦于解决pwntools asm在Mac和Linux系统

weixin_30247781的博客 649

【亲测免费】 探索LibFFI:一个灵活的函数接口库

在软件开发中,动态调用未知函数的能力是至关重要的。这就是大展拳脚的地方。这是一个开源项目,旨在提供一种在各种平台上跨语言的动态调用函数的能力。 ## 项目简介 LibFFI(Foreign Function Interface)是一个C库,允许程序员在运行时调用任何具有已知参数和返回类型函数的能力。它使得开发者可以在不知道函数实现的语言或其具体位置的情况下,与外部代码进行交互。此外,libff...

gitblog_00038的博客 1946

企业级业务Agent落地实践:确定性响应与业务语义对齐

业务Agent并非通用AI助手,而是面向ERP、OA、CRM等核心系统的可审计、可监控、可灰度的自动化执行体。其本质是将自然语言指令转化为确定性业务动作,依赖意图识别、工具调度、状态同步与交付物生成四大技术支柱。关键突破在于用规则引擎锚定业务语义,以业务原子动作(BAA)替代黑盒API调用,并通过状态同步代理实现与业务系统同源一致。这种设计显著提升准确率与合规性,支撑采购审批、HR入职、财务报销等强流程场景。本文聚焦可生产部署的企业级Agent工程实践,涵盖双轨意图解析、BAA契约定义、State Sync

技术百科 631

Hermes Agent文件操作5大核心配置详解

Hermes Agent 是面向终端的AI开发代理,其文件操作能力并非开箱即用,而是依赖严格的运行时环境配置。其底层原理基于终端进程继承的shell上下文,涉及路径解析、权限控制、编码识别与子进程执行四大技术环节。技术价值在于将HTML等前端文件的修改从手动编辑升维为可编程、可复现、可集成CI/CD的自动化流程。典型应用场景包括快速更新HTML meta标签、批量生成页面模板、同步修改多环境配置文件等。本文聚焦`file`工具失效的根因,系统梳理`tools.enabled`、`file.working_d

weixin_30471561的博客 473

在LISP中用FFI调用C语言的程序-成功了

用C代码实现一个DLL,可以用VC创建一个工程,让它生成lispcall.dll,导出一个整形变量和一个函数,函数里面随便写点什么。 LISPC_API extern int x; LISPC_API int f(int v) { return v*v; }   再创建一个LISP源文件,写上下面代码 (defpackage ffi-test (:use common-lisp

节奏胜于思想的专栏 1495

cpp-libffi一个便携式外部功能接口库

libffi - 一个便携式外部功能接口库

IntelliJ IDEA 中 Java 调用 DLL:JNI 与 JNA 实战

Java 调用 DLL 是跨语言集成的典型问题:JVM 需通过本地接口访问 C/C++ 编译的动态链接库。JNI 依靠 native 方法与 JNIEnv 建立符号级桥接,JNA 则用接口映射、动态代理和 libffi 在运行时完成参数封送。其价值在于复用硬件 SDK、C/C++ 遗产代码和 Win32 原生能力,常见于读卡器、加密狗、工业相机、医疗设备等场景。真正落地 IntelliJ IDEA 时,位数匹配、调用约定、字符串编码、依赖 DLL 链及随 Jar 打包往往比接口声明更棘手,Unsatisfi

weixin_34408624的博客 375

LayaNative源码编译

修改好后的LayaNative2.0需要重新编译生成Android和iOS所需要的动态库文件。

末之 1359

vCenter Server出现no healthy upstream的解决方法

可以通过"VMware vCenter Server Management(服务)"来排查问题,在网页里面找到“服务”模块,会发现有很多服务没有启动起来,而且手动启动也无法启动。7.0 地址后,页面出现“no healthy upstream”,无法正常登录vCenter,重启后依旧如此,该故障的前提是没有对vCenter做过任何配置,如下图所示。于是用命令查询是否有证书过期,发现真有多个服务依赖的证书在当天早上过期。使用如下命令尝试打开服务发现不分服务可以打开,不分服务无法打开。

1万+

php内核源码解析=扩展开发进阶——高级扩展技术

&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="UTF-8"&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt; &lt;title&gt;Document&lt;/title&gt; &lt;style&gt; *{ margin: 0;

qq_37805832的博客 863

U3d WebGL使用ToLua

首先我们在编辑器模式下加入Tolua插件,然后配置修改到在编辑器模式下能够正常的运行。 同个U3d 插件Build出WebGL的工程,运行的时候你会发现(有些需要把这个WebGL工程放在一个HttpServer下面,直接本地访问index.html会被拒绝,让你使用http的格式)。 当要加载完的时候会出现 To use dlopen, you need to use Emscripten'...

qq_35267906的博客 2947

仓颉编程语言开发指南:从工具链安装、核心语法到宏与并发编程的全链路实战手册

本篇技术指南以本仓库 [docs/dev_guide.md](https://link.gitcode.com/i/94447c2e77ab06f8c6a1db44cdb18835)(仓颉语言开发指南)为绝对主体,系统梳理仓颉编程语言(Cangjie)从环境搭建、基础语法、面向对象与泛型、模式匹配、异常与并发,到宏元编程、编译构建与部署运行的完整知识链路。读者学完后,将能够独立完成仓颉工具链的安装

gitblog_00809的博客 341

FPGA_AD9361实战

1.参考网站。

Pual_Georg的博客 748

Power BI----各类切片器的使用

首先介绍切片器的最基础的创建和效果演示,紧跟着介绍切片器在多页中的同步和本身与报表的联动,再然后介绍关于切片器属性的设置,使得交互更加美观,最后介绍几种很好看的第三方的切片器样式和自己构建的可折叠的切片器.........

Itsme_MrJJ的博客 2万+

突破性方案:解决Windows Vista/Server 2008系统Python 3.8+兼容性问题的完整技术实现

在Python 3.8版本之后,官方正式放弃了对Windows Vista SP2和Windows Server 2008 SP2操作系统的支持,这一技术决策导致大量仍在使用这些经典系统的企业环境和遗留设备无法享受Python最新版本带来的性能优化、安全增强和新功能特性。PythonVista项目通过深度技术修改,成功解决了这一系统兼容性难题,为老旧Windows系统提供了完整的Python 3.

gitblog_00780的博客 1088

用 Custom Entity 打造 RAP Value Help:从能跑到好用的深度踩坑笔记

本文介绍了在RAP项目中实现Value Help(Fiori输入帮助)的完整流程。通过Custom Entity方式,开发者可以灵活构建不依赖数据库实体的值帮助功能,但需手动实现CDS原本提供的过滤、排序、分页等能力。文章以一个典型场景为例,详细说明了如何为Partner维护界面的City字段创建可搜索的值帮助,包括创建查询实现类、绑定CDS Custom Entity、服务发布等关键步骤,并特别指出了处理排序分页请求等常见问题。最后强调在ABAP Cloud环境下需要自行实现查询能力,而非依赖传统工具类。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 140

Windows 11 + WSL2 环境复现WAIC多智能体协作演示全流程指南

多智能体(Multi-Agent)协作是当前人工智能领域的重要发展方向,其核心原理在于通过多个具备特定能力的AI智能体(Agent)进行分工、通信与协同,以完成复杂任务。这种架构的技术价值在于能够突破单一模型的局限性,实现更强大的问题解决能力和更接近人类团队的工作流程。其典型应用场景包括自动化软件开发、复杂决策分析、智能客服系统等。本文聚焦于在Windows 11平台上,借助WSL2(Windows Subsystem for Linux 2)技术,搭建一个兼容Linux生态的AI开发环境,并基于Dify等

weixin_34318272的博客 351

实战:RK3568 Android14 调试 ES8388 音频驱动 (MIC 篇)

这个板子的MIC设计使用了硬件自动切换方案,极大简化了软件层面的逻辑。我们不需要根据耳机插拔事件去编写复杂的路由切换代码,只需要守住 LIN1/RIN1 这一个入口,配置好增益,剩下的交给那颗 SGM3157 芯片即可。祝您的录音功能调试顺利!

qq_33427070的博客 1680

【100%通过率】2024华为OD机试真题E卷 C++ 实现【栈数据合并/空栈压数】

向一个空栈压入正整数,每当压入一个整数时,执行以下规则(设: 栈顶至栈底整数依次编号为n1、n2...nx, n1为最新压入的整数)

MISAYAONE的博客 1553

WSL2开发环境全攻略:从安装配置到性能调优与避坑实践

WSL2是Windows内置的轻量级虚拟化方案,基于Hyper-V运行完整Linux内核,通过9P协议实现文件系统互通,启动仅需1-2秒且内存按需分配。相比传统虚拟机和云服务器,WSL2在资源占用、文件互操作和本地调试上优势明显,尤其适合Python、Node.js、Docker等开发场景。其技术价值在于兼顾Linux完整能力与Windows宿主便利性,但需注意文件系统性能差异——项目应放在Linux侧而非/mnt/c/下。本文围绕WSL2的安装配置、资源分配、Docker部署、网络排查等核心环节,提供从入

weixin_34326429的博客 266

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

CentOS 7 Linux系统基础 03 IP地址虚拟网络、Linux软件包管理、ssh服务、apache服务和samba服务的简单搭建

CentOS 7 Linux系统基础 03 IP地址虚拟网络、Linux软件包管理、ssh服务、apache服务和samba服务的简单搭建

weixin_42830015的博客 2930

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

WSL2与Ubuntu 22.04开发环境搭建与优化指南

Windows Subsystem for Linux (WSL) 是微软推出的Linux兼容层,允许开发者在Windows系统上直接运行Linux环境。WSL2作为第二代产品,采用轻量级虚拟机架构,性能接近原生Linux系统。通过虚拟化技术实现内核级兼容,特别适合需要跨平台开发的场景。本文以Ubuntu 22.04 LTS为例,详细介绍WSL2环境搭建、存储迁移、网络配置等核心技巧,并涵盖Docker集成、VS Code联动等生产力工具配置方案。针对企业开发环境中常见的虚拟化限制、证书错误等问题提供解决方

weixin_33976072的博客 362

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Windows系统通过WSL2安装配置OpenClaw开源工具全攻略

在自动化运维与安全研究领域,命令行工具集是提升效率的核心组件。其原理在于通过脚本化和模块化设计,将复杂的手动操作转化为可重复执行的指令序列,从而实现任务自动化。这类工具的技术价值在于显著减少重复劳动、降低人为错误,并支持大规模、标准化的系统管理与数据采集。典型的应用场景包括批量服务器配置、安全漏洞扫描、日志分析与信息收集等。为了在Windows操作系统上高效运行此类通常基于Linux生态的工具,Windows Subsystem for Linux (WSL) 提供了完美的解决方案。WSL2通过轻量级虚拟化

weixin_33717117的博客 327

Win11 + Docker 部署 OpenClaw 自动化框架实战指南

OpenClaw 是一种面向智能体协同与任务编排的轻量级自动化运行时框架,其核心价值在于将自然语言指令精准转化为可审计、可调试的确定性动作。依托 WSL2 和 Docker Desktop,Win11 提供了近乎原生的 Linux 容器运行环境,显著优于 Win10 的虚拟化兼容性与服务稳定性。通过 Alpine 基础镜像构建、技能目录挂载、host 网络模式配置及 Windows 服务集成,OpenClaw 可实现开机自启、崩溃自恢复与右键菜单触发等桌面级体验。本文聚焦生产就绪部署路径,涵盖虚拟化验证、资

weixin_30439067的博客 470
上一篇: Linux系统调用(syscall)原理
下一篇: 关于安卓APP加固基础的总结
mergerly
博客等级 码龄22年 953粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值