Forum: PC-Programmierung wie benutzt man pseudoterminals?


von Bauform B. (bauformb)


Lesenswert?

schönen Freitag miteinander!

Programm1 redet normalerweise per /dev/ttyUSBx mit einer Hardware. Jetzt 
soll Programm2 diese Hardware simulieren. Prog1 kann einen symlink (z.B. 
"/tmp/hardware") für die Schnittstelle benutzen, dann sieht es sehr 
einfach aus:
1
pts = open("/dev/ptmx", O_RDONLY);
2
path = ptsname (pts);
3
grantpt (pts);
4
unlockpt (pts);
5
symlink (path, "/tmp/hardware");
so kann Prog1 sein normales open("/tmp/hardware") machen.

Jetzt muss man "nur noch" dafür sorgen, dass der Link entfernt wird, 
wenn Prog2 beendet wird. atexit() funktioniert dafür nur meistens. Ja, 
Prog1 könnte das pty in /proc/$(pidof Prog2)/fd/ suchen, aber wie macht 
man das normalerweise?

von Jim M. (turboj)


Lesenswert?

Den Symlink könnte man früher löschen - der kann weg sobald Prog1 sein 
open() call ausgeführt hat.

Ob das in der Praxis funktioniert ist zweifelhaft.

TTYUSB kennt noch die für UART/RS232 typischen Schnittstellenparameter 
und deren Syscalls. Die funktionieren auf Dateien oder named Pipes 
nicht.

von Bauform B. (bauformb)


Lesenswert?

Jim M. schrieb:
> Den Symlink könnte man früher löschen - der kann weg sobald Prog1 sein
> open() call ausgeführt hat.

Gute Idee, danke!

> Ob das in der Praxis funktioniert ist zweifelhaft.

Meinst du das Link-löschen oder die Schnittstellenparameter? Es bleibt 
ein kleines Restrisiko: Prog2 startet, Prog1 aber nicht, Prog2 erwischt 
einen Segfault, Prog1 startet und findet den kaputten Link. Irgendwas 
ist immer; hauptsächlich wundert mich, dass es dafür scheinbar keinen 
Mechanismus gibt. So in der Art, wie z.B. Speicher garantiert 
freigegeben wird, egal, wie das Programm beendet wird.

Die ioctl() Syscalls funktionieren, praktisch als NOP. Nur das Timing 
ist natürlich völlig anders, dagegen reicht mir in diesem speziellen 
Fall ein usleep().

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Jim M. schrieb:
> Den Symlink könnte man früher löschen - der kann weg sobald Prog1 sein
> open() call ausgeführt hat.
>
> Ob das in der Praxis funktioniert ist zweifelhaft.

Das gilt eigentlich für alle was über Filedescriptoren angesprochen 
wird. Mann kann das Objekt (File, (Sym)Link, Gerät, ...) auf 
Filesystem-Ebene hinterrücks löschen. Das Objekt bleibt trotzdem so 
lange erhalten bis der letzte Filedescriptor drauf zu gemacht wird.

Eine uralte, schon zu UNIX-Zeiten beliebte Technik um zum Beispiel 
temporäre Dateien automatisch zu löschen wenn der Prozess endet. Datei 
öffnen, Filedescriptor brav behalte, Datei im Filesystem löschen, frisch 
und fröhlich mit der nun unsichtbaren Datei über den Filedescriptor 
arbeiten. Der Prozess endet und Linux räumt erst dann die Datei ab.

von Thomas W. (datenreisender)


Lesenswert?

Hannes J. schrieb:

> Eine uralte, schon zu UNIX-Zeiten beliebte Technik um zum Beispiel
> temporäre Dateien automatisch zu löschen wenn der Prozess endet. Datei
> öffnen, Filedescriptor brav behalte, Datei im Filesystem löschen, frisch
> und fröhlich mit der nun unsichtbaren Datei über den Filedescriptor
> arbeiten. Der Prozess endet und Linux räumt erst dann die Datei ab.

Oder, wenn der Prozess weiter und weiter auf /tmp schreibt, gibt es eine 
panic und der Systemverwalter steht in Deinem Buero... Der schoene neue 
Convex-Supercomputer sollte fuer etwas gut sein...

von Daniel A. (daniel-a)


Lesenswert?

> Ja, Prog1 könnte das pty in /proc/$(pidof Prog2)/fd/ suchen, aber wie macht
> man das normalerweise?

Da würde es aber doch nur das ptm, nicht das pts, finden.

Falls Programm 2 von Program 1 gestartet wird, könnte man Program 2 ein 
FD von Program 1 übergeben. Man könnte das pts öffnen und als FD 0,1,2 
(stdin, stdout, stderr) setzen. Oder man könnte eine environment 
Variable oder einen Parameter setzen, und ihm so mitteilen, wo das pts 
zu finden ist.

