Seite 1 von 2
Wie Kernel Funktionen verwenden?
Verfasst: 28.01.2013 14:19
von Justin
Hallo,
ich wollte mal anfrage, wie man Kernel- Funktionen verwenden kann. Z.B. zur Verzeichnisüberwachung
http://en.wikipedia.org/wiki/Inotify.
Gruß
Justin
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 21:06
von MVXA
Hi,
Für „Kernel Funktionen“ gibt es in der /lib/libc.so bzw /usr/lib/libc.so (von Distri zu Distri verschieden) Wrapper, die man aus Programmen heraus aufrufen kann/soll.
Für C und C++ gibt es unter /usr/include/ jede Menge Dateien, Header genannt, die die Prototypen für die Funktionen in der libc deklarieren. Will man nun die Funktionen aus der libc in Purebasic direkt verwenden, muss man diese für den Compiler erstmal sichtbar machen, sprich deklarieren. Dies geht mit Hilfe des ImportC Keywords. Leider müssen die Header von Hand übersetzt werden. Daher ist Purebasic auch nur bedingt gut für die Entwicklung unter Linux geeignet.
Für Inotify sieht das z.B. so aus:
Code: Alles auswählen
ImportC "/lib/libc.so"
inotify_init.l ()
inotify_add_watch.l (fd.l, pathname.s, mask.l)
inotify_rm_watch.l (fd.l, wd.l)
_os_read.l (fd.l, *buf, count.l) As "read"
EndImport
Structure inotify_event
wd.l
mask.l
cookie.l
len.l
name.s{4096}
EndStructure
#IN_ACCESS = $00000001
#IN_MODIFY = $00000002
#IN_ATTRIB = $00000004
#IN_CLOSE_WRITE = $00000008
#IN_CLOSE_NOWRITE = $00000010
#IN_CLOSE = (#IN_CLOSE_WRITE | #IN_CLOSE_NOWRITE)
#IN_OPEN = $00000020
#IN_MOVED_FROM = $00000040
#IN_MOVED_TO = $00000080
#IN_MOVE = (#IN_MOVED_FROM | #IN_MOVED_TO)
#IN_CREATE = $00000100
#IN_DELETE = $00000200
#IN_DELETE_SELF = $00000400
#IN_MOVE_SELF = $00000800
#IN_UNMOUNT = $00002000
#IN_Q_OVERFLOW = $00004000
#IN_IGNORED = $00008000
#IN_CLOSE = (#IN_CLOSE_WRITE | #IN_CLOSE_NOWRITE)
#IN_MOVE = (#IN_MOVED_FROM | #IN_MOVED_TO)
#IN_ONLYDIR = $01000000
#IN_DONT_FOLLOW = $02000000
#IN_EXCL_UNLINK = $04000000
#IN_MASK_ADD = $20000000
#IN_ISDIR = $40000000
#IN_ONESHOT = $80000000
#IN_ALL_EVENTS = (#IN_ACCESS | #IN_MODIFY | #IN_ATTRIB | #IN_CLOSE_WRITE | #IN_CLOSE_NOWRITE | #IN_OPEN | #IN_MOVED_FROM | #IN_MOVED_TO | #IN_CREATE | #IN_DELETE | #IN_DELETE_SELF | #IN_MOVE_SELF)
;
; EXAMPLE
;
; Der "File Descriptor", der von inotify_init zurück gegeben wird, kann auch
; in einem (e)poll getriebenen reaktor pattern wiederverwendet werden. Das ist
; interessant für asynchrone programmierung.
fd.l = inotify_init()
; Der Kernel wird uns darüber informieren, wenn eine Datei angelegt oder gelöscht
; wurde.
wd.l = inotify_add_watch(fd, "/tmp", #IN_CREATE | #IN_DELETE)
; Die Events sind von dynamischer Größe, da sie Dateinamen enthalten.
; Der Buffer wird daher bestenfalls ~256/64 Events halten können. Sollten
; doch noch mehr vorliegne, werden diese im 2. Durchlaufe eingelesen. Alles Dandy.
*buf.inotify_event = AllocateMemory(SizeOf(inotify_event) * 256)
Repeat
; Lese so viele Events wie möglich und fülle diese in den Buffer
length.l = _os_read(fd, *buf, SizeOf(inotify_event) * 256)
; tmp_buf wird unsere Laufvariable. Sie wird nach jeden Event von uns hoch gezählt
; und zeigt auf das nächste Event.
*tmp_buf.inotify_event = *buf
While *tmp_buf < *buf + length
; \name enthält den \0 terminierten gültigen Dateinamen, der Subjekt
; eines Ereignisses wurde.
; \mask enthält eine Verknüpfung aus den oben definierten Konstanten, die
; das Event beschreiben (Datei gelöscht oder angelegt).
If *tmp_buf\mask & #IN_CREATE = #IN_CREATE
Debug *tmp_buf\name + " Angelegt (" + RSet(Hex(*tmp_buf\mask), 8, "0") + ")"
ElseIf *tmp_buf\mask & #IN_DELETE = #IN_DELETE
Debug *tmp_buf\name + " Gelöscht (" + RSet(Hex(*tmp_buf\mask), 8, "0") + ")"
Else
Debug *tmp_buf\name + " Unbekanntes Event (" + RSet(Hex(*tmp_buf\mask), 8, "0") + ")"
EndIf
; Pointer auf das nächste Event zeigen lassen.
*tmp_buf = *tmp_buf + SizeOf(inotify_event) + *tmp_buf\len
Wend
ForEver
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 21:45
von ts-soft
Hallo Arthur
Ich denke mal, der Import sollte so aussehen:
Code: Alles auswählen
ImportC "/lib/libc.so"
inotify_init.l ()
inotify_add_watch (fd.l, pathname.s, mask.l)
inotify_rm_watch (fd.l, wd.l)
_os_read (fd.l, *buf, count.l) As "read"
EndImport
*buf.s ist nicht mehr erlaubt (ab PB5.10)
// edit: die folgende Zeile trifft auf Linux nicht zu! Source geändert.
Die anderen Datentypen sind lt. Doku Integer, was natürlich nur auf einem 64-Bit Linux wichtig sein sollte.
Gruß
Thomas
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 22:05
von MVXA
> Hallo Arthur
Hi
> *buf.s ist nicht mehr erlaubt (ab PB5.10)
Klingt vernünftig, ein String ist es ja im Grunde nicht.
> Die anderen Datentypen sind lt. Doku Integer, was natürlich nur auf einem 64-Bit Linux wichtig sein sollte.
Shit, das müssen eigentlich .l sein in dem Fall dann, da hab ich wohl Mist gebaut. In den Header sind sie als int deklariert und so weit ich mich erinnern kann sind diese immer noch 32Bit breit. Auch unter 64Bit Linux
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 22:16
von ts-soft
Sicher bin ich mir da nicht, aber ich verstehe das so, das es Integer sind,
sind ja Handles, diese sind eigentlich immer Integer.
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 22:22
von NicTheQuick
Ja, int sind immer 32 Bit, auch unter einem 64-Bit Linux.
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 22:23
von MVXA
ts-soft hat geschrieben:Sicher bin ich mir da nicht, aber ich verstehe das so, das es Integer sind,
sind ja Handles, diese sind eigentlich immer Integer.
Code: Alles auswählen
#include <stdio.h>
int
main ()
{
printf("%zd\n", sizeof(int));
return 0;
}
Gibt mir auf der Shell
aus

. Hier läuft
Linux yuno 3.7.5-1-ARCH #1 SMP PREEMPT Mon Jan 28 10:03:32 CET 2013 x86_64 GNU/Linux
Edit:
Damit ist die Verwirrung komplett

. Dachte zuerst ich hätte mich vertan, aber die Felder sind ja schon als .l deklariert

. Ich passe das Beispiel trotzdem nochmal an.
Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 22:28
von ts-soft
Scheint, als wenn Ihr Recht habt. Scheint dann aber nur für Linux zu gelten.
Integer (Datentyp) - Wikepedia hat geschrieben:Ein Integer besteht in der Regel aus 8, 16, 32, 64 oder 128 Bits (also 1, 2, 4, 8 oder 16 Bytes) – entsprechend der Wortbreite der jeweiligen CPU
Hier haben wir also die Ausnahme von der Regel

Re: Wie Kernel Funktionen verwenden?
Verfasst: 07.02.2013 22:51
von NicTheQuick
Unter Windows ist es auch so. Das hat hier aber mehr mit der Programmiersprache zu tun und nicht mit der allgemeinen Definition für Ganzzahlen.
Siehe hier:
int on a 64-Bit machine
Es gibt sogar Unterschiede zwischen verschiedenen Compilern (
What should be the sizeof(int) on a 64-bit machine?). Ich spreche übrigens auch aus Erfahrung.

Wenn ich unter C/C++ mit Pointern oder Handels arbeiten will, dann nutze ich dort auch diesen Datentyp. Der passt sich dann ganz sicher an. Z.B. void*.
Re: Wie Kernel Funktionen verwenden?
Verfasst: 21.07.2014 16:38
von mariosk8s
Hm, bei mir tut das nicht
Ähnlicher code wie oben auf mein Betriebs system angepasst
Code: Alles auswählen
EnableExplicit
ImportC "/lib/i386-linux-gnu/libc.so.6"
inotify_init.l ()
inotify_add_watch.l (fd.l, pathname.s, mask.l)
inotify_rm_watch.l (fd.l, wd.l)
_os_read.l (fd.l, *buf, count.l) As "read"
EndImport
Structure inotify_event
wd.l
mask.l
cookie.l
len.l
name.s{4096}
EndStructure
#IN_ACCESS = $00000001
#IN_MODIFY = $00000002
#IN_ATTRIB = $00000004
#IN_CLOSE_WRITE = $00000008
#IN_CLOSE_NOWRITE = $00000010
#IN_CLOSE = (#IN_CLOSE_WRITE | #IN_CLOSE_NOWRITE)
#IN_OPEN = $00000020
#IN_MOVED_FROM = $00000040
#IN_MOVED_TO = $00000080
#IN_MOVE = (#IN_MOVED_FROM | #IN_MOVED_TO)
#IN_CREATE = $00000100
#IN_DELETE = $00000200
#IN_DELETE_SELF = $00000400
#IN_MOVE_SELF = $00000800
#IN_UNMOUNT = $00002000
#IN_Q_OVERFLOW = $00004000
#IN_IGNORED = $00008000
#IN_CLOSE = (#IN_CLOSE_WRITE | #IN_CLOSE_NOWRITE)
#IN_MOVE = (#IN_MOVED_FROM | #IN_MOVED_TO)
#IN_ONLYDIR = $01000000
#IN_DONT_FOLLOW = $02000000
#IN_EXCL_UNLINK = $04000000
#IN_MASK_ADD = $20000000
#IN_ISDIR = $40000000
#IN_ONESHOT = $80000000
#IN_ALL_EVENTS = (#IN_ACCESS | #IN_MODIFY | #IN_ATTRIB | #IN_CLOSE_WRITE | #IN_CLOSE_NOWRITE | #IN_OPEN | #IN_MOVED_FROM | #IN_MOVED_TO | #IN_CREATE | #IN_DELETE | #IN_DELETE_SELF | #IN_MOVE_SELF)
;
; EXAMPLE
;
; Der "File Descriptor", der von inotify_init zurück gegeben wird, kann auch
; in einem (e)poll getriebenen reaktor pattern wiederverwendet werden. Das ist
; interessant für asynchrone programmierung.
Define fd.l = inotify_init()
Debug "got fd " + Str(fd)
; Der Kernel wird uns darüber informieren, wenn eine Datei angelegt oder gelöscht
; wurde.
Define wd.l = inotify_add_watch(fd, "/tmp/", #IN_ALL_EVENTS)
Debug "got wd1 " + Str(wd)
Define wd2.l = inotify_add_watch(fd, "/etc/", #IN_ALL_EVENTS)
Debug "got wd2 " + Str(wd2)
; Die Events sind von dynamischer Größe, da sie Dateinamen enthalten.
; Der Buffer wird daher bestenfalls ~256/64 Events halten können. Sollten
; doch noch mehr vorliegne, werden diese im 2. Durchlaufe eingelesen. Alles Dandy.
Define *buf.inotify_event = AllocateMemory(SizeOf(inotify_event) * 256)
Repeat
; Lese so viele Events wie möglich und fülle diese in den Buffer
Debug "read max " + Str(SizeOf(inotify_event) * 256) + " bytes from fd" + Str(fd)
Define length.l = _os_read(fd, *buf, SizeOf(inotify_event) * 256)
Debug "length " + Str(length)
; tmp_buf wird unsere Laufvariable. Sie wird nach jeden Event von uns hoch gezählt
; und zeigt auf das nächste Event.
Define *tmp_buf.inotify_event = *buf
While *tmp_buf < *buf + length
; \name enthält den \0 terminierten gültigen Dateinamen, der Subjekt
; eines Ereignisses wurde.
; \mask enthält eine Verknüpfung aus den oben definierten Konstanten, die
; das Event beschreiben (Datei gelöscht oder angelegt).
If *tmp_buf\mask & #IN_CREATE = #IN_CREATE
Debug *tmp_buf\name + " Angelegt (" + RSet(Hex(*tmp_buf\mask), 8, "0") + ")"
ElseIf *tmp_buf\mask & #IN_DELETE = #IN_DELETE
Debug *tmp_buf\name + " Gelöscht (" + RSet(Hex(*tmp_buf\mask), 8, "0") + ")"
Else
Debug *tmp_buf\name + " Unbekanntes Event (" + RSet(Hex(*tmp_buf\mask), 8, "0") + ")"
EndIf
; Pointer auf das nächste Event zeigen lassen.
*tmp_buf = *tmp_buf + SizeOf(inotify_event) + *tmp_buf\len
Wend
ForEver
liefert leider nur
Code: Alles auswählen
got fd 3
got wd1 1
got wd2 1
read max 2101248 bytes from fd3
und hängt dann endlos am _os_read Aufruf.
Weiterhin ist komisch, dass beide Male der selbe watch descriptor (wd1, wd2) nämlich 1 zurückkommt.
Ähnliche code in C funktioniert wie erwartet.
Funktioniert das bei irgendjemand?
Bei mir läuft Ubuntu 13.04 kernel 3.8.0-25-generic #37-Ubuntu SMP i686