php: selaras dengan proses. pt. komunikasi antara proses dengan shmop

PHPz
Lepaskan: 2024-08-20 06:34:37
asal
525 orang telah melayarinya

php bukanlah jenis bahasa yang biasanya difikirkan oleh pembangun tentang perkara seperti ingatan. kita hanya semacam anduh sekitar pembolehubah dan fungsi dan biarkan dalaman memikirkan semua itu 'barangan ram' untuk kita. mari ubah itu.

dalam bahagian pertama siri ini, kami membina skrip php yang dapat menjalankan beberapa tugas secara serentak dengan memotong proses anak. ia berfungsi dengan baik, tetapi terdapat masalah yang ketara dan tidak ditangani: tiada cara untuk proses kanak-kanak tersebut menghantar data kembali ke proses induk.

dalam ansuran ini, kami akan menyelesaikan isu itu dengan menggunakan shmop, "operasi memori kongsi" php.

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

kandungan

  • penjelasan yang sangat singkat dan sangat pilihan tentang memori bersama
  • jejambat asas shmop
  • pelaksanaan sebenar
  • membuka blok memori dengan shmop_open
  • menggunakan shmop_write untuk... menulis
  • memori membaca dengan shmop_read
  • menggunakan shmop_delete untuk membersihkan
  • mengendalikan ralat

penjelasan yang sangat singkat dan sangat pilihan tentang memori yang dikongsi

apabila proses baharu bermula, sistem pengendalian memberikannya sebahagian memori untuk digunakan. proses tidak boleh membaca atau menulis ke ingatan yang bukan milik mereka kerana, tentu saja, itu akan menjadi mimpi ngeri keselamatan. sangat munasabah.

ini menimbulkan isu untuk kami apabila berurusan dengan proses yang kami buat dengan pcntl_fork dalam bahagian satu siri ini, walau bagaimanapun, kerana ini bermakna tiada cara mudah untuk proses kanak-kanak berkomunikasi antara satu sama lain atau ibu bapa mereka. proses anak akan mendapat salinan ingatan ibu bapa mereka apabila ia dibuat supaya semua pembolehubah yang ditetapkan sebelum garpu boleh diakses, tetapi sebarang perubahan pada pembolehubah ini akan terhad kepada proses anak. memori yang berbeza dan semua itu. jika kita mahu kanak-kanak itu boleh menulis kepada pembolehubah yang boleh dibaca oleh proses ibu bapa, kita mempunyai masalah.

terdapat beberapa penyelesaian untuk ini, semuanya dikumpulkan di bawah kategori umum 'komunikasi antara proses' atau ipc. yang kami akan gunakan untuk skrip php kami ialah 'memori kongsi'.

seperti namanya, memori kongsi ialah blok memori yang boleh diakses oleh bilangan proses yang sewenang-wenangnya. blok memori yang dikongsi dikenal pasti oleh kunci unik (mudah-mudahan). sebarang proses yang mengetahui kuncinya boleh mengakses blok memori itu. ini membolehkan proses kanak-kanak melaporkan kembali kepada proses induk mereka; kanak-kanak akan menulis data ke blok memori yang dikongsi dan, selepas ia berhenti, ibu bapa akan membaca data yang dikongsi. ia adalah penyelesaian elegan sempadan.

sudah tentu, terdapat beberapa senapang kaki yang perlu kita elakkan apabila melakukan ini: kita perlu memastikan bahawa kunci yang mengenal pasti blok memori dikongsi adalah unik, dan kita perlu menguatkuasakan bahawa komunikasi memori dikongsi hanya berjalan satu cara untuk mengelakkan pelbagai proses semua cuba menulis ke blok yang sama pada masa yang sama dan menyebabkan kekacauan. kami akan merangkumi semua ini dalam pelaksanaan.

jejambat asas shmop

php mempunyai api yang kaya dan mantap untuk menangani memori bersama. manual menyatakan "Shmop ialah set fungsi yang mudah digunakan yang membolehkan PHP membaca, menulis, mencipta dan memadamkan segmen memori kongsi Unix", dan... tidak salah.

mari lihat langkah teras untuk menggunakan memori kongsi:

cipta kunci unik: semua memori yang dikongsi dikenal pasti oleh kunci. sebarang proses yang mengetahui kunci blok memori yang dikongsi boleh mengaksesnya. secara tradisinya, kunci ini dicipta dengan menjana data daripada sistem fail (iaitu nilai yang dibina daripada inod fail sedia ada) kerana sistem fail ialah sesuatu yang mempunyai persamaan pada semua proses. kami akan menggunakan ftok untuk ini.

