php7 c 扩展调用so库,PHP7.4支持调用动态链接库中的C函数了

鼓掌,撒花

本篇文章给大家带来的内容是关于PHP7.4中FFI的介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

FFI扩展已经通过RFC,正式成为PHP 7.4核心扩展。

什么是FFI

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

FFI的使用非常简单,只用声明和调用两步就可以,对于有C语言经验,但是不了解Zend引擎的程序员来说,这简直是打开了新世界的大门,可以快速地使用C类库进行原型试验。

(此处有图:溜了溜了,要懂C的……)

下面通过2个例子,看一下FFI是怎样使用的。

Libbloom

libbloom是一个C实现的bloom filter,比较知名的用户有Shadowsocks-libev,下面看一下怎样通过FFI在PHP里调用libbloom。

第一步,从头文件bloom.h把主要的数据结构和函数声明复制出来:

$ffi = FFI::cdef("

struct bloom

{

int entries;

double error;

int bits;

int bytes;

int hashes;

double bpe;

unsigned char * bf;

int ready;

};

int bloom_init(struct bloom * bloom, int entries, double error);

int bloom_check(struct bloom * bloom, const void * buffer, int len);

int bloom_add(struct bloom * bloom, const void * buffer, int len);

void bloom_free(struct bloom * bloom);

", "libbloom.so.1.5");

FFI目前不支持预处理器(除了FFI_LIB和FFI_SCOPE),所以宏定义要自己展开。

之后就可以通过$ffi创建已声明的数据结构和调用函数:

// 创建一个bloom结构体,然后用FFI::addr取地址

// libbloom的函数都是使用bloom结构体的指针

$bloom = FFI::addr($ffi->new("struct bloom"));

// 调用libbloom的初始化函数

$ffi->bloom_init($bloom, 10000, 0.01);

// 添加数据

$ffi->bloom_add($bloom, "PHP", 3);

$ffi->bloom_add($bloom, "C", 1);

// PHP可能存在

var_dump($ffi->bloom_check($bloom, "PHP", 3));     // 1

// Laravel不存在

var_dump($ffi->bloom_check($bloom, "Laravel", 7)); // 0

// 释放

$ffi->bloom_free($bloom);

$bloom = null;

Linux Namespace

Linux命名空间是容器技术的基石之一,通过FFI可以直接调用glibc的对应系统调用封装,从而通过PHP实现容器。下面是一个让bash在一个新的命名空间里运行的例子。

首先是一些常量,可以从Linux的头文件得到:

// clone

const CLONE_NEWNS     = 0x00020000; // mount namespace

const CLONE_NEWCGROUP =    0x02000000; // cgroup namespace

const CLONE_NEWUTS    = 0x04000000; // utsname namespace

const CLONE_NEWIPC    = 0x08000000; // ipc namespace

const CLONE_NEWUSER   = 0x10000000; // user namespace

const CLONE_NEWPID    = 0x20000000; // pid namespace

const CLONE_NEWNET    = 0x40000000; // network namespace

// mount

const MS_NOSUID  = 2;

const MS_NODEV   = 4;

const MS_NOEXEC  = 8;

const MS_PRIVATE = 1 <

const MS_REC     = 16384;

接着时我们要用到的函数声明:

$cdef="

// fork进程

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

// 挂载文件系统

int mount(const char *source, const char *target, const char *filesystemtype,

unsigned long mountflags, const void *data);

// 设置gid

int setgid(int gid);

// 设置uid

int setuid(int uid);

// 设置hostname

int sethostname(char *name, unsigned int len);

";

$libc = FFI::cdef($cdef, "libc.so.6");

定义我们的子进程:

// 生成一个容器ID

$containerId = sha1(random_bytes(8));

// 定义子进程

$childfn = function() use ($libc, $containerId) {

usleep(1000); // wait for uid/gid map

$libc->mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, null);

$libc->setuid(0);

$libc->setgid(0);

$libc->sethostname($containerId, strlen($containerId));

pcntl_exec("/bin/sh");

};

在子进程里,我们重新挂载了/proc,设置了uid、gid和hostname,然后启动/bin/sh。

父进程通过clone函数,创建子进程:

