mikrocontroller.net

Forum: Compiler & IDEs Wert einer Speicheradresse auslesen


Autor: Lu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

Ich habe hier schon mehrere Beiträge über das Thema gelesen, jedoch trug 
keiner zur Lösung meines Problems bei.
Und war möchte ich den,  von einem anderen Programm(programm 1) an der 
Adresse (z.b 0x7ef1df30) ,  gespeicherten Wert auslesen lassen und 
weiter nutzen.
Die Adresse wird nach Start des Programms 1 in eine Datei geschrieben 
und dann vom Programm 2(mehrere Programme)  eingelesen, sodass Programm 
2 weiß wo der Wert steht. Hatte erst an die Einbindung der Programme 
ineinander gedacht. Da es aber wichtig ist, das alle Programme zur 
gleichen Zeit den gleichen Wert auslesen, fällt das schon mal flach.
Um zu sehen, ob der Wert ausgelesen wurde habe ich ihn einfach mal 
testweise ausgeben wollen.
#include <stdio.h> 
int main () {
Int *adresse=(int*) (0x7ef1df30) ;
int wert=*adresse; //Variante 1
printf(" %i\n", *adresse) ;
printf(" %i\n", wert) ; //variante 1
return 0;
} 
Ausgegeben bekomme ich jedoch nur  0
Wo liegt der Fehler?
Gruß
Lucien

Autor: Karl M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

was ist den das?
Int *

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf welcher Plattform/OS soll das laufen?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lu schrieb:
> Und war möchte ich den,  von einem anderen Programm(programm 1) an der
> Adresse (z.b 0x7ef1df30) ,  gespeicherten Wert auslesen lassen und
> weiter nutzen.
> Die Adresse wird nach Start des Programms 1 in eine Datei geschrieben
> und dann vom Programm 2(mehrere Programme)  eingelesen,

Was jetzt, in eine Speicherstelle im RAM oder in einer Datei? Unter 
normalen Betriebssystemen wie Linux, Windows kannst du nicht einfach an 
eine beliebige Speicherstelle schreiben, und schon gar nicht so dass 
andere Programme das sehen können.

Autor: Lu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Int sollte natürlich klein geschrieben sein... Sorry das mach die 
Autokorrektur ganz schnell
Also laufen soll das ganze unter Raspbian.
Wie schon beschrieben wird die von Programm 1 zum Programmstart 
festgelegte speicheradresse ausgelesen und in eine Datei geschrieben 
diese können andere Programme dann einlesen und so erfahren an welcher 
Adresse der Wert gespeichert wird.
Da sich der Wert alle 0,1 sec ändert ist das Schreiben in eine Datei die 
immer wieder überschrieben wird keine Option(zieht zu viel 
Prozessorleistung). Also dachte ich, wenn allen Programmen der 
speicherort bekannt ist könnten sie die Werte von dort auslesen.