(Mit /proc/self/fd/N kann man für FDs auch einen Dateipfad angeben. Aber 
ein open da drauf gibt eine neue File Description (nicht nur einen neuen 
File Descriptor). Beim pts kein Problem, bei (gelöschten) Dateien auch 
nicht. Aber z.B. bei einem fd auf ein ptm von /dev/ptmx bekäme man ein 
neues pts).

: Bearbeitet durch User
von Bauform B. (bauformb)


Lesenswert?

Daniel A. schrieb:
>> Ja, Prog1 könnte das pty in /proc/$(pidof Prog2)/fd/ suchen, aber wie macht
>> man das normalerweise?
>
> Da würde es aber doch nur das ptm, nicht das pts, finden.

Stimmt auch wieder. Wenn man einem vorhandenen Programm eine Hardware 
vortäuschen will, kommt sowas ja sowieso nicht in Frage, da ist ein 
symlink vernünftig. Und im Normalfall, wenn beide Programme 
zusammenarbeiten, funktioniert ja alles automatisch richtig. Dankeschön!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Thomas W. schrieb:
> Oder, wenn der Prozess weiter und weiter auf /tmp schreibt, gibt es eine
> panic und der Systemverwalter steht in Deinem Buero...

Systemverwalter lernt was über Quotas.

von Philipp K. (philipp_k59)


Lesenswert?

Im Prinzip sollte kein Programm in einen segfault laufen, das wäre 
eigentlich das Maß der Dinge.

Man kann ansonsten alle anderen Sigs abfangen und abhandeln, dann wäre 
jede andere Möglichkeit den Symlink doch zu löschen abgedeckt.

Einen kritischen Segfault kann man verhindern indem man es richtig 
programmiert. man könnte auch bei jedem start des "Programm2" einen 
vorhandenen symlink löschen. Somit gäbe es keinen unlösbaren Worst case.

von Daniel A. (daniel-a)


Lesenswert?

Philipp K. schrieb:
> Man kann ansonsten alle anderen Sigs abfangen und abhandeln, dann wäre
> jede andere Möglichkeit den Symlink doch zu löschen abgedeckt.

SIGSEGV kann man abfangen, zumindest auf linux. Aber es gibt andere 
Signale, die man nicht abfangen kann. z.B. SIGKILL.

von Daniel A. (daniel-a)


Lesenswert?

Noch eine Sache zu den PTS. Man kann die übrigens auch bind-mounten, um 
sie mehr wie ein echtes TTY statt wie ein PTY aussehen zu lassen: 
https://github.com/Daniel-Abrecht/console-keyboard-multiplexer/blob/master/src/console-keyboard-multiplexer.c#L919-L965

Sofern man sich aber nicht per login(1) da darauf einloggen will, ist 
das normalerweise aber etwas Overkill.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Wenn du auf beide Codes (Anwendung und Simulator) vollen Zugriff hast, 
könnte man auch auf unterster Ebene eine alternative Kommunikation 
einbauen, speziell für den Simu-Fall, z.B. per IP oder IPC oder schlicht 
ein File ...

von Bauform B. (bauformb)


Lesenswert?

Frank E. schrieb:
> Wenn du auf beide Codes (Anwendung und Simulator) vollen Zugriff hast,
> könnte man auch auf unterster Ebene eine alternative Kommunikation...

hätte ich sogar, aber das geht in die falsche Richtung. So richtig 
"minimal-invasiv" ist nur ein Kabel und eine echte serielle 
Schnittstelle; dann stimmt auch das Timing ganz von alleine.

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Nur um das nochmal mit Beispiel gezeigt zu haben, du könntest das 
Programm, welches das TTY braucht, von dem starten lassen, welches es 
bereitstellt.
1
$ ./terminal_providing_program bash -c 'echo "Hello World" >"$MY_DEVICE_PATH"'
2
Got data 13: Hello World..
3
Child program exited
4
Stopping program!
5
$ ./terminal_providing_program bash -c 'exec <>"$MY_DEVICE_PATH" >&0 2>&0; echo test'
6
Got data 6: test..
7
Child program exited
8
Stopping program!
9
$ ./terminal_providing_program bash -c 'exec {fd}<>"$MY_DEVICE_PATH"; export MY_DEVICE_FD="$fd"; bash -c "echo 123 >&$MY_DEVICE_FD"'

Kann man als Pfad, über fixe FDs (z.B. stdin/stdout), oder per erstem 
freien FD + ne env variable, übergeben.

So braucht man keine Symlinks, und es bleibt nachher auch nichts zurück.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.