”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > php:与进程的并发。角与 shmop 的进程间通信

php:与进程的并发。角与 shmop 的进程间通信

发布于2024-11-07
浏览:362

php isn't the sort of language where developers usually think about things like memory. we just sort of sling around variables and functions and let the internals figure out all that 'ram stuff' for us. let's change that.

in the first part of this series, we built a php script that was able to run a number of tasks concurrently by forking child processes. it worked pretty well, but there was a glaring, unaddressed problem: there was no way for those child processes to send data back to the parent process.

in this installment, we're going to solve that issue by using shmop, php's "shared memory operations".

php: concurrency with processes. pt. interprocess communication with shmop
shmop!

contents

  • a very short and extremely optional explanation of shared memory
  • a basic flyover of shmop
  • an actual implementation
  • opening a memory block with shmop_open
  • using shmop_write to... write
  • reading memory with shmop_read
  • using shmop_delete to clean up
  • handling errors

a very short and extremely optional explanation of shared memory

when a new process starts, the operating system assigns it a chunk of memory to use. processes can't read or write to memory that isn't their own because, well, that would be a security nightmare. perfectly reasonable.

this creates an issue for us when dealing with the processes we created with pcntl_fork in part one of this series, however, because it means there's no easy way for the child processes to communicate with each other or their parent. child processes will get a copy of their parent's memory when they're created so all the variables assigned before the fork are accessible, but any changes to these variables will be limited to the child process. different memory and all that. if we want the child to be able to write to a variable that the parent process can read, we have a problem.

there are a number of solutions for this, all grouped under the general category of 'inter process communications' or ipc. the one we're going to use for our php script is 'shared memory'.

as the name implies, shared memory is a block of memory that an arbitrary number of processes can access. shared memory blocks are identified by a (hopefully) unique key. any process that knows what the key is can access that memory block. this makes it possible for a child processes to report back to their parent process; the child will write data to a shared memory block and, after it quits, the parent will read the shared data. it's a borderline elegant solution.

of course, there are a few footguns we will need to avoid when doing this: we will need to ensure that the key that identifies a shared memory block is unique, and we will need to enforce that shared memory communication only goes one way to avoid multiple processes all trying to write to the same block at the same time and causing a mess. we'll cover all this in the implementation.

a basic flyover of shmop

php has a rich and robust api for dealing with shared memory. the manual states "Shmop is an easy to use set of functions that allows PHP to read, write, create and delete Unix shared memory segments", and... it's not wrong.

let's look at the core steps for using shared memory:

create a unique key: all shared memory is identified by a key. any process that knows what the key of a shared memory block is can access it. traditionally, this key is created by generating data from the filesystem (ie. a value built from the inode of an existing file) because the filesystem is something all processes have in common. we will use ftok for this.

assign a memory block using the key: a process can use the key of a shared memory block to 'open' it using shmop_open. if the shared memory block does not exist, this creates it. the return value from the open function is pointer that can be used for reading and writing. if you've ever used fopen and fwrite before, this process should be familiar.

write data to the memory block: writing to shared memory has a very similar interface as fwrite. the pointer is used, and the string to write to memory is passed as an argument. the function to do this is called shmop_write.

read data from the memory block: reading data from shared memory is done with shmop_read, again using the pointer from shmop_open. the return value is a string.

delete the memory block using the key: deleting shared memory after it's no longer needed is important. this is done with shmop_delete.

the actual implementation

let's start with an example. the code below works and, if you are sufficiently un-curious or a tl;dr-type, you can just copy-paste-modify this, but for everyone else, we'll go over all the shmop steps and explain what they do and how they work.

// the file used by ftok. can be any file.
$shmop_file = "/usr/bin/php8.3";

for($i = 0; $i  range('a', 'z')[rand(0, 25)], range(1,5)));

        // write to shmop
        $shm_key = ftok($shmop_file, $i);
        $shm_id = shmop_open($shm_key, 'n', 0755, 1024);
        shmop_write($shm_id, $random_word, 0);

        print "child $i wrote '$random_word' to shmop".PHP_EOL;

        // terminate the child process
        exit(0);
    }
}

// wait for all child processes to finish
while(($pid = pcntl_waitpid(0, $status)) != -1) {
    echo "pid $pid finished".PHP_EOL;
}