Autor: Theor (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Hm. Das hört sich nach einem Wert von einem angeschlossenen Sensor an.
Da würde ich raten, sich mit Treiberprogrammierung zu beschäftigen. Eine 
andere Möglichkeit wäre Interprozess-Kommunikation.

Ich würde aber gleichzeitig sehr davon abraten, eine Lösung an einer 
MMU oder dem OS vorbei zu hacken. Im allgemeinen wird so eine Lösung 
vermutlich ohnehin zu einem erheblichen Teil die gleichen Kenntnisse 
erfordern wie sie die Treiberprogrammierung bzw. die 
Interprozess-Kommunikation erfordert; und sei es nur um diese nicht zu 
stören.

Als letztes rate ich, falls Du mit dieser Antwort nichts anfangen kannst 
oder willst, mal das ganze Problem zu schildern. Das gebietet ohnehin 
die Höflichkeit (und die Netiquette).

Autor: Ergo70 (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Also dachte ich, wenn allen Programmen der speicherort bekannt ist 
könnten sie die Werte von dort auslesen.

Nein. Dafür gibt es shared memory.

Autor: zyxw (Gast)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
Neben dem * gibt es auch noch &, welches die Adresse ausgibt.
Ich habe die Erfahrung gemacht, dass bei fprint manchmal diese 
"Umleitungen" nicht funktionieren, daher besser die Adresse in ein 
normales int übertragen und dieses ausgeben.
versuche es mal mit
int adr = &wert;  //speichert die Adresse von wert in adr.
oder
int*adr = &wert;
fprint(adr);

Autor: G ast (Gast)
Datum:

Bewertung
-7 lesenswert
nicht lesenswert
Karl M. schrieb:
> Hallo,
>
> was ist den das?Int *

Das ist ein Pointer auf int. Kein C gelernt?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Lu schrieb:
> Also dachte ich, wenn allen Programmen der
> speicherort bekannt ist könnten sie die Werte von dort auslesen.

Wenn das ginge, könnte ja ein bösartiges Programm ("Virus") einfach so 
in den Speicher aller anderen Programme reinschreiben und Nutzerdaten 
abgreifen. Alle modernen Betriebssysteme verhindern das, jedes Programm 
hat seinen eigenen Adressraum (virtueller Speicher) von 0 bis 2^32-1 
(bzw. 2^64-1 bei 64bit-Systemen); wenn ein Programm auf Adresse X etwas 
schreibt, und ein anderes Programm Adresse X ausliest, sind es 
tatsächlich zwei komplett unterschiedliche Speicherstellen.
Um Daten zwischen Prozessen auszutauschen gibt es sog. IPC (Inter 
Process Communication). Unter Linux geht das z.B. über Pipes, FIFO's, 
Unix Sockets, TCP Sockets. Shared Memory ist für so etwas einfaches 
vielleicht etwas übertrieben.

Autor: Lu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ergo70 schrieb:
> Also dachte ich, wenn allen Programmen der speicherort bekannt ist
> könnten sie die Werte von dort auslesen.
>
> Nein. Dafür gibt es shared memory.

Das klingt vielversprechend! Damit kann ich was anfangen.
Danke!

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lu schrieb:
> Das klingt vielversprechend! Damit kann ich was anfangen.
> Danke!

Da musst du aber auf Deadlocks und inkonsistente Daten aufpassen und mit 
Thread-Synchronisation arbeiten (Semaphores usw). Da das ziemlich 
kompliziert sein kann, wäre für diese anscheinend simple Aufgabe z.B. 
ein Unix Socket deutlich einfacher.

Autor: Lu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Lu schrieb:
> Also dachte ich, wenn allen Programmen der
> speicherort bekannt ist könnten sie die Werte von dort auslesen.
>
> Wenn das ginge, könnte ja ein bösartiges Programm ("Virus") einfach so
> in den Speicher aller anderen Programme reinschreiben und Nutzerdaten
> abgreifen. Alle modernen Betriebssysteme verhindern das, jedes Programm
> hat seinen eigenen Adressraum (virtueller Speicher) von 0 bis 2^32-1
> (bzw. 2^64-1 bei 64bit-Systemen); wenn ein Programm auf Adresse X etwas
> schreibt, und ein anderes Programm Adresse X ausliest, sind es
> tatsächlich zwei komplett unterschiedliche Speicherstellen.
> Um Daten zwischen Prozessen auszutauschen gibt es sog. IPC (Inter
> Process Communication). Unter Linux geht das z.B. über Pipes, FIFO's,
> Unix Sockets, TCP Sockets. Shared Memory ist für so etwas einfaches
> vielleicht etwas übertrieben.

Mag wohl sein das man dann mit Kanonen auf Spatzen schießt, aber ich 
werde es mal probieren, das erweitert ja schließlich den Horizont

Autor: Thomas G. (blasebalg)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
http://pronix.linuxdelta.de/C/Linuxprogrammierung/

Ab Kapitel 5 wäre für dich interessant.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lu schrieb:
> Mag wohl sein das man dann mit Kanonen auf Spatzen schießt

Shared Memory ist eine Kanone, Pipes, Fifos, Unix-Sockets sind 
Jagdgewehre :-)
Alternativ überlege dir, ob es wirklich zwei Programme sein müssen und 
sich die Funktionalität nicht in eines integrieren lässt.

Lu schrieb:
> Hatte erst an die Einbindung der Programme
> ineinander gedacht. Da es aber wichtig ist, das alle Programme zur
> gleichen Zeit den gleichen Wert auslesen, fällt das schon mal flach.

Was heißt "alle Programme" - muss das Programm explizit mehrfach 
gleichzeitig gestartet werden können?

Autor: Bauform B. (bauformb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lu schrieb:
> Da es aber wichtig ist, das alle Programme zur
> gleichen Zeit den gleichen Wert auslesen,

Das ist die eigentliche Herausforderung. Auch, wenn ein Programm 
tatsächlich fremden Speicher lesen könnte. Ich fürchte, man kann das 
nicht garantieren. Mit einer POSIX message queue pro Programm2 könnte 
Programm1 zumindest abfragen, ob alle Programm2 den aktuellen Wert 
gelesen haben. Aber irgendwie sieht auch nicht richtig aus.

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
3 lesenswert
nicht lesenswert
G ast schrieb:
> Das ist ein Pointer auf int. Kein C gelernt?

Nein, das ist ein Pointer auf "Int". Aber was ist "Int"?

Autor: Helmut S. (helmuts)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Gibt es denn keine Peripherieregister/display-memory die man beschreiben 
und lesen kann um z. B. 4Bytes zu "verstecken"?

: Bearbeitet durch User
Autor: Bauform B. (bauformb)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Obwohl es unter Linux durchaus geht: lieber nicht, sobald der Sensorwert 
zufällig 0xdeafbeef wird, schaltest du die Kühlwasserpumpen für den 
Reaktor ab ;)

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Helmut S. schrieb:
> Gibt es denn keine Peripherieregister/display-memory die man
> beschreiben und lesen kann um z. B. 4Bytes zu "verstecken"?

Und wie würdest du an die die dran kommen als normales 
Anwendungsprogramm?

Autor: Helmut S. (helmuts)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Helmut S. schrieb:
>> Gibt es denn keine Peripherieregister/display-memory die man
>> beschreiben und lesen kann um z. B. 4Bytes zu "verstecken"?
>
> Und wie würdest du an die die dran kommen als normales
> Anwendungsprogramm?

Bei einem embededded ARM-processor schreibt man doch auch oft auf die 
Peripherie-Register. Dafür gibt es doch bestimmt auch beim Raspberry PI 
eine Möglichkeit(Treiber?).

: Bearbeitet durch User
Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Helmut S. schrieb:
> Dafür gibt es doch bestimmt auch beim Raspberry PI eine
> Möglichkeit(Treiber?).

Ja, im Kernel. aber das sieht nicht nach Kernel Code aus. Wäre ziemlich 
absurd, extra einen Treiber zu schreiben, der Daten in einem Peripherie 
Register versteckt, nur damit Programme darüber kommunizieren können. 
Wenn man es schon schafft ein Programm mit einem Kernel Treiber 
kommunizieren zu lassen, schafft man es 10x zwei Programme ganz normal 
über Pipes & Co kommunizieren zu lassen. Das ist nämlich genau dafür da.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Helmut S. schrieb:
> Dr. Sommer schrieb:
>> Helmut S. schrieb:
>>> Gibt es denn keine Peripherieregister/display-memory die man
>>> beschreiben und lesen kann um z. B. 4Bytes zu "verstecken"?
>>
>> Und wie würdest du an die die dran kommen als normales
>> Anwendungsprogramm?
>
> Bei einem embededded ARM-processor schreibt man doch auch oft auf die
> Peripherie-Register. Dafür gibt es doch bestimmt auch beim Raspberry PI
> eine Möglichkeit(Treiber?).

Genau. Sowas machen Treiber. Das wäre dann eine kompliziertere und 
unflexiblere Variante von dem, was man auch mit Shared Memory machen 
könnte.

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bauform B. schrieb:
> Lu schrieb:
>> Da es aber wichtig ist, das alle Programme zur
>> gleichen Zeit den gleichen Wert auslesen,
>
> Das ist die eigentliche Herausforderung.

Absolut richtig, denn dazu müssen die Lesevorgänge synchronisiert werden 
-- insbesondere dann, wenn sich der betreffende Wert alle 0,1 Sekunden 
ändern kann.

> Mit einer POSIX message queue pro Programm2 könnte
> Programm1 zumindest abfragen, ob alle Programm2 den aktuellen Wert
> gelesen haben. Aber irgendwie sieht auch nicht richtig aus.

Stimmt. Da aber auch andere vor solchen oder ähnlichen Anforderungen 
stehen, gibt es für sowas einen eigenen Namen, nämlich Publish-Subscribe 
oder kurz Pubsub, und natürlich auch entsprechende fertige Software wie 
LMAX Disruptor, ZeroMQ, oder auch Apache Kafka. Ich selbst würde daher 
vermutlich Redis' oder ZeroMQs Pubsub-Funktionen benutzen -- 
insbesondere Redis könnte hier je nach Anwendungsfall noch einige 
interessante Möglichkeiten bieten, sonst ZeroMQ.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> natürlich auch entsprechende fertige Software wie
> LMAX Disruptor, ZeroMQ, oder auch Apache Kafka

Photonentorpedos auf Amöben? Man tippe einfach mal "man 7 unix" ein. Da 
gibt's sogar Beispielcode:

http://man7.org/linux/man-pages/man7/unix.7.html

Wenn man das asynchron macht, kann man auch beliebig viele "Clients" 
gleichzeitig versorgen. Braucht keine zusätzliche Software außer dem 
Linux Kernel, ist trivial einzurichten, braucht keine Synchronisierung 
per Semaphore o.ä., pfuscht nicht irgendwie im Speicher rum, über die 
Dateinamen der Socket-Datei ist das ganze sogar recht transparent für 
den Nutzer.

Das heißt natürlich nicht dass Kafka & Co nicht auch sinnvoll sein 
können, aber die Anforderungen sehen bis jetzt nicht so aus...

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Sheeva P. schrieb:
>> natürlich auch entsprechende fertige Software wie
>> LMAX Disruptor, ZeroMQ, oder auch Apache Kafka
>
> Photonentorpedos auf Amöben? Man tippe einfach mal "man 7 unix" ein. Da
> gibt's sogar Beispielcode:

Das sind Photonentorpedos mit Ruhemasse Null! Mit sowas wie ZeroMQ 
bekommt er gleich Layer 4 und 5 für die er sich ansonsten die Finger mit 
fehlerträchtigem langweiligem Boilerplate blutig programmieren müsste 
wenn er mit nackten sockets rummachen wollte.

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Sheeva P. schrieb:
>> natürlich auch entsprechende fertige Software wie
>> LMAX Disruptor, ZeroMQ, oder auch Apache Kafka
>
> Photonentorpedos auf Amöben? Man tippe einfach mal "man 7 unix" ein. Da
> gibt's sogar Beispielcode:
>
> http://man7.org/linux/man-pages/man7/unix.7.html
>
> Wenn man das asynchron macht, kann man auch beliebig viele "Clients"
> gleichzeitig versorgen. Braucht keine zusätzliche Software außer dem
> Linux Kernel, ist trivial einzurichten, braucht keine Synchronisierung
> per Semaphore o.ä., pfuscht nicht irgendwie im Speicher rum, über die
> Dateinamen der Socket-Datei ist das ganze sogar recht transparent für
> den Nutzer.

UNIX Domain Sockets dienen der Kommunikation zwischen zwei Prozessen, 
der TO sucht jedoch etwas zur Kommunikation zwischen einem Senderprozeß 
mit mehreren Empfängerprozessen. Für haargenau diesen Anwendungsfall 
gibt es den besagten Publish-Subscribe-Mechanismus, wie ihn die von mir 
genannten Bibliotheken und Programme implementieren.

Wenn wir schon bei den traditionellen UNIX-Technologien bleiben wollen, 
erscheint mir POSIX Shared Memory (man 7 shm_overview) viel sinnvoller. 
Damit können die beteiligten Prozesse den gewünschten Speicher direkt 
mit mmap(2) in ihren Speicherraum mappen und unmittelbar wie auf eine 
normale Variable davon lesen bzw. darauf schreiben.

Klar, UNIX Domain Sockets würden auch gehen. Aber dann muß der arme TO 
sich manuell um seine Dateideskriptoren für die einzelnen 
Client-Verbindungen kümmern, und das ist aufwändig und fehleranfällig. 
Du hast natürlich Recht, daß das dann nicht mehr als einen UNIX-Kernel 
braucht, aber was soll der Geiz? ZeroMQ und Redis sind in den 
Paketrepositories von Debian, Raspbian und Ubuntu bereits enthalten und 
nur einen Aufruf von apt(8) entfernt.

Dafür bieten ZeroMQ und Redis gleich auch noch weitere Funktionen wie 
zum Beispiel Netzwerktransparenz und im Falle von Redis sogar noch viel 
mehr. Sowohl ZeroMQ als auch Redis sind sehr leicht zu benutzen und 
kümmern sich  vollautomatisch um all das, was Du im Falle von UNIX 
Domain Sockets alles manuell machen müsstest.

Insofern ist die Frage, warum der TO eine prinzipbedingt nicht 
sonderlich gut geeignete Technologie wie UNIX Domain Sockets aufbohren 
sollte, wenn gleichzeitig genau für diesen Anwendungsfall vorgesehene 
Technologien wie ZeroMQ und Redis mit wenigen Handgriffen verfügbar 
sind. ;-)

