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 :wink:
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 :wink:
Hi :wink:

> *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

Code: Alles auswählen

ask:[~]$ ./test 
4
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 :mrgreen:. 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 :roll:

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. :wink:
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 :cry:

Ä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