tetapkan blok memori menggunakan kekunci: proses boleh menggunakan kunci blok memori kongsi untuk 'membuka' menggunakan shmop_open. jika blok memori yang dikongsi tidak wujud, ini menciptanya. nilai pulangan daripada fungsi terbuka ialah penunjuk yang boleh digunakan untuk membaca dan menulis. jika anda pernah menggunakan fopen dan fwrite sebelum ini, proses ini sepatutnya biasa.

tulis data ke blok memori: menulis ke memori kongsi mempunyai antara muka yang hampir sama seperti fwrite. penunjuk digunakan, dan rentetan untuk menulis ke ingatan diluluskan sebagai hujah. fungsi untuk melakukan ini dipanggil shmop_write.

baca data daripada blok memori: membaca data daripada memori kongsi dilakukan dengan shmop_read, sekali lagi menggunakan penuding daripada shmop_open. nilai pulangan ialah rentetan.

padamkan blok memori menggunakan kekunci: memadamkan memori kongsi selepas ia tidak diperlukan lagi adalah penting. ini dilakukan dengan shmop_delete.

pelaksanaan sebenar

mari kita mulakan dengan contoh. kod di bawah berfungsi dan, jika anda cukup tidak ingin tahu atau jenis tl;dr, anda boleh salin-tampal-ubah suai ini, tetapi untuk orang lain, kami akan menyemak semua langkah shmop dan menerangkan perkara yang mereka lakukan dan cara ia berfungsi.

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