Autor: Jobst Q. (joquis)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Ergo70 schrieb:
> Also dachte ich, wenn allen Programmen der speicherort bekannt ist
> könnten sie die Werte von dort auslesen.
>
> Nein. Dafür gibt es shared memory.

Man kann auch ganz normale Dateien vereinbaren, das ist flexibler. Mit 
Ramdisk, wenn es schnell gehen soll . Damit hat man so etwas wie 
Datenbausteine bei einer SPS.

Ich habe ein ganzes Steuerungsystem aus vielen kleinen Programmen, die 
auf diese Weise miteinander kommunizieren und mit recht 
unterschiedlicher Hardware arbeiten können.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> und das ist aufwändig und fehleranfällig.

In dem Beispiel war das nicht so kompliziert... Rate mal was redis nutzt 
um sich mit lokalen Servern zu verbinden - richtig, Unix Sockets :-) die 
kann man übrigens auch sehr leicht Netzwerk Transparent machen indem man 
sie mit AF_INET erstellt...

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jobst Q. schrieb:
> Ergo70 schrieb:
>> Also dachte ich, wenn allen Programmen der speicherort bekannt ist
>> könnten sie die Werte von dort auslesen.
>>
>> Nein. Dafür gibt es shared memory.
>
> Man kann auch ganz normale Dateien vereinbaren, das ist flexibler. Mit
> Ramdisk, wenn es schnell gehen soll.

Äh... bitte entschuldige, aber alle halbwegs modernen Betriebssysteme 
halten den Inhalt von Dateien ohnehin in einem Dateisystempuffer, also 
in-memory. Sogar dann, wenn man ein fflush(3) macht -- um die 
Synchronisation mit der Persistenzschicht zu erreichen, braucht es ein 
fsync(2). Da nutzt Dir die Ramdisk leider gar nichts. ;-)

> Ich habe ein ganzes Steuerungsystem aus vielen kleinen Programmen, die
> auf diese Weise miteinander kommunizieren und mit recht
> unterschiedlicher Hardware arbeiten können.

Naja, Shared Memory, UNIX Domain Sockets, POSIX Message Queues und alle 
anderen traditionellen Arten der Interprozeßkommunikation funktionieren 
ebenso auf unterschiedlichsten Hardwareplattformen wie Redis und ZeroMQ.

Darüber hinaus kann man sowas natürlich trotzdem machen, wenn man sich 
große Latenzen, wenig Durchsatz und verlorene Writes leisten kann.

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> In dem Beispiel war das nicht so kompliziert...

Logisch, die Beispiele beziehen sich aber auch nur auf die Verwendung 
genau einer Verbindung zwischen genau einem Client und einem Server. 
Aber wenn Du UNIX Domain Sockets ins Feld führst reden wir von n 
Verbindungen zwischen n Clients und besagtem Server.

Also muß dieser Server die Verbindungen seiner Clients irgendwie 
verwalten, und da der Server niemals davon ausgehen kann, daß alle 
Clients konnektiert  und verfügbar sind und zudem allesamt korrekt 
funktionieren, müßte er seine Client-Verbindungen einzeln und also 
dynamisch verwalten. Mit Disconnects, Abstürzen, Race Conditions und 
Vollauslastungen seiner Clients... ;-)

Traditionelle Implementierungen haben für jede Verbindung mit fork(2) 
einen neuen Prozeß erzeugt (was dank Copy-On-Write zunächst relativ 
unaufwändig war), moderne starten einen Thread (was noch unaufwändiger 
ist, weil kein neuer Speicherraum allokiert werden muß), und unter Linux 
benutzen sie eh heutzutage alle intern den clone(2)-Syscall. Das würde 
aber bedeuten, daß Du am Ende etliche Prozesse oder Threads 
synchronisieren mußt.

> Rate mal was redis nutzt
> um sich mit lokalen Servern zu verbinden - richtig, Unix Sockets :-) die
> kann man übrigens auch sehr leicht Netzwerk Transparent machen indem man
> sie mit AF_INET erstellt...

Meine Güte, ja. Aber das ist doch genau mein Punkt: Redis macht genau 
das, und zwar fertig, vielfach getestet und für produktionsstabil 
befunden, was Du lieber von Hand implementiert sehen möchtest. Zudem ist 
der Performance-Impact bei lokalen Verbindungen zwischen Redis-Client 
und Redis-Server nahe null, und zwar gerade weil die lokalen 
Verbindungen über einen UNIX Domain Socket gehen.

Ebenso funktioniert auch ZeroMQ natürlich mit UNIX Domain Sockets -- und 
ist dabei je nach Socket-Library angeblich sogar schneller als 
klassische AF_UNIX Sockets. Auch ZeroMQ kümmert sich professionell und 
selbständig, vielfach getestet und produktionsstabil um alles, was Du 
aufwändig alles manuell implementieren willst.

Nebenbei bringen alle genannten Pubsub-Implementierungen schon 
Fähigkeiten mit, die jede Eigenimplementierung erst einmal 
implementieren müßte -- und zwar ohne, daß ich mir Gedanken über 
Byteorders, Programmiersprachen, oder Protokolle machen müßte. Zeig' mir 
sowas mal mit UNIX Domain Sockets, dann können wir gerne weiterreden. 
;-)

Uns sei bitte nicht böse, wenn ich noch einmal frage: warum sollte ich 
mir Arbeit machen, wenn es haargenau diesen Anwendungsfall bereits 
performant, stabil, fertig und getestet gibt?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> Uns sei bitte nicht böse, wenn ich noch einmal frage: warum sollte ich
> mir Arbeit machen, wenn es haargenau diesen Anwendungsfall bereits
> performant, stabil, fertig und getestet gibt?

Weil es bei den (bis jetzt bekannten) Anforderungen massiver Overkill 
ist, und eine Eigenimplementation nicht so schwierig ist. Auch die 
Unterstützung mehrerer Clients gleichzeitig (wo war die nochmal 
gefordert?) ist nicht so schwierig, man kann das auch asynchron mit 
select() machen, dann spart man sich die Thread Synchronisation. Es gibt 
auch so was wie sportlichen Ehrgeiz, nicht jeder braucht eine super 
performante Fertig-Library um 40 Bytes pro Sekunde zu übertragen. Aber 
ja, heutige Programmierer können ja nur Libraries zusammen kopieren :-)

Das viel beschriene PubSub-Modell liefert der Kernel gratis mit: publish 
ist listen (), subscribe ist connect().

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> Wenn wir schon bei den traditionellen UNIX-Technologien bleiben wollen,
> erscheint mir POSIX Shared Memory (man 7 shm_overview) viel sinnvoller.
> Damit können die beteiligten Prozesse den gewünschten Speicher direkt
> mit mmap(2) in ihren Speicherraum mappen und unmittelbar wie auf eine
> normale Variable davon lesen bzw. darauf schreiben.

Der Nachteil ist, dass er sich dann mit Synchronisation beschäftigen 
muss. Sockets oder Pipes haben den Vorteil, dass das entfällt.

> Klar, UNIX Domain Sockets würden auch gehen. Aber dann muß der arme TO
> sich manuell um seine Dateideskriptoren für die einzelnen
> Client-Verbindungen kümmern, und das ist aufwändig und fehleranfällig.

Naja, ein Array mit Dateideskriptoren und selct oder poll sind nun keine 
Raketenwissenschaft und zum Lernen, wie so ein System funktioniert (was 
nach eigenem Bekunden ja auch mit sein Ziel ist) durchaus geeignet.

> Dafür bieten ZeroMQ und Redis gleich auch noch weitere Funktionen wie
> zum Beispiel Netzwerktransparenz und im Falle von Redis sogar noch viel
> mehr.

Sockets bieten auch Netzwerktransparenz, wenn man TCP- oder UDP-Sockets 
statt UNIX-Domain-Sockets nimmt.

