{"id":773,"date":"2013-01-31T20:41:42","date_gmt":"2013-01-31T20:41:42","guid":{"rendered":"https:\/\/raspberry-projects.com\/pi\/?p=773"},"modified":"2019-10-25T11:50:45","modified_gmt":"2019-10-25T11:50:45","slug":"shared-memory-between-c-application-and-php-web-server","status":"publish","type":"post","link":"https:\/\/raspberry-projects.com\/pi\/programming-in-c\/memory\/shared-memory-between-c-application-and-php-web-server","title":{"rendered":"Shared Memory Between C Application And PHP Web Server"},"content":{"rendered":"\n<p>A fully working demonstration of a php page running in Apache on the Raspberry Pi being able to safely access shared memory created by a C application also running on the Raspberry Pi. \u00a0A semaphore is used to avoid access conflicts. <\/p>\n\n\n\n<p>The C program creates a byte array. \u00a0The php\u00a0shared memory functions work using strings, but this code does the conversion to and from byte arrays giving access to the raw C byte variables and therefore working round the differences in the\u00a0variable types between the two platforms. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The C Application <\/h4>\n\n\n\n<h5 class=\"wp-block-heading\">Header File <\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/----- SEMAPHORE -----\n\/\/On linux systems this union is probably already defined in the included sys\/sem.h, but if not use this default basic definition:\nunion semun {\n\tint val;\n\tstruct semid_ds *buf;\n\tunsigned short *array;\n};\n\n\/\/----- SHARED MEMORY -----\nstruct shared_memory1_struct {\n\tchar some_data[1024];\n};\n#define\tSEMAPHORE_KEY\t\t\t291623581  \t\t\t\/\/Semaphore unique key (MAKE DIFFERENT TO PHP KEY)\n#define\tSHARED_MEMORY_KEY \t\t672213396   \t\t\/\/Shared memory unique key (SAME AS PHP KEY)\n\nstatic int semaphore1_get_access(void);\nstatic int semaphore1_release_access(void);\n\nstatic int semaphore1_id;\nvoid *shared_memory1_pointer = (void *)0;\nstruct shared_memory1_struct *shared_memory1;\nint shared_memory1_id;\n<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\">C File <\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;sys\/shm.h>\t\t\/\/Used for shared memory\n#include &lt;sys\/sem.h>\t\t\/\/Used for semaphores\n\n\/\/********************************\n\/\/********************************\n\/\/********** INITIALISE **********\n\/\/********************************\n\/\/********************************\nvoid initialise (void)\n{\n\n\t\/\/..... Do init stuff ....\n\n\t\/\/-----------------------------------------------\n\t\/\/----- CREATE SHARED MEMORY WITH SEMAPHORE -----\n\t\/\/-----------------------------------------------\n\tprintf(\"Creating shared memory with semaphore...\\n\");\n\tsemaphore1_id = semget((key_t)SEMAPHORE_KEY, 1, 0666 | IPC_CREAT);\t\t\/\/Semaphore key, number of semaphores required, flags\n\t\/\/\tSemaphore key\n\t\/\/\t\tUnique non zero integer (usually 32 bit).  Needs to avoid clashing with another other processes semaphores (you just have to pick a random value and hope - ftok() can help with this but it still doesn't guarantee to avoid colision)\n\n\t\/\/Initialize the semaphore using the SETVAL command in a semctl call (required before it can be used)\n\tunion semun sem_union_init;\n\tsem_union_init.val = 1;\n\tif (semctl(semaphore1_id, 0, SETVAL, sem_union_init) == -1)\n\t{\n\t\tfprintf(stderr, \"Creating semaphore failed to initialize\\n\");\n\t\texit(EXIT_FAILURE);\n\t}\n\n\t\/\/Create the shared memory\n\tshared_memory1_id = shmget((key_t)SHARED_MEMORY_KEY, sizeof(struct shared_memory1_struct), 0666 | IPC_CREAT);\t\t\/\/Shared memory key , Size in bytes, Permission flags\n\t\/\/\tShared memory key\n\t\/\/\t\tUnique non zero integer (usually 32 bit).  Needs to avoid clashing with another other processes shared memory (you just have to pick a random value and hope - ftok() can help with this but it still doesn't guarantee to avoid colision)\n\t\/\/\tPermission flags\n\t\/\/\t\tOperation permissions \tOctal value\n\t\/\/\t\tRead by user \t\t\t00400\n\t\/\/\t\tWrite by user \t\t\t00200\n\t\/\/\t\tRead by group \t\t\t00040\n\t\/\/\t\tWrite by group \t\t\t00020\n\t\/\/\t\tRead by others \t\t\t00004\n\t\/\/\t\tWrite by others\t\t\t00002\n\t\/\/\t\tExamples:\n\t\/\/\t\t\t0666 Everyone can read and write\n\n\tif (shared_memory1_id == -1)\n\t{\n\t\tfprintf(stderr, \"Shared memory shmget() failed\\n\");\n\t\texit(EXIT_FAILURE);\n\t}\n\n\t\/\/Make the shared memory accessible to the program\n\tshared_memory1_pointer = shmat(shared_memory1_id, (void *)0, 0);\n\tif (shared_memory1_pointer == (void *)-1)\n\t{\n\t\tfprintf(stderr, \"Shared memory shmat() failed\\n\");\n\t\texit(EXIT_FAILURE);\n\t}\n\tprintf(\"Shared memory attached at %X\\n\", (int)shared_memory1_pointer);\n\n\t\/\/Assign the shared_memory segment\n\tshared_memory1 = (struct shared_memory1_struct *)shared_memory1_pointer;\n\n\n\t\/\/----- SEMAPHORE GET ACCESS -----\n\tif (!semaphore1_get_access())\n\t\texit(EXIT_FAILURE);\n\n\t\/\/----- WRITE SHARED MEMORY -----\n\tint Index;\n\tfor (Index = 0; Index &lt; sizeof(struct shared_memory1_struct); Index++)\n\t\tshared_memory1->some_data[Index] = 0x00;\n\n\t\/\/Write initial values\n\tshared_memory1->some_data[0] = 'H';\n\tshared_memory1->some_data[1] = 'e';\n\tshared_memory1->some_data[2] = 'l';\n\tshared_memory1->some_data[3] = 'l';\n\tshared_memory1->some_data[4] = 'o';\n\tshared_memory1->some_data[5] = 0x00;\n\tshared_memory1->some_data[6] = 1;\n\tshared_memory1->some_data[7] = 255;\n\tshared_memory1->some_data[8] = 0;\n\tshared_memory1->some_data[9] = 0;\n\n\tprintf(\"stored=%s\\n\", shared_memory1->some_data);\n\n\t\/\/----- SEMAPHORE RELEASE ACCESS -----\n\tif (!semaphore1_release_access())\n\t\texit(EXIT_FAILURE);\n}\n\n\n\/\/***********************************\n\/\/***********************************\n\/\/********** MAIN FUNCTION **********\n\/\/***********************************\n\/\/***********************************\nint main(int argc, char **argv)\n{\n\n\t\/\/**********************\n\t\/\/**********************\n\t\/\/***** INITIALISE *****\n\t\/\/**********************\n\t\/\/**********************\n\n\t\/\/GENERAL INITIALISE\n\tinitialise();\n\n\n\t\/\/*********************\n\t\/\/*********************\n\t\/\/***** MAIN LOOP *****\n\t\/\/*********************\n\t\/\/*********************\n    while (1)\t\t\t\t\t\t\/\/ Do forever\n    {\n\t\tdelayMicroseconds(100);\t\t\t\t\/\/Sleep occasionally so scheduler doesn't penalise us (THIS REQUIRES -lrt ADDING AS A COMPILER FLAG OR IT WILL CAUSE LOCK UP)\n\n\n\t\t\/\/--------------------------------------------------------------------------------\n\t\t\/\/----- CHECK FOR EXIT COMMAND RECEIVED FROM OTHER PROCESS VIA SHARED MEMORY -----\n\t\t\/\/--------------------------------------------------------------------------------\n\n\t\t\/\/----- SEMAPHORE GET ACCESS -----\n\t\tif (!semaphore1_get_access())\n\t\t\texit(EXIT_FAILURE);\n\t\t\n\t\t\/\/----- ACCESS THE SHARED MEMORY -----\n\t\t\/\/Just an example of reading 2 bytes values passed from the php web page that will cause us to exit\n\t\tif ((shared_memory1->some_data[8] == 30) &amp;&amp; (shared_memory1->some_data[9] = 255))\n\t\t\tbreak;\n\t\t\n\t\t\/\/----- SEMAPHORE RELEASE ACCESS -----\n\t\tif (!semaphore1_release_access())\n\t\t\texit(EXIT_FAILURE);\n\n\n    }\n\n    \/\/------------------------------------------\n    \/\/------------------------------------------\n    \/\/----- EXIT MAIN LOOP - SHUTTING DOWN -----\n    \/\/------------------------------------------\n    \/\/------------------------------------------\n\n\n\t\/\/----- DETACH SHARED MEMORY -----\n\t\/\/Detach and delete\n\tif (shmdt(shared_memory1_pointer) == -1)\n\t{\n\t\tfprintf(stderr, \"shmdt failed\\n\");\n\t\t\/\/exit(EXIT_FAILURE);\n\t}\n\tif (shmctl(shared_memory1_id, IPC_RMID, 0) == -1)\n\t{\n\t\tfprintf(stderr, \"shmctl(IPC_RMID) failed\\n\");\n\t\t\/\/exit(EXIT_FAILURE);\n\t}\n\t\/\/Delete the Semaphore\n\t\/\/It's important not to unintentionally leave semaphores existing after program execution. It also may cause problems next time you run the program.\n\tunion semun sem_union_delete;\n\tif (semctl(semaphore1_id, 0, IPC_RMID, sem_union_delete) == -1)\n\t\tfprintf(stderr, \"Failed to delete semaphore\\n\");\n\n\n#ifndef __DEBUG\n\tif (do_reset)\n\t{\n\t\tsystem(\"sudo shutdown \\\"now\\\" -r\");\n\t}\n#endif\n\n    return 0;\n}\n\n\n\n\/\/***********************************************************\n\/\/***********************************************************\n\/\/********** WAIT IF NECESSARY THEN LOCK SEMAPHORE **********\n\/\/***********************************************************\n\/\/***********************************************************\n\/\/Stall if another process has the semaphore, then assert it to stop another process taking it\nstatic int semaphore1_get_access(void)\n{\n\tstruct sembuf sem_b;\n\tsem_b.sem_num = 0;\n\tsem_b.sem_op = -1; \/* P() *\/\n\tsem_b.sem_flg = SEM_UNDO;\n\tif (semop(semaphore1_id, &amp;sem_b, 1) == -1)\t\t\/\/Wait until free\n\t{\n\t\tfprintf(stderr, \"semaphore1_get_access failed\\n\");\n\t\treturn(0);\n\t}\n\treturn(1);\n}\n\n\/\/***************************************\n\/\/***************************************\n\/\/********** RELEASE SEMAPHORE **********\n\/\/***************************************\n\/\/***************************************\n\/\/Release the semaphore and allow another process to take it\nstatic int semaphore1_release_access(void)\n{\n\tstruct sembuf sem_b;\n\tsem_b.sem_num = 0;\n\tsem_b.sem_op = 1; \/* V() *\/\n\tsem_b.sem_flg = SEM_UNDO;\n\tif (semop(semaphore1_id, &amp;sem_b, 1) == -1)\n\t{\n\t\tfprintf(stderr, \"semaphore1_release_access failed\\n\");\n\t\treturn(0);\n\t}\n\treturn(1);\n}\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">The PHP Page <\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/xhtml1\/DTD\/xhtml1-transitional.dtd\">\n&lt;html xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n&lt;head>\n&lt;meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\" \/>\n\n&lt;title>Test Page&lt;\/title>\n&lt;\/head>\n\n&lt;body>\n\n&lt;?php\n\n\/\/--------------------------------\n\/\/--------------------------------\n\/\/----- ACCESS SHARED MEMORY -----\n\/\/--------------------------------\n\/\/--------------------------------\n\n\/\/----- SHARED MEMORY CONFIGURATION -----\n$SEMAPHORE_KEY = 291623581;   \t\t\t\/\/Semaphore unique key (MAKE DIFFERENT TO RPi App KEY)\n$SHARED_MEMORY_KEY = 672213396;   \t\/\/Shared memory unique key (SAME AS RPi App  KEY)\n\n\/\/Create the semaphore\n$semaphore_id = sem_get($SEMAPHORE_KEY, 1);\t\t\/\/Creates, or gets if already present, a semaphore\nif ($semaphore_id === false)\n{\n    echo \"Failed to create semaphore.  Reason: $php_errormsg&lt;br \/>\";\n    exit;\n}\n\n\/\/Acquire the semaphore\nif (!sem_acquire($semaphore_id))\t\t\t\t\t\t\/\/If not available this will stall until the semaphore is released by the other process\n{\n    echo \"Failed to acquire semaphore $semaphore_id&lt;br \/>\";\n    sem_remove($semaphore_id);\t\t\t\t\t\t\/\/Use even if we didn't create the semaphore as something has gone wrong and its usually debugging so lets no lock up this semaphore key\n    exit;\n}\n\n\/\/We have exclusive access to the shared memory (the other process is unable to aquire the semaphore until we release it)\n\n\/\/Setup access to the shared memory\n$shared_memory_id = shmop_open($SHARED_MEMORY_KEY, \"w\", 0, 0);\t\/\/Shared memory key, flags, permissions, size (permissions &amp; size are 0 to open an existing memory segment)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\/\/flags: \"a\" open an existing shared memory segment for read only, \"w\" read and write to a shared memory segment\nif (empty($shared_memory_id))\n{\n\techo \"Failed to open shared memory.&lt;br \/>\";\t\t\t\/\/&lt;&lt;&lt;&lt; THIS WILL HAPPEN IF THE C APPLICATION HASN'T CREATED THE SHARED MEMORY OR IF IT HAS BEEN SHUTDOWN AND DELETED THE SHARED MEMORY\n}\nelse\n{\n\t\/\/--------------------------------------------\n\t\/\/----- READ AND WRITE THE SHARED MEMORY -----\n\t\/\/--------------------------------------------\n\techo \"Shared memory size: \".shmop_size($shared_memory_id).\" bytes&lt;br \/>\";\n\n\n\t\/\/----- READ FROM THE SHARED MEMORY -----\n\t$shared_memory_string = shmop_read($shared_memory_id, 0, 10);\t\t\t\t\/\/Shared memory ID, Start Index, Number of bytes to read\n\tif($shared_memory_string == FALSE) \n\t{\n\t\t\techo \"Failed to read shared memory\";\n\t\t\tsem_release($semaphore_id);\n\t\t\texit;\n\t}\n\n\t\/\/Display as a string\n\techo \"Shared memory string: $shared_memory_string &lt;br \/>\";\n\n\t\/\/CONVERT TO AN ARRAY OF BYTE VALUES\n\t$shared_memory_array = array_slice(unpack('C*', \"\\0\".$shared_memory_string), 1);\n\n\techo \"Shared memory bytes: \";\n\tfor($i = 0; $i &lt; 10; $i++)\n\t{\n\t\techo $shared_memory_array[$i] . \", \";\n\t}\n\techo \"&lt;br \/>\";\n\n\n\n\t\/\/----- WRITE TO THE SHARED MEMORY -----\n\tif(isset($_REQUEST['shutdown']))\t\t\t\/\/Include \"?shutdown\" at the end of the url to write these bytes which causes the C application to exit\n\t{\n\t\t\/\/The array to write\n\t\t$shared_memory_array = array(30, 255);\n\t\t\n\t\t\/\/Convert the array of byte values to a byte string\n\t\t$shared_memory_string = call_user_func_array(pack, array_merge(array(\"C*\"), $shared_memory_array));\n\t\techo \"Writing bytes: $shared_memory_string&lt;br \/>\";\n\t\tshmop_write($shared_memory_id, $shared_memory_string, 8);\t\t\t\/\/Shared memory id, string to write, Index to start writing from\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\/\/Note that a trailing null 0x00 byte is not written, just the byte values \/ characters, so in this example just 2 bytes are written.\n\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\n\t\/\/Detach from the shared memory\n\tshmop_close($shared_memory_id);\n}\n\n\/\/Release the semaphore\nif (!sem_release($semaphore_id))\t\t\t\t\/\/Must be called after sem_acquire() so that another process can acquire the semaphore\n    echo \"Failed to release $semaphore_id semaphore&lt;br \/>\";\n\n\/\/Delete the shared memory (only do this if we created it and its no longer being used by another process)\n\/\/shmop_delete($shared_memory_id);\n\n\/\/Delete the semaphore (use only if none of your processes require the semaphore anymore)\n\/\/sem_remove($semaphore_id);\t\t\t\t\/\/Destroy the semaphore for all processes\n\n\necho \"Complete&lt;br \/>\";\n\n?>\n\n&lt;p>&lt;a href=\"this_page_name.php?shutdown\">Shutdown C App&lt;\/a>&lt;\/p>\n\n&lt;\/body>\n&lt;\/html><\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">PHP Converting To And From Byte Values <\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\t\/\/----- READ INT16 WORDS EXAMPLE -----\n\t\/\/Convert bytes value read to Int16 words\n\t$shared_memory_array = array_slice(unpack('C*', \"\\0\".$shared_memory_string), 1);\n\t$index = 0;\n\t$word_value_0 = $shared_memory_array[$index++];\n\t$word_value_0 |= (int)($shared_memory_array[$index++]) &lt;&lt; 8;\n\t$word_value_1 = $shared_memory_array[$index++];\n\t$word_value_1 |= (int)($shared_memory_array[$index++]) &lt;&lt; 8;\n\t$word_value_2 = $shared_memory_array[$index++];\n\t$word_value_2 |= (int)($shared_memory_array[$index++]) &lt;&lt; 8;\n\t\n\t\/\/----- WRITE INT16 WORDS EXAMPLE -----\n\t\/\/Convert Int16 words to byte values ready to write\n\t$shared_memory_array = array(\n\t\t\t\t\t\t\t\t\t($word_value_0 &amp; 0x00ff), \n\t\t\t\t\t\t\t\t\t($word_value_0 >> 8),\n\t\t\t\t\t\t\t\t\t($word_value_1 &amp; 0x00ff), \n\t\t\t\t\t\t\t\t\t($word_value_1 >> 8),\n\t\t\t\t\t\t\t\t\t($word_value_2 &amp; 0x00ff), \n\t\t\t\t\t\t\t\t\t($word_value_2 >> 8)\n\t\t\t\t\t\t\t\t\t);\n\t$shared_memory_string = call_user_func_array(pack, array_merge(array(\"C*\"), $shared_memory_array));<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Issues<\/h4>\n\n\n\n<p>If you change the overall memory size then you will get shared memory access failure if the RPi has run an earlier version of your app since boot as linux seems not to tolerate the size changing for a previously used SHARED_MEMORY_KEY. Change the key or reboot to work around.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A fully working demonstration of a php page running in Apache on the Raspberry Pi being able to safely access shared memory created by a C application also running on the Raspberry Pi. \u00a0A semaphore is used to avoid access conflicts. The C program creates a byte array. \u00a0The php\u00a0shared memory functions work using strings, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21],"tags":[],"class_list":["post-773","post","type-post","status-publish","format-standard","hentry","category-memory"],"_links":{"self":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts\/773","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/comments?post=773"}],"version-history":[{"count":21,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts\/773\/revisions"}],"predecessor-version":[{"id":3124,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts\/773\/revisions\/3124"}],"wp:attachment":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/media?parent=773"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/categories?post=773"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/tags?post=773"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}