Zusammenfassung zur Verarbeitung von Befehlszeilenparametern in C/C++
Vor einiger Zeit habe ich beim Durchsehen des Linux-Kernel-Codes die Verarbeitung von Modulparametern (moduleparam) im Kernel gesehen und fand sie ziemlich raffiniert. Das hat mich dazu angeregt, zu untersuchen, wie man Kommandozeilenparameter in C besser handhaben kann. Der gesamte verwendete Code ist hier zu finden aparsingDer Code kann unter Windows, Linux und Mac OS X kompiliert und ausgeführt werden. Eine detaillierte Anleitung zur Kompilierung finden Sie in der README.md.
getenv
Die Standardbibliothek stellt uns eine Funktion getenv
zur Verfügung, die wörtlich genommen dazu dient, Umgebungsvariablen abzurufen. Wenn wir die benötigten Umgebungsvariablen im Voraus festlegen, können wir sie im Programm abrufen und damit indirekt Parameter an das Programm übergeben. Schauen wir uns den folgenden Code请提供要翻译的文本。
getenv
Funktionserklärung siehe Punkt 4Gehe, übergebe den gewünschten Variablennamen und erhalte den Wert dieser Variable zurück. Wenn die Variable nicht gefunden wird, wird 0 zurückgegeben. 10和 15Die Aktion besteht darin, die Werte von zwei Umgebungsvariablen getrennt abzurufen. Wenn die Variablen gültig sind, wird der Wert der Variablen ausgegeben. Es ist zu beachten, dass getenv
nur Strings zurückgibt, weshalb der Benutzer die numerischen Typen manuell umwandeln muss, was die Handhabung etwas umständlich macht. Kompilieren und ausführen:
Unter Windows:
Unter Linux:
Translate these text into German language:
Ausgabe:
getopt
Linux bietet uns eine Reihe von Funktionen getopt, getopt_long, getopt_long_only
, um die über die Befehlszeile übergebenen Argumente zu verarbeiten. Die Deklarationen dieser drei Funktionen sind:
getopt
kann nur kurze Parameter (d.h. einzeilige Parameter) verarbeiten, während getopt_long
und getopt_long_only
lange Parameter verarbeiten können. Eine detaillierte Funktionsbeschreibung finden Sie im Handbuch unter Linux. Im Folgenden erklären wir die Verwendung von getopt
und getopt_long
anhand von Beispielen.
Es ist zu beachten, dass diese Funktion in Windows nicht angeboten wird, daher habe ich einen Quellcode gefunden, der unter Windows kompiliert werden kann, und einige kleine Änderungen vorgenommen. Der Code ist hier.
Lassen Sie uns die Verwendung von getopt_long
genauer analysieren. Die ersten drei Parameter von getopt_long
sind dieselben wie bei getopt
, nämlich: die Anzahl der Befehlszeilenargumente argc
, das Array der Befehlszeilenargumente argv
und die spezifische Form der kurzen Parameter optstring
. Das Format von optstring
besteht aus den einzelnen Zeichen der kurzen Parameter, wobei ein Doppelpunkt :
bedeutet, dass ein Parameter erforderlich ist, und zwei Doppelpunkte ::
anzeigen, dass der Parameter optional ist. Zum Beispiel in Zeile 19 wird die Form der kurzen Parameter erklärt: Der Parameter b
erfordert keine zusätzlichen Parameter, der Parameter a
benötigt zusätzliche Parameter, und c
ist ein optionaler Parameter.
Die letzten beiden Parameter von getopt_long
dienen der Verarbeitung von langen Parametern, wobei die Struktur von option
wie folgt aussieht:
struct option {
const char *name; // Long parameter name
int has_arg; // Whether an additional argument is included
int *flag; // Setzt, wie das Ergebnis des Funktionsaufrufs zurückgegeben wird
int val; // The returned value
};
name
dennoch auf eine einzeichige Länge gesetzt werden.
has_arg
kann auf no_argument, required_argument, optional_argument
gesetzt werden, was jeweils bedeutet, dass keine Argumente, erforderliche Argumente oder optionale Argumente übergeben werden.
flag
und val
werden zusammen verwendet. Wenn flag = NULL
ist, gibt getopt_long
direkt val
zurück. Andernfalls, wenn flag
ein gültiger Zeiger ist, führt getopt_long
eine ähnliche Operation wie *flag = val
aus und setzt die Variable, auf die flag
zeigt, auf den Wert von val
.
Wenn getopt_long
ein passendes kurzes Argument findet, gibt es den Zeichenwert dieses kurzen Arguments zurück. Wenn ein passendes langes Argument gefunden wird, gibt es val
zurück (wenn flag = NULL
) oder 0
(wenn flag != NULL; *flag = val;
). Wenn es auf ein Zeichen stößt, das kein Argument ist, gibt es ?
zurück. Wenn alle Argumente verarbeitet wurden, gibt es -1
zurück.
Nutzen wir die Eigenschaften eines Rückgabewerts, können wir eine vergleichbare Wirkung erzielen, indem wir lange Parameter mit kurzen Parametern gleichsetzen. Zum Beispiel wird beim ersten Parameter add
von long_options
der val
Wert auf das Zeichen 'a'
des kurzen Parameters gesetzt. Bei der Rückgabewertprüfung werden --add
und -a
in denselben Verarbeitungszweig geleitet und als gleichwertig behandelt.
Die letzte Aufgabe des Puzzles ist die Verwendung von optind
und optarg
. optind
zeigt auf die Position des nächsten zu verarbeitenden Arguments in argv
, während optarg
auf zusätzliche Argumente verweist.
Kompilieren und Ausführen von Code:
$ .\getopt_test -a 1 -b -c4 --add 2 --verbose --verbose=3 -123 -e --e
option a with value '1'
option b
option c with value '4'
option a with value '2'
option verbose
option verbose with arg 3
option 1
option 2
option 3
.\getopt_test: invalid option -- e
.\getopt_test: unrecognized option `--e'
Die Bedeutungen von -a
und --add
sind identisch. Bei kurzen Parametern folgt der optionale Parameter direkt danach, zum Beispiel -c4
, während bei langen Parametern der optionale Parameter mit einem Gleichheitszeichen verbunden sein muss, zum Beispiel --verbose=3
.
mobuleparam
Ok, finally we come to the method that originally sparked this article. The Linux kernel cleverly uses a method called moduleparam
to pass parameters to kernel modules. Let me briefly explain the practice of moduleparam
in the Linux kernel here, for a more detailed explanation you can refer to the code. Although I have borrowed some of the handling methods from moduleparam
, there are some differences compared to the Linux kernel's moduleparam
. To distinguish, I will refer to my method as small moduleparam
, while the Linux kernel's method will continue to be referred to as moduleparam
.
Lass uns zunächst die Verwendung von moduleparam
anschauen, indem wir es im Modul deklarieren:
Then enter the input parameters when loading the module:
Die Variable enable_debug
ist korrekt auf 1
gesetzt, was die Verwendung bequem macht und nur wenig zusätzlichen Code erfordert. Der Code kann kurz und elegant geschrieben werden, ohne viele Schleifenüberprüfungen wie bei getenv
und getopt
, und er hat sogar integrierte Typumwandlung. Deshalb dachte ich mir, wenn man diese Methode verwenden könnte, um Befehlszeilenargumente zu verarbeiten, wäre das noch besser.
Weiter geht es mit dem Kern der Implementierung von moduleparam
:
Die module_param
ist eine Makrofunktion, die tatsächlich ein Struktur kernel_param
erstellt, die auf die übergebene Variable reflektieren kann. Diese Struktur speichert ausreichende Informationen zum Zugriff und Setzen der Variablen, wie in den Zeilen 20-24. Die Struktur wird im section
namens __param
platziert (__section__ ("__param")
). Nachdem die Struktur erstellt wurde, sucht der Kernel beim Laden des Moduls die Position und Anzahl der Strukturen im elf-Dateibereich section __param
und setzt die Werte für jeden Parameter entsprechend Namen und param_set_fn
. Die Methode zum Auffinden einer spezifischen section
nach Namen ist plattformspezifisch. Der Linux-Kernel implementiert das Handling von elf-Dateien, und Linux bietet das Kommando readelf
an, um Informationen über elf-Dateien anzuzeigen. Interessierte können die Hilfeinformationen von readelf
einsehen.
Oben wurde erwähnt, dass die Vorgehensweise des Linux-Kernels plattformspezifisch ist. Ich suche jedoch eine plattformunabhängige Methode zur Behandlung von Parametern. Deshalb müssen wir die ursprüngliche Methode von moduleparam
etwas anpassen und die Deklaration von __section__("__param")
entfernen, schließlich wollen wir nicht unnötig kompliziert auf die Sektion
der elf-Datei zugreifen. Schauen wir uns nun die Verwendung nach der Änderung an:
Um jede reflektierte Struktur zu erhalten, habe ich ein Makro init_module_param(num)
hinzugefügt, um den Speicherplatz für die Struktur zu deklarieren. num
ist die Anzahl der Parameter. Wenn die tatsächlich deklarierte Parameteranzahl num
überschreitet, wird ein Assertionsfehler im Programm ausgelöst. Die Deklaration von module_param
unterscheidet sich leicht von der Originalversion, da der letzte Parameter, der den Zugriff darstellt, entfernt wurde und keine Zugriffskontrolle stattfindet. Darüber hinaus wurde ein Makro module_param_bool
hinzugefügt, um Variable vom Typ bool
zu behandeln. Dies ist in den Linux-Versionen nicht notwendig, da es die gcc-internen Funktionen __builtin_types_compatible_p
zur Bestimmung des Variablentyps verwendet. Leider hat MSVC diese Funktion nicht, sodass ich diese Funktion entfernen musste und ein Makro hinzufügen musste. module_param_array
und module_param_string
sind die Behandlung von Arrays und Zeichenketten. Diese beiden Funktionen waren bereits in der ursprünglichen Version vorhanden.
Nachdem die Parameter deklariert sind, geht es darum, die übergebenen Parameter zu verarbeiten. Verwenden Sie das Makro parse_params
, geben Sie argc, argv
an und als dritten Argument den Funktionszeiger für den Umgang mit unbekannten Parametern an. Sie können auch NULL
übergeben, dann wird die Verarbeitung der Parameter an der Stelle von Positionalparametern abgebrochen und ein Fehlercode zurückgegeben.
Kompilieren und Ausführen von Code:
.\moduleparam_test.exe error=0 test=101 btest=1 latest=1,2,3 strtest=\"Hello World!\"
Parsing ARGS: error=0 test=101 btest=1 latest=1,2,3 strtest="Hello World!"
find unknown param: error
test = 101
btest = Y
latest = 1,2,3
strtest = Hello World!
Es sind Werte sichtbar, Arrays und Strings können korrekt eingelesen und formatiert werden. Wenn ein nicht konvertierbares Argument auftritt, wird ein Fehlercode zurückgegeben und relevante Informationen ausgedruckt. Wir können ganz einfach ein paar Zeilen Code hinzufügen, um das Einlesen und die Umwandlung der Parameter abzuschließen, was sehr elegant ist. Eine detailliertere Implementierung kann direkt im Code angesehen werden, hierI'm sorry, but I can't provide a translation without any text to work with. Could you please provide the content you'd like me to translate into German?
Zusammenfassung
Dieses Mal haben wir drei Methoden zur Verarbeitung von Befehlszeilenparametern in C/C++ zusammengefasst: getenv
, getopt
und moduleparam
. Jede Methode hat ihre eigenen Merkmale, sodass Sie je nach Bedarf die geeignete Methode auswählen können.
getenv
ist nativ plattformübergreifend unterstützt und kann direkt verwendet werden, ist jedoch etwas rudimentär und nutzt Umgebungsvariablen, was die Umgebung in gewissem Maße belastet. Es ist ratsam, vor jeder Verwendung unnötige Umgebungsvariablen zu bereinigen, um zu verhindern, dass die Einstellungen vom letzten Mal Rückstände hinterlassen.getopt
is native to the Linux platform and not supported on Windows, so code implementation is required for cross-platform usage. The parameter passing follows the standard command line parameter format in Linux, supporting optional parameters. However, it can be slightly cumbersome to use, usually requiring loops and conditional statements to handle different parameters, and is not very user-friendly for numerical parameters.moduleparam
ist ein Befehlszeilenparameterverarbeitungstool, das an der Implementierung vonmoduleparam
im Linux-Kernel orientiert ist. Es unterstützt plattformübergreifende Nutzung, ist einfach zu bedienen und kann Typkonvertierungen für verschiedene Parameterarten durchführen. Der Nachteil besteht darin, dass jeder Parameter eine entsprechende Variable zur Speicherung benötigt.
Original: https://wiki.disenone.site/de
This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.
Visitors. Total Visits. Page Visits.
Dieser Text wurde mit ChatGPT übersetzt. Bitte geben Sie Ihr Feedbackdarauf hin, dass es irgendwelche Auslassungen gibt.