Autor: Bernd K. (prof7bit)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Weil es bei den (bis jetzt bekannten) Anforderungen massiver Overkill
> ist, und eine Eigenimplementation nicht so schwierig ist.

Dem "Eigenimplementierung nicht so schwierig" steht gegenüber daß bei 
ZeroMQ die Eigenimplementierung gar nicht erst erforderlich ist da man 
den code den man sonst selbst schreiben müsste einfach aus ner gut 
getesteten lib hinzulinkt.

> Overkill

Nein. Der zu schreibende Code wird kleiner, der Overhead des extrem 
leichtgewichtigen ZeroMQ (welches wahrscheinlich aufgrund seiner 
Allgegenwart¹ eh schon installiert ist) ist also kleiner als null.


--
¹) siehe Bild

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Sheeva P. schrieb:
>> Uns sei bitte nicht böse, wenn ich noch einmal frage: warum sollte ich
>> mir Arbeit machen, wenn es haargenau diesen Anwendungsfall bereits
>> performant, stabil, fertig und getestet gibt?
>
> Weil es bei den (bis jetzt bekannten) Anforderungen massiver Overkill
> ist, und eine Eigenimplementation nicht so schwierig ist.

Ok, Junge, zeig' mir mal Deine Eigenimplementierung, und dann halten wir 
meine daneben. Na los, trau' Dich.

> Auch die
> Unterstützung mehrerer Clients gleichzeitig (wo war die nochmal
> gefordert?)

In diesem Thread. Lesen bildet, probier's aus.

> Es gibt
> auch so was wie sportlichen Ehrgeiz, nicht jeder braucht eine super
> performante Fertig-Library um 40 Bytes pro Sekunde zu übertragen.

NIH existiert.

> Aber ja, heutige Programmierer können ja nur Libraries zusammen kopieren

Muß ich mir das nach dreißig Jahren System- und Kernelentwicklung von 
einem sagen lassen, der lieber irgendwelchen händischen Unsinn prokelt 
statt eine getestete und stabile Software zu benutzen? I think not.

> Das viel beschriene PubSub-Modell liefert der Kernel gratis mit: publish
> ist listen (), subscribe ist connect().

Erzähl doch keinen Unsinn. Komm, Junge, laß' Code sprechen. ;-)

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wow, jetzt fühl ich mich richtig motiviert was für dich zu 
programmieren.

Autor: S. R. (svenska)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Was hindert den TO eigentlich daran, die Daten in die Datei zu 
schreiben, statt nur deren Adresse?

Und was hindert den TO eigentlich daran, eine passend dimensionierte 
Datei (in /tmp) mit mmap() und MAP_SHARED in zwei Prozessen zu mappen 
und genau das zu erreichen, was er ursprünglich wollte?

Autor: Helmut S. (helmuts)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
S. R. schrieb:
> Was hindert den TO eigentlich daran, die Daten in die Datei zu
> schreiben, statt nur deren Adresse?

Was hindert den TO eigentlich daran, die Daten in die Datei zu
schreiben, statt nur deren Adresse?

In der Aufgabe würde 10 mal je Sekunde eine Datei geschrieben. Das ginge 
vielleicht mit RAM-Disk. Allerdings ob da z. B. 10 Programme 10mal pro 
Sekunde auf diese Datei zugreifen können ist fraglich.
Die normale FLASH-Speicherkarte würde wahrscheinlich einen Dauerbetrieb 
über Monate hinweg nicht überleben.

Autor: S. R. (svenska)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Helmut S. schrieb:
> Das ginge vielleicht mit RAM-Disk.

Dafür nimmt man sowas, richtig.

Helmut S. schrieb:
> Die normale FLASH-Speicherkarte würde wahrscheinlich einen Dauerbetrieb
> über Monate hinweg nicht überleben.

Dafür gibt es einen Plattencache.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
S. R. schrieb:
> Helmut S. schrieb:
>> Das ginge vielleicht mit RAM-Disk.
>
> Dafür nimmt man sowas, richtig.

Finde ich nur irgendwie recht umständlich, einerseits für das System, 
das dann immer den Umweg über ein Dateisystem gehen muss, andererseits 
für den Benutzer, der erstmal eine Ramdisk einrichten muss.

> Helmut S. schrieb:
>> Die normale FLASH-Speicherkarte würde wahrscheinlich einen Dauerbetrieb
>> über Monate hinweg nicht überleben.
>
> Dafür gibt es einen Plattencache.

Der wird aber regelmäßig (unter Linux soweit ich weiß einmal pro 
Sekunde) auf das Medium raussynchronisiert.

: Bearbeitet durch User
Autor: Jobst Q. (joquis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sheeva P. schrieb:
>> Man kann auch ganz normale Dateien vereinbaren, das ist flexibler. Mit
>> Ramdisk, wenn es schnell gehen soll.
>
> Äh... bitte entschuldige, aber alle halbwegs modernen Betriebssysteme
> halten den Inhalt von Dateien ohnehin in einem Dateisystempuffer, also
> in-memory. Sogar dann, wenn man ein fflush(3) macht -- um die
> Synchronisation mit der Persistenzschicht zu erreichen, braucht es ein
> fsync(2). Da nutzt Dir die Ramdisk leider gar nichts. ;-)

Eine Ramdisk ist trotzdem schneller, ua weil sie das ganze caching nicht 
braucht. Die fflush und fsync Funktionen kehren da sofort zurück, weil 
sie nichts zu tun haben.

Eine Datei hat gegenüber dem shared memory den Vorteil, dass sie viel 
einfacher zu handhaben ist und ohne Aufwand kopiert oder eingelesen 
werden kann.

>> Ich habe ein ganzes Steuerungsystem aus vielen kleinen Programmen, die
>> auf diese Weise miteinander kommunizieren und mit recht
>> unterschiedlicher Hardware arbeiten können.
>
> Naja, Shared Memory, UNIX Domain Sockets, POSIX Message Queues und alle
> anderen traditionellen Arten der Interprozeßkommunikation funktionieren
> ebenso auf unterschiedlichsten Hardwareplattformen wie Redis und ZeroMQ.

Ich meinte mit Hardware nicht die Plattform, sondern die Peripherie. 
Also dass es für die meisten Programme egal ist, woher die Daten kommen 
und wohin sie gehen, ob SPS, GPIOs, RS485-Module, SD-Karte, Netzwerk 
usw. Um auf Werte oder einzelne Bits zuugreifen, werden nur Nummern für 
die Datei und die Adresse in der Datei gebraucht.

Autor: Jobst Q. (joquis)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Finde ich nur irgendwie recht umständlich, einerseits für das System,
> das dann immer den Umweg über ein Dateisystem gehen muss, andererseits
> für den Benutzer, der erstmal eine Ramdisk einrichten muss.

Bei Raspbian, und darum geht es ja hier, gibt es schon eine Ramdisk, 
nämlich /run. Ansonsten ist das Einrichten auch nicht schwer, eine Zeile 
in /etc/fstab und bei laufendem Betrieb ein mount. Das lässt sich auch 
bei der Installation eines Programmes machen.

Für das System ist der Umweg nicht so sehr groß, im Wesentlichen das 
Registrieren von Informationen wie Zeitstempel und Dateilänge. 
Informationen, die aber für vieles auch brauchbar sind . Die Ramdisk 
(ramfs oder tmpfs) ist ja auch recht einfach gegenüber anderen 
Dateisystemen.

Autor: Joerg W. (joergwolfram)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
> Eine Datei hat gegenüber dem shared memory den Vorteil, dass sie viel
> einfacher zu handhaben ist und ohne Aufwand kopiert oder eingelesen
> werden kann.

Bei shared memory hält sich der Aufwand auch in Grenzen. Letztendlich 
braucht man im wesentlichen nur shmget und shmat (und für den daemon 
noch shmdt und shmctl, um das Segment beim Beenden zu löschen).
Dafür muss man nicht jedes Mal die Variablen einlesen, sondern hat 
direkten Zugriff. Locking habe ich bis jetzt auch immer gleich über 
Flag-Variablen im SHM gemacht, weil das meiner Meinung nach am 
flexibelsten ist.

Jörg

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Joerg W. schrieb:
> Locking habe ich bis jetzt auch immer gleich über
> Flag-Variablen im SHM gemacht, weil das meiner Meinung nach am
> flexibelsten ist.

