Buchempfehlung
Windows-Programmierung. Das Entwicklerhandbuch zur WIN32-API
Windows-Programmierung. Das Entwicklerhandbuch zur WIN32-API
"Der" Petzold, das über 1000 Seiten starke Standardwerk zum Win32-API - besonders nützlich u. a. bei der GUI-Programmierung in FreeBASIC! [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

[Linux] Kommunikation zwischen Programmen mit Shared Memory

von MitgliedstephanbrunkerSeite 1 von 3

Shared Memory in Linux

Linux als Betriebssystem stellt über die System V-Anweisungen die Möglichkeit bereit, Informationen zwischen Anwendungen im Arbeitsspeicher auszutauschen. Wozu braucht man so was? In meinem Fall habe ich eine Datenerfassung, die auf einem Raspberry Pi läuft und die über ein Webinterface abgefragt werden kann. Der einfache Weg hätte darin bestanden, die Informationen bei jeder Akutualisierung in eine Datei zu schreiben und diese dann aus der Web-Anwendung auszulesen. Da der RPi aber mit einer SD-Karte läuft, hätten diese ständigen Zugriffe die Speicherkarte in kürzester Zeit degeneriert. Also musste eine Lösung her, die die Informationen ins RAM schreibt, was ja auch naheliegend ist.

Als Ausgangspunkt gibt es dieses Tutorial, welches die Interaktion von einen PHP-Skript mit einer C-Anwendung beschreibt: Externer Link!www.raspberry-projects.com/pi/programming-in-c/memory/shared-memory-between-c-application-and-php-web-server Das Prinzip funktioniert aber ganz allgemein in Linux und zwischen Programmen, solange man auf die Systembefehle zugreifen kann, es muss nicht unbedingt PHP sein, sondern zweimal Freebasic ginge auch.

Im Grunde ist jetzt eigentlich nichts mehr zu tun als die C-header und das Programm nach Freebasic zu übersetzen. Da ich mir die Arbeit schon gemacht habe, gibt es Externer Link!hier die nötigen header ipc.bi, sem.bi und shm.bi. Diese sind zwar nicht vollständig, beinhalten aber die nötigen Funktionen und laufen auf dem RPi.

Das Freebasic-Programm

Das Freebasic-Programm beginnt wie üblich mit den Deklarationen:

'-----------------------------
'        DECLARATIONS
'-----------------------------

#include "sys/shm.bi"
#include "sys/sem.bi"
#include once "crt/errno.bi"

type shared_memory_1024
    some_data as zstring * 1024
end type

#define SEMAPHORE_KEY           291623558
#define SHARED_MEMORY_KEY       672213396
declare function semaphore1_get_access() as integer
declare function semaphore1_release_access() as integer

dim shared semaphore1_id as integer
dim shared shared_memory1_id as integer
dim shared shared_memory1_pointer as shared_memory_1024 ptr

Der Semaphore Key und der Shared Memory Key sind eigentlich frei wählbare Zahlen, es gibt nur die unwahrscheinliche Möglichkeit, das ein anderer laufender Prozess den gleichen Key verwendet, dann gibt es ein Problem. Als Puffer für den Datenaustausch bietet sich bei Freebasic ein selbst konstruierter Type an, der die gewünschten Variablen enthält, hier einen 1024 Zeichen langen String. Wichtig ist nur eine fixe Größe des Types.

Der nächste Schritt ist die Initialisierung des Shared Memory. Das sind eigentlich zwei Schritte, das Shared Memory einerseits und ein Semaphore andererseits. Der Semaphore erfüllt die gleiche Funktion wie ein Mutex beim Multi-Threading, nämlich das Verhindern eines gleichzeitigen Zugriffs auf den Speicher. Die eine Anwendung sperrt also den Speicher, schreibt und gibt ihn wieder frei, während die andere Anwendung, falls sie auf eine Sperre trifft, wartet bis er wieder frei ist oder der Timeout erreicht ist.

Die Initalisierung ruft zuerst semget() auf, mit dem man vom System eine SemaphoreID erhält. Diese ID wird mit semctl() initialisiert. Als nächstes wird das Shared Memory reserviert, ebenfalls mit zwei Funktionen: shmget() arbeitet wie ALLOCATE, und gibt nach Angabe von Zugriffsart, Rechten und Größe eine ID zurück, und shmat() gibt dann nach Angabe der ID einen Pointer zum Speicher zurück. Diesen Zugriff können wir in Freebasic elegant mit NEW zu einem UDT zuweisen. Wenn wir dann den UDT ändern, ändert sich direkt der Wert im Speicher.

'-----------------------------
'        INITALIZE
'-----------------------------
sub initalise()
    '..... Do init stuff ....

    '-----------------------------------------------
    '----- CREATE SHARED MEMORY WITH SEMAPHORE -----
    '-----------------------------------------------
    print "Creating shared memory with semaphore..."
    semaphore1_id = semget(SEMAPHORE_KEY, 3, &o0666 or IPC_CREAT)
                        'Semaphore key, number of semaphores required, flags
    ' Semaphore key
    ' Unique 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)

    if semaphore1_id = -1 then
        print "Failed to get Semaphore ID, errno: ";errno
    else
        print "Semaphore ID: ";semaphore1_id
    end if

    'Initialize the semaphore using the SETVAL command in a semctl call (required before it can be used)
    dim sem_union_init as integer = 1
    if semctl(semaphore1_id, 0, SETVAL, sem_union_init) = -1 then
        print "Creating semaphore failed to initialize"
        print "errno: ";errno
        sleep:end
    end if

    'Every semaphore_id can have multiple semaphores attached to it (the semnum argument).
    'If the val_ variable is 1, the semaphore is unlocked, is it 0, the semaphore is locked.
    'after semctl, the val_ should be 1, so test it with GETVAL:

    dim sem0val as integer
    sem0val = semctl(semaphore1_id, 0, GETVAL)
    if sem0val = -1 then
        print "Semaphore get value failed, error: ";errno
    else
        print "Semaphore1(0) Val: ";sem0val
    end if

    'Create the shared memory
    shared_memory1_id = shmget(SHARED_MEMORY_KEY, sizeof(shared_memory_1024), &o0666 or IPC_CREAT)
                                'Shared memory key , Size in bytes, Permission flags
    '   Shared memory key
    '       Unique 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)
    '   Permission flags
    '       Operation permissions   Octal value
    '       Read by user            00400
    '       Write by user           00200
    '       Read by group           00040
    '       Write by group          00020
    '       Read by others          00004
    '       Write by others         00002
    '       Examples:
    '       0666 Everyone can read and write

    if shared_memory1_id = -1 then
        print "Shared memory shmget() failed"
        sleep:end
    end if

    'Make the shared memory accessible to the program
    dim shmptr as shared_memory_1024 ptr
    shmptr = shmat(shared_memory1_id, 0, 0)
    if shmptr = -1 then
        print "Shared memory shmat() failed"
        sleep: end
    end if
    print "Shared memory attached at ";shmptr

    'Assign the shared_memory segment
    shared_memory1_pointer = new (shmptr) shared_memory_1024

end sub

Wie im Kommentar beschrieben, kann jede SemaphoreID mehrere Semaphoren zugeordnet haben. Auch wenn wir nur eine brauchen, fragen wir bei semget drei Stück an, denn bei der PHP-Seite sind es immer drei, mit nur einer funktioniert es nicht und weil das damals nirgendwo stand, war das einen ziemlich harte Nuss zu knacken.

Für den Zugriff gibt es dann die zwei Funktionen: semaphore1_get_access() und semaphore1_release_access().

'-----------------------------------------
'  WAIT IF NECESSARY THEN LOCK SEMAPHORE
'-----------------------------------------
'Stall if another process has the semaphore, then assert it to stop another process taking it
function semaphore1_get_access() as integer
    dim sem_b as sembuf
    sem_b.sem_num = 0
    sem_b.sem_op = -1 'decreases the val of the semaphore to 0 - lock it
    sem_b.sem_flg = SEM_UNDO
    if semop(semaphore1_id, @sem_b, 1) = -1 then        'function sleeps until the semaphore can be locked
        print "semaphore1_get_access failed"
        return 0
    end if
    return 1
end function

'-----------------------------------------
'            RELEASE SEMAPHORE
'-----------------------------------------
'Release the semaphore and allow another process to take it
function semaphore1_release_access() as integer
    dim sem_b as sembuf
    sem_b.sem_num = 0
    sem_b.sem_op = 1 '* V() */
    sem_b.sem_flg = SEM_UNDO
    if semop(semaphore1_id, @sem_b, 1) = -1 then
        print "semaphore1_release_access failed"
        return 0
    end if
    return 1
end function

 

Gehe zu Seite Gehe zu Seite  1  2  3  
Zusätzliche Informationen und Funktionen
  Bearbeiten Bearbeiten  

  Versionen Versionen