// read all our shared memories
for($i = 0; $i 



creating a shared memory key with ftok

as we covered above, all shared memory blocks are identified by a unique integer key and before we can get down to the task of assigning memory we have create that key.

in all honesty, we can use any integer we want to, so long as it is unique, however the generally accepted, canonical way to do this is by using ftok to create an integer using an existing file in the filesystem as a reference point.

the rationale for doing this is pretty straightforward. processes don't know anything about each other, which makes it difficult for them to share a mutually agreed-upon value. one of the few things all processes on a system do have in common, though, is the filesystem. hence, ftok.

in addition to the path to an existing file, ftok also takes a project_id argument. this is, according to the docs, a 'one character string', what people in every other programming language would call a 'char'. the purpose of the project id is to prevent collisions when creating shared memory. if two projects by two separate vendors both decided to use /etc/passwd as their argument to ftok, chaos would ensue.

let's look at a fairly straightforward example:

$shm_key = ftok('/usr/bin/php8.3', 'j');
print "shm_key = $shm_key";

here we are passing the full path to a file that we know exists on the system and providing a one character project_id, 'j'. if we run this, the print statement will output something like:

shm_key = 855706266

that's a good integer to use for creating our shared memory!

if you run this code on your system, you will almost certainly get a different return value, even though you have used the same arguments. this is because, under the hood, ftok uses the inode of the file, and that is different from system to system.

if, for some reason, we pass to ftok a file that doesn't exist, we get a warning.

PHP Warning:  ftok(): ftok() failed - No such file or directory in  on line 
shm_key = -1

note that this is only a warning and ftok will charge ahead and give us a value of -1, which will result in problems down the road. be careful.

now, let's revist our call to ftok on line 20:

$shm_key = ftok($shmop_file, $i);

here we have passed ftok the path of the file we have set in $shm_key, in this case /usr/bin/php8.3, a file we know exists on the system.

for our project_id we are using $i, the index of the array we are looping over. we are doing this so that each of our child processes has it's own shared memory block to store its results. remember that if more than one process tries to write to shared memory, Bad Things happen. using the index here helps us avoid that.

opening a memory block with shmop_open

if you've ever done file access with tools like php's fopen and fwrite, then using shmop will be very familiar.

let's start with opening a shared memory block with shmop_open:

$shm_id = shmop_open($shm_key, 'n', 0755, 1024);

this function takes four arguments:

  • key: the unique key we created using ftok.
  • mode: the type of access we want. when opening a file with fopen we use modes like r for 'read' or w for write. shmop_open's mode is similar to that, but there are differences. we'll go over all those below.
  • permissions: the read/write/execute permissions of the shared memory block in octal notation. the interface for dealing with shared memory is strongly analogous to file access, and that includes permissions. if you're not confident with octal notation, there are file permissions calculators you can use. we're using 0755 in this example, but you may want to tighten that up.
  • size: the size of the memory block in bytes. in the example, we are assigning one megabyte, which is clearly overkill. however, note that if we overwrite our shared memory block, the value will be truncated. if we try to read more bytes from a memory block than its size, a fatal error occurs. if we are unsure of the exact size of the data we will be writing to memory, it is better to overestimate how big we need our memory block to be.

an important note here is that calling shmop_open will create a new memory block if one at that key doesn't already exist. this is similar to how fopen behaves, but with shmop_open this behaviour is dependent on the 'mode' argument we pass.

as shown in the example, shmop_open returns a pointer that can be used for access: reading or writing, depending on the mode used to open the memory block.

php: concurrency with processes. pt. interprocess communication with shmop
007 are bad permissions for a spy

a little bit more about that 'mode' argument

the mode argument that we pass to shmop_open determines how we can access our shared memory block. there are four options, all covered in the official documentation, but for the sake of simplicity, we'll only look at the two we need for our purposes.

  • n : the 'n' stands for 'new' and is used when we want to create a new shared memory block. there does exist a mode c for 'create', but we are choosing to use n here because this mode will fail if we try to open a memory block that already exists. that's a safety feature! in fact, the docs state that using n to create a new shared memory block is 'useful' for 'security purposes'. the pointer returned from shmop_open using mode n is writeable.
  • a : this is for 'access'; ie. reading. do not confuse this with fopen's a mode, which is for 'append'. memory blocks opened with mode a are read-only.

if we look at the example, we can see that when we open the shared memory block in the child process to write our data, we use the n mode. this creates the new memory block in a safe way and returns a pointer that we can write to.

using shmop_write to... write.

once our child process has created a new shared memory block and received a pointer to it, it can write whatever it wants there using shmop_write.

in our example, doing this looks like:

shmop_write($shm_id, $random_word, 0);

the shmop_write function takes three arguments:

  • the pointer: the pointer returned from shmop_open. note that shmop_open must be called with a mode that allows writing (n in our example), otherwise attempts to use shmop_write will fail.
  • the value to write: the string to write to the shared memory block.
  • the offset: the number of bytes in memory to offset the start point of the write by. using the offset can allow us to append to a value already in the shared memory block, but doing this means keeping track of bytes written and can become unmanageable pretty quickly. in our example, we use the offset 0; we start writing at the beginning of the memory block.

shmop_write returns, as an integer, the number of bytes written.

a short note about shmop_close

if you've done file access using fopen, you're probably (hopefully!) in the habit of calling fclose when you're done writing.

we do not do that with shmop.

there is a shmop_close function, but it has been deprecated since php 8.0 and does nothing (other than throw a deprecation warning, that is). the standard practice with shmop is to just leave the pointer 'open' after we're done writing. we'll delete it later.

reading from shared memory

once all the child processes have written their data to their respective shared memory blocks an exited, all that remains is for the parent process to read that data. the strategy for this is:

  • recreate the key of the shared memory block
  • use the key to open the shared memory in 'access only' mode
  • read the data into a variable

let's look again at the example we have for reading shared memory.

// read all our shared memories
for($i = 0; $i 



recreating the shmop key

when we made the key to create our shared memory blocks, we used ftok with two arguments: the path an existing file in the filesystem, and a 'project id'. for the project id, we used the index of the array we looped over to fork multiple children.

we can use the exact same strategy to recreate the keys for reading. as long as we input the same two arguments into ftok, we get the same value back.

opening the shared memory

we open the shared memory block for reading almost exactly the same way as we did above for writing. the only difference is the mode.

for reading, we use the a mode. this stands for 'access', and gives us a read-only pointer to our shared memory block.

reading from the shared memory block

once we have a pointer to our shared memory block, we can read from it using shmop_read.

shmop_read takes three arguments:

  • the pointer we got from shmop_open.
  • the offset, in bytes. since we are reading the entirety of the memory block, starting at the beginning, this is 0 in our example (and will probably be for most real-life uses, as well)
  • the number of bytes to read. in most cases, the smart thing here is to just read the entire size of the block, in our example 1024 bytes.

the return type is a string. if there are errors reading, we get a boolean false.

deleting shared memory blocks

once we are done reading our shared memory, we can delete it.

this is an important step. unlike variables in our script, the memory we assigned with shmop will persist after our program has exited, hogging resources. we do not want to litter our system with blocks of unused, reserved memory, piling up higher and higher with each successive run of our script!

freeing up shared memory blocks is done with shmop_delete. this function takes one argument: the pointer we created with shmop_open, and returns a boolean true on success.

note that shmop_delete destroys the memory block and frees up the space for other applications to use. we should only call it when we're completely done with using the memory.

handling errors

the example we've been going over doesn't really do any error handling. this is a decision borne out of a desire for brevity, not delusional optimism. in real applications we should certainly do some error testing!

we used a path to a file as an argument for ftok; we should test that it exists. shmop_write will throw a value error if our memory block is opened read-only or we overwrite its size. that should be handled. if there's a problem reading data, shmop_read will return false. test for that.

php: concurrency with processes. pt. interprocess communication with shmop
i am asking you to do some error handling

fixing 'already exists' errors with shmop_open

if we open a shared memory block and then the script terminates before we call shmop_delete, the memory block still exists. if we then try to open that memory block again with shmop_open using the n mode, we will get the error:

PHP Warning:  shmop_open(): Unable to attach or create shared memory segment "File exists" in /path/to/my/script on line 

if our script is well-designed, this shouldn't happen. but, while developing and testing we may create these orphaned memory blocks. let's go over how to delete them.

the first step is to get the key of the memory block as a hex number. we do this by calling ftok as normal, and then converting the returned integer from base ten to base-16 like so:

$shm_key = ftok($shmop_file, $i);
$shm_key_hex = "0x".base_convert($shm_key, 10, 16);

we do this because linux comes with a number of 'interprocess communication' tools that we can use to manage shared memory blocks, and they all use hexadecimal numbers for their keys.

the first command line tool we'll use is ipcs. we're going to use this to confirm that the shared memory block we want to delete does, in fact, exist.

the ipcs command, when run without arguments, will output all interprocess communication channels, including all shared memory blocks. we'll narrow down that output by using grep with the hexadecimal key we created above. for instance, if our shared memory block's key in hexadecimal is 0x33010024, we could do this:

ipcs | grep "0x33010024"

if we get a line of output, the memory block exists. if nothing is returned, it does not.

once we've confirm that a shared memory block exists, we can remove it with ipcrm

ipcrm --shmem-key 0x33010024

knowing how to inspect and clean up (without resorting to a restart) shared memory allows us to develop and experiment without turning our ram into a ghost town of abandoned blocks.

wrapping up

achieving concurrency in php using fork and shared memory does take some effort and knowledge (and the official manual is scant help). but it does work and, if you've made it through this article and the first installment on pcntl_fork, you should have a good base from which to start.

? this post originally appeared in the grant horwood technical blog

版本声明 本文转载于:https://dev.to/gbhorwood/php-concurrency-with-processes-pt-2-interprocess-communication-with-shmop-555k?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何在 Mac OS X 上有效管理多个 Java 版本?
    如何在 Mac OS X 上有效管理多个 Java 版本?
    在 Mac OS X 上管理多个 Java 版本在 Mac OS X 上进行开发时,对于具有不同兼容性要求的各种项目可能需要多个 Java 版本。本文探讨了如何在 Mac 上有效地安装和管理多个 Java 版本。Homebrew 方法Homebrew 是一款流行的 Mac 包管理器,为管理多个 Ja...
    编程 发布于2024-11-07
  • 掌握 Neowith Java:设置、查询、事务和可视化
    掌握 Neowith Java:设置、查询、事务和可视化
    Neo4j 是一个强大的图形数据库,擅长管理高度互联的数据。当与 Java 结合使用时,它为构建需要复杂关系建模的应用程序提供了强大的解决方案。这篇文章将引导您了解在 Java 中使用 Neo4j 的基础知识,包括设置、查询和最佳实践。 使用 Java 设置 Neo4j 首先,您需要...
    编程 发布于2024-11-07
  • JavaScript 中最大的错误(以及如何避免它们)
    JavaScript 中最大的错误(以及如何避免它们)
    JavaScript 是一种非常强大且适应性强的语言,但它也可能存在难以检测的问题。在这篇博客文章中,我们将探讨开发人员在使用 JavaScript 时发现的五个最常见的缺陷,以及这些问题的原因和解决方案。无论您是经验丰富的开发人员还是刚刚起步的开发人员,了解这些常见危险都会为您节省故障排除时间。 ...
    编程 发布于2024-11-07
  • 限制 Laravel 模型上的急切加载关系
    限制 Laravel 模型上的急切加载关系
    介绍 有时,当您渴望在 Laravel 模型上加载关系时,您可能希望限制返回的相关模型的数量。 例如,在博客平台上,您可能希望加载系统中的每位作者及其三篇帖子。 在 Laravel 的旧版本中,限制急切加载的关系是一项有点繁琐的任务。我从来没有真正找到一种感觉正确的优雅方式来做到这...
    编程 发布于2024-11-07
  • 如何使用 GDB 在 C++ 中打印向量元素?
    如何使用 GDB 在 C++ 中打印向量元素?
    通过 GDB 在 C 中打印向量元素在 GDB 中调试 C 代码时,检查 std::vector 的内容可能具有挑战性。例如,考虑一个名为 myVector 的 std::vector。我们如何有效地打印它的元素?在 GCC 4.1.2 中,解决方案涉及访问向量的内部指针 myVector._M_i...
    编程 发布于2024-11-07
  • 如何在不同浏览器中自定义下拉列表宽度?
    如何在不同浏览器中自定义下拉列表宽度?
    IE 下拉列表宽度修改在 Internet Explorer 中,下拉列表镜像其保管箱的宽度,而在 Firefox 中,它会适应内容。此限制需要扩展保管箱以容纳最长的选择,从而导致网页美观不美观。基于 CSS 的可变宽度解决方案要克服此问题,使用 CSS 允许下拉框和下拉列表使用不同的宽度,请考虑以...
    编程 发布于2024-11-07
  • 在 C++ 中格式化时如何右对齐输出字符串?
    在 C++ 中格式化时如何右对齐输出字符串?
    在 C 中通过右对齐格式化输出字符串 处理包含数据(例如坐标)的文本文件时,需要对齐列中的项目经常出现正确格式化的问题。在 C 中,输出字符串的操作对于实现这种对齐至关重要。本文解决了输出字符串右对齐的问题,提供了使用标准 C 技术的解决方案。为了处理输入文本文件,使用 line.split() 函...
    编程 发布于2024-11-07
  • CSS 渐变生成器
    CSS 渐变生成器
    欢迎来到“免费 CSS 工具”系列。 在本系列中,我们将找到完全免费且易于使用的 CSS 工具。 在解释了如何使用该工具后,我将与您分享该工具的链接。 工具链接:此工具可在 webdevtales.com 上获得 工具1:CSS渐变生成器 工具查看: 介绍 欢迎使用 CSS 渐变生...
    编程 发布于2024-11-07
  • 为什么小型函数会让你成为编码英雄的原因
    为什么小型函数会让你成为编码英雄的原因
    嘿,代码爱好者们! ?您是否曾经发现自己迷失在无尽的线条海洋中,想知道一个功能在哪里结束,另一个功能从哪里开始?我们都去过那里。今天,我们来谈谈为什么将代码分解成更小的、可管理的块不仅仅是一种最佳实践——它还能改变你的开发技能和职业生涯。 1.未来的你会感谢你 想象一下:现在是凌晨 ...
    编程 发布于2024-11-07
  • JavaScript 变量名称中美元符号的含义是什么?
    JavaScript 变量名称中美元符号的含义是什么?
    为什么在 JavaScript 变量名称中使用美元符号?提供的 JavaScript 代码包含一个名为“$item”的变量,该变量引发问题:变量名中美元符号的用途是什么?在 JavaScript 中,变量名前面的美元符号对于解释器来说没有特殊含义。它用作轻松识别包含 jQuery 对象的变量的约定,...
    编程 发布于2024-11-07
  • Laravel 中的授权 - 初学者指南
    Laravel 中的授权 - 初学者指南
    掌握 Laravel 中的授权:Gates 与策略类 ?? 在现代 Web 应用程序中,控制谁可以访问或修改资源至关重要。例如,在博客应用程序中,您可能希望确保只有帖子的所有者才能编辑或删除它。 Laravel 提供了两种优雅的方式来处理授权:Gates 和 Policy Clas...
    编程 发布于2024-11-07
  • Laravel 的枚举
    Laravel 的枚举
    报告 在我从事的一个项目中,有一个选择字段定义了不会更改的值。因此,为了列出此选择中的项目,我决定创建一个枚举类,然后描述这些值。但是,该项目需要支持英语和西班牙语,并且选择选项的文本需要适应这一点,同时又不丢失对相应枚举项的引用。换句话说,如果我选择了“马”这个项目,我需要系统知道这个项目仍然是“...
    编程 发布于2024-11-07
  • \“模块 vs 主要:现代英雄 vs package.json 的复古传奇!\”
    \“模块 vs 主要:现代英雄 vs package.json 的复古传奇!\”
    什么是模块字段? package.json 中的 module 字段指定 ESM(ES6 模块) 的入口点。与为 CommonJS 模块 (require()) 设计的 main 字段不同,模块用于支持较新的 ESM 标准的目标环境,例如 JavaScript 捆绑程序(Webpa...
    编程 发布于2024-11-07
  • 如何在 CSS 文件中实现类似变量的行为?
    如何在 CSS 文件中实现类似变量的行为?
    CSS 文件中的变量声明和使用在 CSS 中,通常需要在整个样式表中重用特定值。虽然没有明确的变量声明语法,但有一些技术可以实现此功能。一种方法是利用 CSS 选择器和样式规则。通过将相关样式组合在单个规则下,您可以避免重复,同时澄清每种样式的范围。例如:/* Theme color: text *...
    编程 发布于2024-11-07
  • 如何在 PHP 中编写基本函数来从文本中删除表情符号?
    如何在 PHP 中编写基本函数来从文本中删除表情符号?
    用 PHP 编写一个简单的 removeEmoji 函数处理在线文本通常需要删除表情符号,特别是在 Instagram 评论等情况下。本文探讨了针对这种需求的解决方案,利用 PHP preg_replace 函数来有效地消除给定文本中的表情符号。removeEmoji 函数利用一系列正则表达式来匹配...
    编程 发布于2024-11-07

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3