Wie sorgst du dafür, dass der lesende Prozess die Schreibzugriffe in der 
selben Reihenfolge sieht? Wie wartest du darauf dass der Bereich 
freigegeben wird, per Spinlock?

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Wow, jetzt fühl ich mich richtig motiviert was für dich zu
> programmieren.

Naja, dann laß' es meinetwegen. Es ist und bleibt eine Tatsache, daß es 
für das fachliche Ziel des TO ein fertiges Muster namens 
Publish-Subscribe, und vielfach bewährte, gut getestete 
Implementierungen dafür gibt, ganz egal ob man nun eine Library (wie 
ZeroMQ) benutzt oder eine externe Software. Ob Du Deinen Editor jetzt 
anwirfst oder ihn kalt läßt, ändert nichts daran.

Mit ZeroMQ ist die Sache allerdings wirklich ein Kinderspiel. Ein 
Publisher könnte so aussehen (Linken mit -lzmq nicht vergessen!):
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(void) {

    void *context = zmq_ctx_new();
    void *sock = zmq_socket(context, ZMQ_PUB);
    zmq_bind(sock, "tcp://127.0.0.1:5555");

    char buf[] = "bla Hello World!";

    sleep(1); /* wait for our clients */
    
    for(int i = 0; i < 10; ++i) {
  zmq_send(sock, buf, strlen(buf), 0);
    }

    zmq_close(sock);
    zmq_ctx_destroy(context);

    return 0;
}

und ein Subscriber:
#include <zmq.h>
#include <stdio.h>

#define MAXLEN 255

int main(void) {

    void* context = zmq_ctx_new ();
    void* sock = zmq_socket(context, ZMQ_SUB);
    zmq_connect(sock, "tcp://localhost:5555");
    zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "bla", 3);

    char buf[MAXLEN+1];
    
    while(1) {
  int size = zmq_recv(sock, buf, MAXLEN, 0);
  buf[size] = '\0';
  printf("%s\n", buf);
    }

    zmq_close(sock);
    zmq_ctx_destroy(context);

    return 0;
}

35 Zeilen Code. Meine Güte, diese Photonentorpedos! ;-)

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> 35 Zeilen Code. Meine Güte, diese Photonentorpedos! ;-)

Und jetzt bitte noch Kafka und Redis einbinden.

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Was hindert den TO eigentlich daran, die Daten in die Datei zu
> schreiben, statt nur deren Adresse?

Woher wissen die lesenden Prozesse denn dann, daß neue Daten verfügbar 
sind? Also ist schon wieder eine Synchronisation notwendig.

Verzeihung, aber Dateien und Ramdisks gibt es unter UNIXoiden schon 
ziemlich lange, das ganze System beruht doch auf dem Konzept "alles ist 
eine Datei". Aber trotzdem haben die Väter von UNIX recht aufwändige 
Mechanismen für die Interprozesskommunikation implementiert. Warum nur? 
Vielleicht weil sich mit Dateien eben doch nicht alles abbilden läßt?

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Sheeva P. schrieb:
>> 35 Zeilen Code. Meine Güte, diese Photonentorpedos! ;-)
>
> Und jetzt bitte noch Kafka und Redis einbinden.

Du willst es nicht verstehen. Auch gut.

Autor: Lu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
also ich hab es zuerst mit Named Pipe ausprobiert, ist aber für meine 
zwecke nicht sehr tauglich. Es zieht unmengen an CPUleistung, genau so 
gut auf eine Datei zugreiben in diese schreiben und daraus auslesen.
so ab ich es mal mit shared Memory probiert. mein bisheriger Code sieht 
wie folgt aus:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <time.h>

struct  shared_memory1_struct 
{
    uint16_t shared_data;
};

void *shared_memory1_pointer = (void *)0;
//Variablen
struct shared_memory1_struct *shared_memory1;
int shared_memory1_id;
int main (void)
{
uint8_t i;
uint8_t i_sm;  //counter für shared memory

  //erstelle Shared Memory
printf("erstelle shared memory...\n");
shared_memory1_id = shmget((key_t)213151587, sizeof(struct shared_memory1_struct), 0666 | IPC_CREAT);  ///0666 everyone can read and write
//shared memory für programm zugänglich machen
shared_memory1_pointer = shmat(shared_memory1_id, (void *)0, 0);

shared_memory1 = (struct shared_memory1_struct *)shared_memory1_pointer;
while(1)
{

  shared_memory1->shared_data= 2789;
  
  
nanosleep((const struct timespec[]){{0,1000000}}, NULL);
} 
}

Auslesen lassen wollte ich das mit Python2.7, da es für eines meiner 
Bauteile leider nur eine Python Bibiothek gibt...
das hab ich folgendermaßen gemacht:
import sysv_ipc
import time
memory = sysv_ipc.SharedMemory(213151587)
memory_value = memory.read()
while True:
  ausgabe = memory_value
  ausgabe = map(ord,ausgabe)
  print ausgabe
  time.sleep(1)


bei zweistelligen zahlen wird die zahl zwar noch richtig ausgegeben,ist 
aber nur die erste zahl von einem vector der göße ca. [1x100]. die erste 
zahl beispielsweise 20 steht in der ersten spalte, danach folgen 
unzählige nullen.
wird eine 4stellige zahl wie oben im code ausgegeben, so erhalte ich:
[229, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0]

wo liegt hier der Fehler ?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
3 lesenswert
nicht lesenswert
Lu schrieb:
> also ich hab es zuerst mit Named Pipe ausprobiert, ist aber für meine
> zwecke nicht sehr tauglich. Es zieht unmengen an CPUleistung,

Dann hast du etwas falsch gemacht. Wenn du nicht gerade Gigabytes pro 
Sekunde an Daten überträgst, ist der Overhead nicht merkbar.

Autor: Jobst Q. (joquis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sheeva P. schrieb:

> Verzeihung, aber Dateien und Ramdisks gibt es unter UNIXoiden schon
> ziemlich lange, das ganze System beruht doch auf dem Konzept "alles ist
> eine Datei". Aber trotzdem haben die Väter von UNIX recht aufwändige
> Mechanismen für die Interprozesskommunikation implementiert. Warum nur?
> Vielleicht weil sich mit Dateien eben doch nicht alles abbilden läßt?

Hat doch keiner gesagt, dass man shared memory oder andere IPC-Techniken 
nicht verwenden sollte. Hingewiesen wurde nur auf die Tatsache, dass man 
auch gewöhnliche Dateien verwenden kann.

Beides hat seine Vor- und Nachteile, die je nach Anwendungsfall 
bedeutsam sind. Nach den spärlichen Informationen des Themenerstellers 
wäre für seinen Fall beides möglich. Über ähnliche Fälle von anderen 
Interessenten dieses Themas können wir nichts wissen, also ist es gut, 
alle Möglichkeiten aufzuzeigen.

IPC-Techniken sind ein geschlossenes System nur für eingeweihte Prozesse 
auf demselben Rechner. Dateien sind ein offenes System,die Daten sind 
auch für andere Rechner im Netzwerk zugänglich.

Autor: S. R. (svenska)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
>> Was hindert den TO eigentlich daran, die Daten in die Datei zu
>> schreiben, statt nur deren Adresse?
>
> Woher wissen die lesenden Prozesse denn dann, daß neue Daten verfügbar
> sind? Also ist schon wieder eine Synchronisation notwendig.

Nun, die lesenden Prozesse könnten z.B. feststellen, dass die Datei 
gewachsen ist, oder dass sich ihr Zeitstempel geändert hat... das 
passiert üblicherweise, wenn man neue Daten an eine Datei schreibt.

Aber du missverstehst mich:

Die ursprüngliche Aufgabe bestand aus "Ich schreibe die Adresse eines 
Speicherblocks in eine Datei und meine Daten in den Speicher an diese 
Adresse. N andere Prozesse sollen nun die Daten an dieser Adresse 
vorfinden."

Mir ging es darum, dass man
a) auch die Daten selbst in eine Datei schreiben könnte;
b) diese Datei per mmap() mit MAP_SHARED einbinden könnte.

Genau das gibt dem TO, wonach er gefragt hat.
Shared Memory ebenfalls (und ist besser).

Sheeva P. schrieb:
> Verzeihung, aber Dateien und Ramdisks gibt es unter UNIXoiden schon
> ziemlich lange, das ganze System beruht doch auf dem Konzept "alles ist
> eine Datei".