for($i = 0; $i < 4; $i++) {
    // create the fork
    $pid = pcntl_fork();

    // an error has ocurred
    if($pid === -1) {
        echo "error".PHP_EOL;
    }

    // child process
    else if(!$pid) {

        // create a random 'word' for this child to write to shared memory 
        $random_word = join(array_map(fn($n) => 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 < 4; $i++) {

    // recreate the shm key
    $shm_key = ftok($shmop_file, $i);

    // read from the shared memory
    $shm_id = shmop_open($shm_key, 'a', 0755, 1024);
    $shmop_contents = shmop_read($shm_id, 0, 1024);

    print "reading '$shmop_contents' from child $i".PHP_EOL;

    // delete the shared memory so the shm key can be reused in future runs of this script
    shmop_delete($shm_id);
}
Salin selepas log masuk

mencipta kunci memori kongsi dengan ftok

seperti yang kita bincangkan di atas, semua blok memori yang dikongsi dikenal pasti oleh kunci integer yang unik dan sebelum kita boleh turun ke tugas untuk memberikan memori, kita telah mencipta kunci itu.

sejujurnya, kita boleh menggunakan mana-mana integer yang kita mahu, asalkan ia unik, namun cara kanonik yang diterima umum untuk melakukan ini adalah dengan menggunakan ftok untuk mencipta integer menggunakan fail sedia ada dalam sistem fail sebagai titik rujukan.

rasional untuk melakukan ini agak mudah. proses tidak mengetahui apa-apa tentang satu sama lain, yang menyukarkan mereka untuk berkongsi nilai yang dipersetujui bersama. salah satu daripada beberapa perkara yang semua proses pada sistem lakukan mempunyai persamaan, walaupun, ialah sistem fail. oleh itu, ftok.

selain laluan ke fail sedia ada, ftok juga mengambil argumen project_id. ini, menurut dokumen, 'satu rentetan aksara', apa yang orang dalam setiap bahasa pengaturcaraan lain akan panggil 'char'. tujuan id projek adalah untuk mengelakkan perlanggaran semasa mencipta memori bersama. jika dua projek oleh dua vendor berasingan kedua-duanya memutuskan untuk menggunakan /etc/passwd sebagai hujah mereka kepada ftok, huru-hara akan berlaku.

mari kita lihat contoh yang agak mudah:

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

di sini kami menghantar laluan penuh ke fail yang kami tahu wujud pada sistem dan menyediakan project_id satu aksara, 'j'. jika kita menjalankan ini, pernyataan cetakan akan mengeluarkan sesuatu seperti:

shm_key = 855706266
Salin selepas log masuk

itu adalah integer yang bagus untuk digunakan untuk mencipta memori kongsi kami!

jika anda menjalankan kod ini pada sistem anda, anda hampir pasti akan mendapat nilai pulangan yang berbeza, walaupun anda telah menggunakan hujah yang sama. ini kerana, di bawah hud, ftok menggunakan inod fail, dan itu berbeza dari sistem ke sistem.

jika, atas sebab tertentu, kami menghantar ke ftok fail yang tidak wujud, kami mendapat amaran.

PHP Warning:  ftok(): ftok() failed - No such file or directory in <our script> on line <the line>
shm_key = -1
Salin selepas log masuk

perhatikan bahawa ini hanyalah amaran dan ftok akan mengecaj ke hadapan dan memberi kita nilai -1, yang akan mengakibatkan masalah di jalan raya. berhati-hati.

sekarang, mari kita semak semula panggilan kami ke ftok di talian 20:

$shm_key = ftok($shmop_file, $i);
Salin selepas log masuk

di sini kami telah melepasi ftok laluan fail yang telah kami tetapkan dalam $shm_key, dalam kes ini /usr/bin/php8.3, fail yang kami tahu wujud pada sistem.

untuk project_id kami, kami menggunakan $i, indeks tatasusunan yang kami gelungkan. kami melakukan ini supaya setiap proses anak kami mempunyai blok memori dikongsi sendiri untuk menyimpan hasilnya. ingat bahawa jika lebih daripada satu proses cuba menulis ke memori bersama, Perkara Buruk berlaku. menggunakan indeks di sini membantu kami mengelakkannya.

membuka blok memori dengan shmop_open

jika anda pernah melakukan akses fail dengan alatan seperti php's fopen dan fwrite, maka penggunaan shmop akan menjadi sangat biasa.

mari mulakan dengan membuka blok memori kongsi dengan shmop_open:

$shm_id = shmop_open($shm_key, 'n', 0755, 1024);
Salin selepas log masuk

fungsi ini memerlukan empat hujah:

  • kunci: kunci unik yang kami cipta menggunakan ftok.
  • mod: jenis akses yang kami mahukan. apabila membuka fail dengan fopen kami menggunakan mod seperti r untuk 'baca' atau w untuk tulis. Mod shmop_open adalah serupa dengan itu, tetapi terdapat perbezaan. kami akan membincangkan semua yang di bawah.
  • kebenaran: kebenaran baca/tulis/laksana blok memori kongsi dalam tatatanda perlapanan. antara muka untuk menangani memori yang dikongsi sangat serupa dengan akses fail, dan itu termasuk kebenaran. jika anda tidak yakin dengan tatatanda oktal, terdapat kalkulator kebenaran fail yang boleh anda gunakan. kami menggunakan 0755 dalam contoh ini, tetapi anda mungkin mahu mengetatkannya.
  • saiz: saiz blok memori dalam bait. dalam contoh, kami memberikan satu megabait, yang jelas berlebihan. walau bagaimanapun, ambil perhatian bahawa jika kita menulis ganti blok memori kongsi kita, nilai akan dipotong. jika kita cuba membaca lebih banyak bait daripada blok memori daripada saiznya, ralat maut berlaku. jika kita tidak pasti saiz sebenar data yang akan kita tulis ke ingatan, adalah lebih baik untuk menilai terlalu tinggi berapa besar kita memerlukan blok ingatan kita.

nota penting di sini ialah memanggil shmop_open akan mencipta blok memori baharu jika satu pada kekunci itu belum wujud. ini serupa dengan cara fopen berkelakuan, tetapi dengan shmop_open kelakuan ini bergantung pada hujah 'mod' yang kami lalui.

seperti yang ditunjukkan dalam contoh, shmop_open mengembalikan penunjuk yang boleh digunakan untuk akses: membaca atau menulis, bergantung pada mod yang digunakan untuk membuka blok memori.

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);
Salin selepas log masuk

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 < 4; $i++) {

    // recreate the shm key
    $shm_key = ftok($shmop_file, $i);

    // read from the shared memory
    $shm_id = shmop_open($shm_key, 'a', 0755, 1024);
    $shmop_contents = shmop_read($shm_id, 0, 1024);

    print "reading '$shmop_contents' from child $i".PHP_EOL;

    // delete the shared memory so the shm key can be reused in future runs of this script
    shmop_delete($shm_id);
}
Salin selepas log masuk

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 <line number>
Salin selepas log masuk

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);
Salin selepas log masuk

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"
Salin selepas log masuk

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
Salin selepas log masuk

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

Atas ialah kandungan terperinci php: selaras dengan proses. pt. komunikasi antara proses dengan shmop. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan