」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > php:與進程的並發。角與 shmop 的進程間通信

php:與進程的並發。角與 shmop 的進程間通信

發佈於2024-11-07
瀏覽:530

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]刪除
最新教學 更多>
  • 限制 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_impl...
    程式設計 發佈於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 Cl...
    程式設計 發佈於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 捆綁程式(Webpack、Ro...
    程式設計 發佈於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
  • Slim 和 Flight PHP 框架比較
    Slim 和 Flight PHP 框架比較
    为什么要使用微框架? 在社交媒体上,新的 PHP 开发人员经常会问“我的项目应该使用什么框架”,通常给出的答案是“Laravel”或“Symfony”。 虽然这些都是不错的选择,但这个问题的正确答案应该是“你需要框架做什么?” 正确的框架应该能够满足您的需要,并且不会包含大量您永远...
    程式設計 發佈於2024-11-07
  • 如何建立您的第一個 Python 遊戲:使用 PyGame 創建簡單射擊遊戲的逐步指南
    如何建立您的第一個 Python 遊戲:使用 PyGame 創建簡單射擊遊戲的逐步指南
    Hi lovely readers, Have you ever wanted to create your own video game? Maybe you’ve thought about building a simple shooter game where you can move ar...
    程式設計 發佈於2024-11-07
  • 為什麼我的 Java JDBC 程式碼在連接到 Oracle 時拋出“IO 錯誤:網路適配器無法建立連線”?
    為什麼我的 Java JDBC 程式碼在連接到 Oracle 時拋出“IO 錯誤:網路適配器無法建立連線”?
    診斷Oracle JDBC「IO 錯誤:網路適配器無法建立連線」嘗試使用JDBC 執行簡單的Java 程式碼時要連線到Oracle資料庫,您可能會遇到神秘的錯誤「IO 錯誤:網路適配器無法建立連線」。這個令人費解的消息源於 JDBC 驅動程式的模糊術語,並且可能由各種根本原因造成。以下是一些可能導致...
    程式設計 發佈於2024-11-07

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3