Und? Es hat sich erstaunlich lange erstaunlich gut bewährt. Deswegen 
muss man es nicht wegschmeißen. Ich sehe allerdings ein, dass sich die 
Welt weitergedreht hat:
- statt Dateien haben wir Datenbanken;
- statt normaler Daten haben wir Content;
- unsere Daten sind jetzt Big.

Und da die Menschheit inzwischen Zugang zu quasi unbegrenzt Speicher und 
Rechenzeit hat (AWS & Co), muss man das natürlich auch ausnutzen. Es gab 
mal eine Zeit, da war TCP ein Overhead, den man nicht unnötigerweise 
wollte. Aber das war auch vor Javascript und Python...

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Sheeva P. schrieb:
>> Woher wissen die lesenden Prozesse denn dann, daß neue Daten verfügbar
>> sind? Also ist schon wieder eine Synchronisation notwendig.
>
> Nun, die lesenden Prozesse könnten z.B. feststellen, dass die Datei
> gewachsen ist, oder dass sich ihr Zeitstempel geändert hat... das
> passiert üblicherweise, wenn man neue Daten an eine Datei schreibt.

Hm. Wenn ich das richtig verstanden haben, sollte das Ganze unter 
Raspbian laufen. Raspbian läuft in der Regel auf SBCs, die wiederum 
üblicherweise von SD-Karten laufen. SD-Karten verkraften aber leider nur 
eine begrenzte Zahl an Schreibzyklen; das wird zwar durch den 
Dateisystempufer teilweise abgefedert, aber früher oder später wird die 
SD-Karte kaputt gehen. Darum erscheint es mir für diesen Anwendungsfall 
durchaus sinnvoll zu sein, eine Ramdisk zu benutzen -- aber nicht aus 
Performancegründen, sondern nur um Schreibzyklen zu sparen und so die 
Lebensdauer der SD-Karte zu erhöhen.

Ja, natürlich könnte man auch eine externe USB-Platte benutzen, aber für 
den Preis einer externen USB-Disk bekomme ich 8 SD-Karten... naja, YMMV.

Aber kommen wir zu den anderen konzeptionellen Problemen mit Deinen 
beiden Ansätzen. Zunächst einmal brauchen beide Ansätze -- sowohl die 
Prüfung der Dateigröße als auch die der Zugriffszeiten -- eine 
Synchronisierung. Sonst wissen die lesenden Prozesse nämlich entweder 
nicht, daß es etwas zu lesen gibt -- oder sie pollen sich an den 
Metadaten zu Tode und erzeugen damit eine signifikante Last auf der CPU.

Bei dem Ansatz mit der Dateigröße käme hinzu, daß es ja auch irgendeiner 
Art von Housekeeping bedarf, damit das Dateisystem nicht überläuft. Der 
Ansatz mit den Zugriffszeiten hingegen erhöht wiederum die Schreiblast 
-- das geht damit wieder auf die Lebensdauer der SD-Karte. Natürlich 
können beide Ansätze kombiniert werden -- der Writer macht dann 
periodisch ein f?truncate(2), was die Reader über die Kombination aus 
Dateigröße und Zugriffszeit bemerken und dann entweder nur einen 
fread(3) machen oder vorher ein fseek(3). Das Problem mit dem Polling 
und der Schreiblast besteht bei diesem Ansatz allerdings trotzdem, 
solange Du nicht mit Semaphoren oder Locks arbeiten willst. Wobei Locks 
dann wieder ein ganz anderes Schweinchen bieten: wenn der Lockholder 
abraucht, ohne das Lock vorher aufgehoben zu haben... ;-)

Bad things happen, all the time.

> Aber du missverstehst mich:
>
> Die ursprüngliche Aufgabe bestand aus "Ich schreibe die Adresse eines
> Speicherblocks in eine Datei und meine Daten in den Speicher an diese
> Adresse. N andere Prozesse sollen nun die Daten an dieser Adresse
> vorfinden."

Ja, genau so hatte ich das auch verstanden: ein Prozeß schreibt Daten, 
und n andere Prozesse sollen sie lesen und verarbeiten. Das ist ein sehr 
altes und bekanntes Problem, für das es etliche Lösungen gibt, einige 
davon habe ich oben bereits genannt. Für IPv4-Netzwerke wurden für 
dieses Problem gar zwei eigene Lösungen entwickelt, Broadcast und 
Multicast. Am Ende geht es allerdings um 
Punkt-zu-Multipunkt-Kommunikation, und dafür sind Dateien -- die der 
Persistierung von Daten und eben nicht zur Kommunikation zwischen 
Prozessen dienen -- nun einmal nicht besonders gut geeignet.

> Mir ging es darum, dass man
> a) auch die Daten selbst in eine Datei schreiben könnte;
> b) diese Datei per mmap() mit MAP_SHARED einbinden könnte.
>
> Genau das gibt dem TO, wonach er gefragt hat.
> Shared Memory ebenfalls (und ist besser).

In beiden Fällen bleibt das Synchronisationsproblem bestehen. Leider hat 
sich der TO nicht dazu ausgelassen, wie tolerabel verlorene Writes, 
stale Reads oder ggf. sogar Race Conditions für ihn sind.

> Sheeva P. schrieb:
>> Verzeihung, aber Dateien und Ramdisks gibt es unter UNIXoiden schon
>> ziemlich lange, das ganze System beruht doch auf dem Konzept "alles ist
>> eine Datei".
>
> Und? Es hat sich erstaunlich lange erstaunlich gut bewährt.

... und trotzdem gibt es unter UNIXoiden eine Vielzahl alter und 
moderner Konzepte für die Interprozeßkommunikation. Wenn das gute alte 
"alles ist eine Datei"- Konzept sich so gut und für alle möglichen 
Anwendungsfälle bewährt hätte, würde es die alle nicht geben.

> Deswegen muss man es nicht wegschmeißen.

Aber wer will das denn? Alles, was ich sage, ist: für den Anwendungsfall 
des TO gibt es ein fertiges und bewährtes Konzept, welches eben nicht 
mit Dateien mit ihren inhärenten Synchronisationsproblemen arbeitet, und 
das heißt Publish-Subscribe und steht in etlichen Implementierungen 
(sogar verschiedene Datenbanken bieten mit dem Notify-Listen-Modell eine 
Lösung für dieses häufige Problem an) stabil und getestet zur Verfügung. 
Diese Implementierungen sind nicht nur stabil und getestet, sondern 
bereits so umfangreich optimiert, daß niemand hier -- mich 
eingeschlossen -- eine adäquate und äquivalente Alternative schreiben 
könnte, ohne dafür einen erheblichen Aufwand zu betreiben.

> Ich sehe allerdings ein, dass sich die
> Welt weitergedreht hat:
> - statt Dateien haben wir Datenbanken;
> - statt normaler Daten haben wir Content;
> - unsere Daten sind jetzt Big.

Na komm, die Sprüchlein über "Content" und "Big Data" sind Polemik, das 
weißt Du genau so gut wie ich. Was hingegen die Datenbanken angeht, so 
existieren gute Gründe dafür, ebensolche zu benutzen, denn dabei geht es 
schließlich um genau die Probleme, die sich mit einfachen Dateien eben 
nicht so einfach lösen lassen. Wie, wenn ich das mal so sagen darf, der 
Anwendungsfall des TO -- und der ist ja noch relativ easy, denn bei ihm 
gibt es ja nur einen Writer. Wenn das alles so einfach mit Dateien 
ginge, dann würde es keine Datenbanken geben, und Larry Ellison wäre ein 
armer Schlucker mit einer abgewrackten Jolle. (Nicht, daß ich ihm das 
nicht von Herzen gönnen würde... ;-))

> Und da die Menschheit inzwischen Zugang zu quasi unbegrenzt Speicher und
> Rechenzeit hat (AWS & Co), muss man das natürlich auch ausnutzen. Es gab
> mal eine Zeit, da war TCP ein Overhead, den man nicht unnötigerweise
> wollte. Aber das war auch vor Javascript und Python...

Also wenn Du schon ein "o tempora, o mores" anstimmen und Dich 
vielleicht auch noch über die Jugend auslassen möchtest, die ja schon 
Aristoteles für "unerträglich, unverantwortlich und entsetzlich 
anzusehen" hielt, dann sind ausgerechnet Javascript und Python 
vermutlich die schlechtesten Beispiele unter den modernen 
Skriptsprachen. ;-)

Autor: S. R. (svenska)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> Wenn ich das richtig verstanden haben, sollte das Ganze unter
> Raspbian laufen. Raspbian läuft in der Regel auf SBCs, die
> wiederum üblicherweise von SD-Karten laufen.

Darum: Ramdisk.

Sheeva P. schrieb:
> Sonst wissen die lesenden Prozesse nämlich entweder
> nicht, daß es etwas zu lesen gibt -- oder sie pollen
> sich an den Metadaten zu Tode und erzeugen damit eine
> signifikante Last auf der CPU.

Der Kernel kann dir mitteilen, wenn etwas geschehen ist (z.B. inotify). 
Man muss keine 100% CPU verbraten, um Dateiänderungen mitzubekommen.

Ich kann mir nicht vorstellen, dass du das nicht weißt. Warum behauptest 
du also, es wäre nicht sinnvoll möglich?

Im Ursprungsproblem wäre es aber egal, ob ich eine Variable im shm polle 
oder ob ich ein Byte in einer ge-mmap()-ten Datei polle. Geschenkt.

Sheeva P. schrieb:
> Ja, genau so hatte ich das auch verstanden: ein Prozeß schreibt Daten,
> und n andere Prozesse sollen sie lesen und verarbeiten.

Und zwar über eine Adresse im RAM. Dass das rückwirkend garnicht das 
Problem ist, sei mal dahingestellt. :-)

Sheeva P. schrieb:
> In beiden Fällen bleibt das Synchronisationsproblem bestehen.

Darüber hat der TO kein Wort verloren, also bin ich davon ausgegangen, 
dass das Problem entweder gelöst ist oder gelöst wird. Lösungen auf 
Dateibasis sind auch möglich (record-based locking o.ä.).

Sheeva P. schrieb:
> Wenn das gute alte "alles ist eine Datei"- Konzept
> sich so gut und für alle möglichen Anwendungsfälle
> bewährt hätte, würde es die alle nicht geben.

Es gibt heutzutage Räder aus verschiedensten Materialien in 
verschiedensten Farben, aber das ursprüngliche metallbeschlagene Holzrad 
funktioniert heute auch noch - wenn man nicht gerade mit 200 km/h und 2t 
Leergewicht über eine verkrebste Betonautobahn brettern will. Und selbst 
dann ist das eher eine Frage von der Größe des Rades.

Hier ist das Konzept möglicherweise durchaus sinnvoll einsetzbar, obwohl 
es auch andere Konzepte gibt. Was für den TO passt, kann nur der TO 
einschätzen, denn was wir über das Problem wissen, ist dürftig.

Was du machst ist missionieren, und das ist genauso nervig wie flamen. 
Zumal die Kompetenz in Bezug auf die anderen Lösungen hier deutlich 
höher sein dürfte als dein Vorschlag. Bei Leuten, die mit verteilten 
Systemen zu tun haben, ist das vermutlich sogar andersrum.

Sheeva P. schrieb:
> Na komm, die Sprüchlein über "Content" und "Big Data"
> sind Polemik, das weißt Du genau so gut wie ich.

Teilweise, ja. Aber nicht so viel wie du glaubst.

Schau dir mobile Betriebssysteme an: Da kommst du nicht mehr mit Dateien 
in Berührung, sie werden aktiv vor dir versteckt. Es gibt nur Content 
und ContentProvider, mit denen man "Bilder", "Videos", "Musikstücke", 
"Apps" und so weiter benutzt. Auf iGeräten konnte (kann?) man keine 
Dateien speichern, die das Gerät nicht versteht.

Das Autoradio meines Vaters kann zwar mit MP3 umgehen, unterstützt aber 
keine Dateinamen mehr - die gesamte Benutzerführung läuft über die 
Metadaten (und bringt meinen Vater regelmäßig zur Weißglut).

Big Data (bzw. große Datenbanken) ist nur möglich, weil man inzwischen 
große, verteilte Systeme halbwegs im Griff hat. Dateien, also an sich 
unstrukturierte, lokale Bytehaufen mit festem Ort passen da nicht 
rein. Netzwerkdateisysteme sind und waren schon immer eine Krücke.

In der Cloud gibt es keine Orte mehr (soll es auch nicht geben), auch 
keine unstrukturierten Daten, nur noch annotierte, durchsuchbare 
Inhalte. Die Suchfunktion ersetzt das Menü - schau dir Chrome oder 
Android an. Vorschlagsysteme machen die Suchfunktion benutzbarer. 
Lernende, sich ständig anpassende Systeme verhindern sogar aktiv das 
motorische Gedächtnis.

Auf Serverseite hantiert man zwar noch mit Dateien, aber auch da wird es 
weniger (z.B. Docker, diverse Administrationsoberflächen). Eine normale 
Android-App besteht aus einer Ordnerstruktur, die ohne IDE kaum 
erträglich ist. Make ist dateigetrieben, aktuelle Buildsysteme (CMake, 
gradle, pip) sind stattdessen datengetrieben (und holen sich notwendige 
Dinge direkt aus dem Netz).

Das ist die aktuelle Entwicklung.
Kann man mögen oder auch nicht, aber weg geht das nicht mehr.

Sheeva P. schrieb:
> dann sind ausgerechnet Javascript und Python
> vermutlich die schlechtesten Beispiele unter den modernen
> Skriptsprachen. ;-)

Sowohl in Javascript als auch in Python (zu nennen wäre definitiv auch 
PHP) wird unglaublich viel unglaublich schlechter Code verzapft. Damit 
meine ich nicht die Sprachen selbst, sondern ihre Nutzer.

Eine gute Beschreibung, die ich dazu heute gelesen habe, nannte es das 
"Visual Basic-Symptom". Die übliche Nutzerbasis von Visual Basic (und 
insbesondere VBA) kann nämlich nicht programmieren, sondern bastelt sich 
eine Lösung zusammen, die das aktuelle Problem irgendwie löst. Das 
Ergebnis (und der resultierende Ruf von Visual Basic) ist bekannt.

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Darum: Ramdisk.

Das schrieb ich ja. Das heißt dann aber leider auch, daß die Ramdisk 
eingerichtet werden muß, womit zusätzliche Abhängigkeiten und damit 
potentielle Fehlerquellen außerhalb der Software erzeugt werden. Das 
kann man machen, aber sonderlich elegant ist das leider nicht.

> Der Kernel kann dir mitteilen, wenn etwas geschehen ist (z.B. inotify).
> Man muss keine 100% CPU verbraten, um Dateiänderungen mitzubekommen.
>
> Ich kann mir nicht vorstellen, dass du das nicht weißt. Warum behauptest
> du also, es wäre nicht sinnvoll möglich?

Weil ich schon sehr intensiv mit inotify gearbeitet habe und daher weiß, 
daß es in vielen Fällen Writes verliert. Die Manpage von inotify(7) 
warnt sogar ausdrücklich davor: "Note that the event queue can overflow. 
In this case, events are lost. Robust applications should handle the 
possibility of lost events gracefully".

> Im Ursprungsproblem wäre es aber egal, ob ich eine Variable im shm polle
> oder ob ich ein Byte in einer ge-mmap()-ten Datei polle. Geschenkt.

Polling ist halt Mist. Immer. Sowas macht ein guter Entwickler nur dann, 
wenn es wirklich nicht anders geht.

> Sheeva P. schrieb:
>> Ja, genau so hatte ich das auch verstanden: ein Prozeß schreibt Daten,
>> und n andere Prozesse sollen sie lesen und verarbeiten.
>
> Und zwar über eine Adresse im RAM. Dass das rückwirkend garnicht das
> Problem ist, sei mal dahingestellt. :-)

Das war die Idee des TO, aber nicht der Anwendungsfall. Ohne 
spezielle Techniken wie Shared Memory könnte er ohnehin nicht mit einem 
Prozeß auf eine Adresse zugreifen, die einem anderen Prozeß gehört. 
Erstens, weil jedes moderne Betriebssystem das mit einer 
Zugriffsverletzung quittiert.

Zweitens aber auch deswegen, weil prozessintern gar nicht mit absoluten 
Speicheradressen gearbeitet wird, sondern mit relativen. Sogar wenn 
unser TO seinen Prozeß dazu bringt, die Speicheradresse einer Variablen 
in eine Datei zu schreiben, würde ihm das nichts nutzen: denn die 
Speicheradresse 0x123 seines Writer-Prozesses W zeigt natürlich auf 
einen völlig anderen physikalischen Speicher als die Speicheradressen 
0x123 seiner Reader.

> Darüber hat der TO kein Wort verloren, also bin ich davon ausgegangen,
> dass das Problem entweder gelöst ist oder gelöst wird.

