Wie Kernel Funktionen verwenden?

In dieser Linux-Ecke dürfen nur Themen rund um Linux geschrieben werden.
Beiträge, die plattformübergreifend sind, gehören ins 'Allgemein'-Forum.
Justin
Beiträge: 167
Registriert: 09.09.2008 16:46

Wie Kernel Funktionen verwenden?

Beitrag 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
PB 5.11 x64 / Kubuntu 12.10 x64 | Windows 7 x64
Benutzeravatar
MVXA
Beiträge: 3823
Registriert: 11.09.2004 00:45
Wohnort: Bremen, Deutschland
Kontaktdaten:

Re: Wie Kernel Funktionen verwenden?

Beitrag 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
Zuletzt geändert von MVXA am 07.02.2013 22:25, insgesamt 1-mal geändert.
Bild
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Wie Kernel Funktionen verwenden?

Beitrag 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
Zuletzt geändert von ts-soft am 07.02.2013 22:38, insgesamt 2-mal geändert.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
MVXA
Beiträge: 3823
Registriert: 11.09.2004 00:45
Wohnort: Bremen, Deutschland
Kontaktdaten:

Re: Wie Kernel Funktionen verwenden?

Beitrag 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
Bild
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Wie Kernel Funktionen verwenden?

Beitrag 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.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Wie Kernel Funktionen verwenden?

Beitrag von NicTheQuick »

Ja, int sind immer 32 Bit, auch unter einem 64-Bit Linux.
Bild
Benutzeravatar
MVXA
Beiträge: 3823
Registriert: 11.09.2004 00:45
Wohnort: Bremen, Deutschland
Kontaktdaten:

Re: Wie Kernel Funktionen verwenden?

Beitrag 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.
Bild
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Wie Kernel Funktionen verwenden?

Beitrag 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:
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Wie Kernel Funktionen verwenden?

Beitrag 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*.
Bild
mariosk8s
Beiträge: 3
Registriert: 10.06.2011 08:16

Re: Wie Kernel Funktionen verwenden?

Beitrag 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
Antworten