// 分配子进程的栈

$child_stack  = FFI::new("char[1024 * 4]");

$child_stack = FFI::cast('void *', FFI::addr($child_stack)) - 1024 * 4;

// fork子进程

$pid = $libc->clone($childfn, $child_stack, CLONE_NEWUSER

| CLONE_NEWNS

| CLONE_NEWPID

| CLONE_NEWUTS

| CLONE_NEWIPC

| CLONE_NEWNET

| CLONE_NEWCGROUP

| SIGCHLD, null);

// 设置UID、GID映射,把容器内的root映射到当前用户

$uid = getmyuid();

$gid = getmyuid();

file_put_contents("/proc/$pid/uid_map", "0 $uid 1");

file_put_contents("/proc/$pid/setgroups", "deny");

file_put_contents("/proc/$pid/gid_map", "0 $gid 1");

// 等待子进程

pcntl_wait($pid);

glibc的clone函数是clone系统调用的封装,它需要一个函数指针作为子进程/线程的执行体,我们可以直接把PHP的闭包和匿名函数当作函数指针使用。

运行效果:

$ php container.php

sh-5.0# id      # 在容器内是root

uid=0(root) gid=0(root) groups=0(root),65534(nobody)

sh-5.0# ps aux  # 独立的PID进程空间

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

root         1  0.0  0.1  10524  4124 pts/1    S    10:19   0:00 /bin/sh

root         3  0.0  0.0  15864  3076 pts/1    R+   10:19   0:00 ps aux

sh-5.0# ip a  # 独立的网络命名空间

1: lo:  mtu 65536 qdisc noop state DOWN group default qlen 1000

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WinCC是西门子为工业自动化领域开发的一套SCADA软件,适用于各种不同的工业控制系统。WinCC7.4脚本函数是其的一个重要特性,它可以让WinCC用户轻松地实现自定义功能和业务逻辑。下面详细介绍一下WinCC7.4脚本函数。 1、脚本函数是什么? 脚本函数是WinCC7.4软件一种编写自定义功能和业务逻辑的方法。它是使用VBScript、JScript等脚本语言编写的小型程序,可以嵌入WinCC文本、按钮、报警、趋势图等图形对象,并在运行时进行执行。WinCC7.4脚本函数提供了大量的API,用户可以根据自己的需求选择并调用API函数,实现定时器、报警处理、订单处理、数据写入等各种功能。 2、WinCC7.4脚本函数有哪些特点? (1)WinCC7.4脚本函数是一种灵活性很高的编程方式,用户可以在不修改WinCC系统源代码的情况下,自定义功能和业务逻辑,提高WinCC系统的可扩展性和可维护性。 (2)WinCC7.4脚本函数的编写非常简单,只需要熟悉VBScript、JScript等脚本语言的语法规则就可以了,不需要深入掌握复杂的面向对象编程技术。 (3)WinCC7.4脚本函数可以灵活运用于各种WinCC图形对象,如文本、按钮、报警、趋势图等,实现不同的功能。 (4)WinCC7.4脚本函数可以与WinCC内置对象、COM对象、数据等进行交互,实现数据的存取操作。同时,WinCC7.4脚本函数还可以与其他系统进行集成,如ERP、MES等系统。 3、WinCC7.4脚本函数如何使用? (1)首先,需要打开WinCC7.4软件,在项目窗口选择需要添加脚本函数的对象,如文本、按钮、报警、趋势图等。 (2)在对象属性找到Script Name标签,点击右侧的“…”按钮,进入脚本编辑界面。 (3)在脚本编辑界面输入VBScript、JScript等脚本语言的代码,调用WinCC提供的API函数实现自定义功能和业务逻辑。 (4)完成脚本编写后,保存并退出编辑界面,然后进行编译和上传操作,即可在运行时实现自定义功能和业务逻辑。 总之,WinCC7.4脚本函数是WinCC系统非常重要的特性,它可以让用户轻松地扩展和定制自己的WinCC系统,提高生产效率和质量。如果你是一位WinCC用户,不妨了解一下WinCC7.4脚本函数的使用方法,相信它会给你带来更多的惊喜和收获。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值