Angesichts der Fragestellung des TO, die, wie im vorhergehenden 
Abschnitt beschrieben, fundamental mit der Speicherverwaltung seines 
Betriebssystem kollidiert, halte ich diese Annahme für, mit Verlaub, 
naiv. Im Gegenteil gehe ich eher davon aus, daß sich der TO um 
Concurrency, Synchronisation, Lost Writes, Stale Reads, Race Conditions 
oder ähnliche Fallstricke noch überhaupt gar keine Gedanken gemacht hat. 
;-)

> Lösungen auf Dateibasis sind auch möglich (record-based locking o.ä.).

Also lieber selbst eine Datenbank schreiben, anstatt eine vorhandene zu 
benutzen? Hauerha, das wird spannend! ;-)

> Es gibt heutzutage Räder aus verschiedensten Materialien in
> verschiedensten Farben, aber das ursprüngliche metallbeschlagene Holzrad
> funktioniert heute auch noch - wenn man nicht gerade mit 200 km/h und 2t
> Leergewicht über eine verkrebste Betonautobahn brettern will. Und selbst
> dann ist das eher eine Frage von der Größe des Rades.

Du wirst es trotzdem nicht erleben, daß ich mit einem metallbeschlagenen 
Holzrad an einem Downhill-Rennen teilnehme.

> Hier ist das Konzept möglicherweise durchaus sinnvoll einsetzbar, obwohl
> es auch andere Konzepte gibt. Was für den TO passt, kann nur der TO
> einschätzen, denn was wir über das Problem wissen, ist dürftig.

Das ist allerdings wahr.

> Was du machst ist missionieren, und das ist genauso nervig wie flamen.

Das magst Du vielleicht so wahrnehmen, ist aber nicht der Fall. Tatsache 
ist, daß ich ressourcenschonende und fertige Lösungen für das Problem 
des TO angeboten habe, woraufhin mich ein gefühlt besonders kluger 
Mensch darauf hingewiesen hat, daß das ja alles viel zu aufwändig und 
deswegen "mit Photonentorpedos auf Amöben" geschossen sei. Stattdessen 
hat der besagte kluge Mensch etwas vorgeschlagen, das keine Lösung sein 
kann, da damit zwar der reine Datenaustausch zwischen den Prozessen 
realisiert, das unserer Fragestellung inhärente Synchronisationsproblem 
damit aber leider nicht korrekt gelöst werden kann.

> Zumal die Kompetenz in Bezug auf die anderen Lösungen hier deutlich
> höher sein dürfte als dein Vorschlag. Bei Leuten, die mit verteilten
> Systemen zu tun haben, ist das vermutlich sogar andersrum.

Mag sein, aber ich habe nun einmal mit verteilten Systemen zu tun und 
weiß daher, daß es für solche Probleme bereits fertige Lösungen gibt, 
die das Gewünschte mit wenigen Zeilen Code (siehe meine Beispiele oben) 
zur Verfügung stellen und kein einziges der Probleme haben, über die wir 
hier diskutieren. Und die einzigen "traditionellen" UNIX-Lösungen leiden 
alle darunter, daß sie wesentlich schwieriger und aufwändiger umzusetzen 
sind als die von mir gezeigte Lösung mit ZeroMQ.

Mal schauen, wenn ich Zeit und Lust habe, schreib' ich vielleicht auch 
noch ein kleines Beispiel mit Redis. Ja, damit schaffe ich auch 
Abhängigkeiten außerhalb der Software, aber dafür bietet Redis noch ein 
paar sehr nette Zusatzmöglichkeiten für eine Software, die letzten Endes 
nicht nur Daten zwischen verschiedenen Prozessen austauschen, sondern 
auch händeln muß.

> Das Autoradio meines Vaters kann zwar mit MP3 umgehen, unterstützt aber
> keine Dateinamen mehr - die gesamte Benutzerführung läuft über die
> Metadaten (und bringt meinen Vater regelmäßig zur Weißglut).

Das tut mir zwar ausgesprochen leid für Deinen Herrn Papa (bitte grüße 
ihn unbekannterweise von mir), ist hier aber nicht das Thema. Soweit ich 
weiß, können die in Mediadateien vorhandenen Metadaten aber editiert 
werden, so daß es möglicherweise kein Problem wäre, den Dateinamen 
einfach in diese Metadatenfelder hinein zu schreiben? Hab' ich aber 
nicht ausprobiert.

> Big Data (bzw. große Datenbanken) ist nur möglich, weil man inzwischen
> große, verteilte Systeme halbwegs im Griff hat. Dateien, also an sich
> unstrukturierte, lokale Bytehaufen mit festem Ort passen da nicht
> rein.

Tatsächlich geht "Big Data" heute genau in diese Richtung, in Form eines 
sogenannten "Data Lake". Darin sammeln der Betreiber zunächst einmal 
alle Daten, derer er habhaft werden kann, strukturierte und 
unstrukturierte, alles idealerweise im Rohformat. Erst bei der 
Exploration und Analyse der Daten werden diese je nach Bedarf ausgewählt 
und (meist) in strukturierte Daten überführt.

Wie dem auch sei, dieses Thema können wir gerne ein andermal und in 
einem anderen Thread besprechen, aber hier gehört das nicht her.

Autor: S. R. (svenska)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> Das war die Idee des TO, aber nicht der Anwendungsfall.

Ich gehöre zu den Menschen, die anderen gerne bei der Lösung ihrer 
Probleme helfen - und wenn mich jemand direkt nach X fragt, dann bekommt 
er auch eine Antwort zu X. Manchmal auch dann, wenn er eigentlich Y 
meinte. :-)

Sheeva P. schrieb:
> Wie dem auch sei, dieses Thema können wir gerne ein andermal und in
> einem anderen Thread besprechen, aber hier gehört das nicht her.

Da hast du wohl recht. Gute Nacht. :-)

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Ich gehöre zu den Menschen, die anderen gerne bei der Lösung ihrer
> Probleme helfen - und wenn mich jemand direkt nach X fragt, dann bekommt
> er auch eine Antwort zu X. Manchmal auch dann, wenn er eigentlich Y
> meinte. :-)

Die korrekte Antwort auf das "X" des TO wäre das gewesen, was ich in 
meinem vorherigen Beitrag in den beiden mit "Das war die Idee des TO" 
beginnenden Abschnitten geschrieben habe. Mir ging es nur darum, dem TO 
auch das "Y" zu zeigen, also: wie er sein eigentliches Problem sauber 
lösen kann.

> Sheeva P. schrieb:
>> Wie dem auch sei, dieses Thema können wir gerne ein andermal und in
>> einem anderen Thread besprechen, aber hier gehört das nicht her.
>
> Da hast du wohl recht. Gute Nacht. :-)

Dankeschön, Dir auch. ;-D

Spaßeshalber habe ich die PubSub-Lösung jetzt auch noch einmal mit Redis 
implementiert. Das Error-Handling ist sicherlich noch 
verbesserungswürdig, aber das Prinzip wird hoffentlich klar.

Publisher:
#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {

    redisContext* conn = redisConnect("127.0.0.1", 6379);
    if(conn == NULL || conn->err)  {
  fprintf(stderr, "Error connecting Redis\n"), exit(-1);
    }

    sleep(2); /* wait for subscribers */

    for(int i = 0; i < 10; ++i) {
  char buffer[20];
  sprintf(buffer, "Hello %d", i);
  
  redisReply* reply = (redisReply*)redisCommand(conn, "PUBLISH bla %s", buffer);
  if(reply->type == REDIS_REPLY_INTEGER) {
      printf("%lld clients received \"%s\"\n", reply->integer, buffer);
  }
  
  freeReplyObject(reply);
    }
    
    redisFree(conn);
    return 0;
}


Subscriber:
#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {

    redisContext* conn = redisConnect("127.0.0.1", 6379);

    if(conn == NULL || conn->err) {
  fprintf(stderr, "Error connecting Redis\n"), exit(-1);
    }

    redisReply* reply = (redisReply*)redisCommand(conn, "SUBSCRIBE bla");
    while(redisGetReply(conn, (void**)&reply) == REDIS_OK) {
  if(reply->elements == 3) {
      printf("Got reply->str: %s\n", reply->element[2]->str);
  }
        freeReplyObject(reply);
    }
    freeReplyObject(reply);
    
    redisFree(conn);

    return 0;
}

41 LOC... auch keine Rocket Science, methinks. ;-)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.