Zusammenfassung zur Verarbeitung von Befehlszeilenargumenten in C/C++
In letzter Zeit, als ich den Linux-Kernel-Code durchgearbeitet habe, bin ich auf die Art und Weise gestoßen, wie der Kernel mit Modulparametern umgeht. Es war wirklich raffiniert und hat mich dazu inspiriert, zu untersuchen, wie man Befehlszeilenargumente unter C noch besser verarbeiten kann. Alle im Artikel verwendeten Codes sind hier zu finden aparsingDer Code unterstützt das Kompilieren und Ausführen unter Windows, Linux und Mac OS X. Eine ausführliche Anleitung zum Kompilieren finden Sie in der Datei README.md.
getenv
Die Standardbibliothek bietet uns eine Funktion namens getenv
. Wörtlich genommen ist diese Funktion dazu da, Umgebungsvariablen abzurufen. Wenn wir also die erforderlichen Umgebungsvariablen im Voraus einstellen und sie dann im Programm abrufen, können wir die Parameter indirekt an das Programm übergeben. Schauen wir uns nun den folgenden CodeBitte geben Sie Text ein, den Sie übersetzen möchten.
Das Kommando getenv
ist in Zeile 4Funktion, die den Wert der angegebenen Variablen zurückgibt. Gibt 0 zurück, wenn die Variable nicht gefunden wurde. 10Translate these text into German language:
和 15Die Zeile ruft die Werte von zwei Umgebungsvariablen ab, zeigt den Wert der Variablen an, wenn sie gültig sind. Beachten Sie, dass getenv
immer Zeichenketten zurückgibt, daher muss der Benutzer die Werte manuell in numerische Typen umwandeln, was die Verwendung etwas umständlich macht. Kompilieren und ausführen:
Unter Windows:
Unter Linux:
Ausgabe:
getopt
Linux provides us with a set of functions getopt, getopt_long, getopt_long_only
to handle command-line arguments passed into functions. The declarations of these three functions are as follows:
getopt
can only handle short options (i.e., single character options), while getopt_long
and getopt_long_only
can handle long options. For a detailed explanation of the functions, you can refer to the manual in Linux. Now, let's demonstrate the usage of getopt
and getopt_long
through examples.
Es ist zu beachten, dass diese Funktionen unter Windows nicht verfügbar sind. Daher habe ich eine Quellcodedatei gefunden, die unter Windows kompiliert werden kann, und habe einige kleine Anpassungen vorgenommen. Der gesamte Code befindet sich hierI'm sorry, but I can't provide a translation for non-text characters.
Wir werden uns hier hauptsächlich mit der Verwendung von getopt_long
beschäftigen. Die ersten drei Parameter von getopt_long
sind identisch mit denen von getopt
: die Anzahl der Befehlszeilenargumente argc
, das Array der Befehlszeilenargumente argv
und die spezifische Form der Kurzparameter optstring
. Das Format von optstring
besteht aus einzelnen Kurzparameter-Zeichen, gefolgt von einem Doppelpunkt :
, um anzuzeigen, dass ein Parameter erwartet wird, und zwei Doppelpunkten ::
, um einen optionalen Parameter anzugeben. Zum Beispiel in Zeile 19 wird die Form der Kurzparameter deklariert: Das Argument b
erwartet keine zusätzlichen Parameter, das Argument a
erwartet einen zusätzlichen Parameter und c
erwartet einen optionalen Parameter.
Die beiden letzten Parameter von getopt_long
dienen zur Behandlung von Langoptionen, wobei die Struktur von option
wie folgt aussieht:
struct option {
const char *name; // Long parameter name
int has_arg; // Indicates whether additional arguments are included.
int *flag; // Define how to return the result of a function call
int val; // The returned value
};
name
immer noch auf die Länge eines einzelnen Zeichens eingestellt werden.
has_arg
kann auf no_argument, required_argument, optional_argument
gesetzt werden, um jeweils keine Argumente, erforderliche Argumente oder optionale Argumente anzugeben.
flag
und val
werden gemeinsam verwendet. Wenn flag = NULL
ist, getopt_long
gibt direkt val
zurück. Andernfalls, wenn flag
ein gültiger Zeiger ist, wird getopt_long
eine Operation wie *flag = val
ausführen und die Variable, auf die flag
zeigt, auf den Wert von val
setzen.
Wenn getopt_long
eine Übereinstimmung mit einem Kurzparameter findet, wird der Zeichenwert dieses Kurzparameters zurückgegeben. Wenn ein Übereinstimmung mit einem Langparameter gefunden wird, wird val
zurückgegeben (flag = NULL
) oder 0
(flag != NULL; *flag = val;
). Wenn ein nicht-parameterbezogenes Zeichen gefunden wird, wird ?
zurückgegeben. Wenn alle Parameter abgearbeitet sind, wird -1
zurückgegeben.
Durch die Verwendung der Rückgabeeigenschaft können wir einen Effekt erzielen, bei dem lange und kurze Argumente die gleiche Bedeutung haben, zum Beispiel beim ersten Argument add
von long_options
, dessen val
-Wert auf das Zeichen des kurzen Arguments 'a'
gesetzt ist. Wenn dann überprüft wird, betreten --add
und -a
denselben Verarbeitungszweig und werden als dieselbe Bedeutung behandelt.
Das letzte Puzzlestück 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 zeigt.
Compile and run the 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'
-a
und --add
haben die gleiche Bedeutung, bei kurzen optionalen Parametern folgt dieser direkt dahinter, z.B. -c4
, während bei langen optionalen Parametern ein Gleichheitszeichen erforderlich ist, z.B. --verbose=3
.
mobuleparam
Ok, we have finally reached the method that initially sparked this article – Linux kernel cleverly utilizes a technique called moduleparam
to pass parameters to kernel modules. I will briefly explain the approach of the moduleparam
in the Linux kernel here, for a more detailed explanation you can refer to the code. While I have borrowed some processing methods from moduleparam
, there are certain differences compared to Linux kernel's moduleparam
. To distinguish, I will refer to my method as small moduleparam
, while Linux kernel's method remains as moduleparam
.
Schauen wir uns zuerst an, wie moduleparam
verwendet wird. Es wird innerhalb des Moduls deklariert:
Danach bei der Modulbeladung Eingabeparameter angeben:
Die Variable enable_debug
wird korrekt auf 1
gesetzt, was die Verwendung sehr praktisch macht. Es erfordert nur wenig zusätzlichen Code und ermöglicht es, den Code kurz und elegant zu halten, ohne viele Schleifenabfragen wie bei getenv
und getopt
schreiben zu müssen. Zudem bietet es bereits Typumwandlung. So dachte ich mir, es wäre großartig, wenn diese Methode auch zur Verarbeitung von Befehlszeilenparametern verwendet werden könnte.
Weiter geht es mit der Kernimplementierung von moduleparam
:
module_param
is a macro that basically creates a structure kernel_param
which can reflect the incoming variables, as described in lines 20-24. This structure holds the necessary information to access and set the variables and is placed within a section
called __param
(__section__ ("__param")
). Once the structure is set up, the kernel will locate the position and number of structures in the elf file's section __param
during module loading. It then proceeds to set the value for each parameter based on its name and param_set_fn
. The method to locate a specific named section
is platform-dependent. The Linux kernel handles elf file processing, and Linux provides the readelf
command to view elf file information. Those interested can refer to the readelf
command's help documentation.
Oben wurde erwähnt, dass die Vorgehensweise des Linux-Kernels plattformabhängig ist. Ich suche jedoch nach einer plattformunabhängigen Methode zur Behandlung von Parametern. Daher müssen wir die ursprüngliche Verwendung von moduleparam
anpassen, indem wir die Deklaration von __section__ ("__param")
entfernen. Immerhin möchten wir nicht aufwändig Abschnitte der elf Datei lesen. Schauen wir uns nun an, wie die Verwendung nach der Modifikation aussieht:
Um jede Reflektionsstruktur zu speichern, habe ich eine Makro namens init_module_param(num)
hinzugefügt, um den Speicherplatz für die Struktur zu deklarieren. num
ist die Anzahl der Parameter. Wenn die tatsächliche Anzahl der deklarierten Parameter größer ist als num
, löst das Programm einen Assertionsfehler aus. Die Deklaration von module_param
unterscheidet sich leicht von der Originalversion und entfernt den letzten Parameter, der den Zugriff darstellt, um die Zugriffskontrolle nicht zu berücksichtigen. Darüber hinaus wurde eine Makro namens module_param_bool
hinzugefügt, um Variablen vom Typ bool
zu behandeln. In Linux ist dies nicht erforderlich, da es die gcc-internen Funktionen __builtin_types_compatible_p
verwendet, um den Typ der Variablen zu überprüfen. Leider hat MSVC diese Funktion nicht, daher musste ich diese Funktionalität entfernen und ein Makro hinzufügen. module_param_array
und module_param_string
sind einfach die Behandlung von Arrays und Zeichenfolgen. Diese beiden Funktionen sind auch in der Originalversion vorhanden.
Nachdem die Parameter deklariert wurden, muss man sie verarbeiten. Verwende das Makro parse_params
, gebe argc, argv
an. Der dritte Parameter ist ein Zeiger auf eine Callback-Funktion zur Behandlung unbekannter Parameter. Du kannst NULL
übergeben, um die Verarbeitung der Positionsaufheberparameter zu unterbrechen und einen Fehlercode zurückzugeben.
Kompilieren und Ausführen des Codes:
.\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!
Sie können sehen, dass Zahlen, Arrays und Zeichenfolgen alle korrekt gelesen und umgewandelt werden können. Wenn ein nicht umwandelbarer Parameter auftritt, wird ein Fehlercode zurückgegeben und relevante Informationen werden gedruckt. Es ist sehr einfach, einige Zeilen Code hinzuzufügen, um das Einlesen und die Umwandlung der Parameter abzuschließen. Es lässt sich sehr elegant nutzen. Weitere detaillierte Implementierungen können direkt im Code gefunden werden, hierI am sorry, but there is nothing to translate in the text provided.
Zusammenfassung
In diesem Fall haben wir drei Methoden zur Behandlung von Befehlszeilenparametern in C/C++ zusammengefasst: getenv
, getopt
und moduleparam
. Jede Methode hat ihre eigenen Merkmale, und man kann je nach Bedarf die geeignete Methode auswählen.
getenv
is native multi-platform supported so you can use it directly, but it's too primitive and uses environment variables, causing a certain degree of pollution to the environment. It's best to clear unnecessary environment variables before each use to prevent the pollution from previous settings.
getopt
is natively supported on Linux systems, but not on Windows, so it requires the inclusion of implemented code to enable cross-platform use. The parameter passing follows the standard of command line arguments on Linux, supporting optional parameters; however, its usage is a bit cumbersome. Typically, it necessitates loops and conditional statements to handle different parameters, and it is not particularly user-friendly when it comes to numerical types of parameters.
moduleparam
is a command line parameter processing tool inspired by the moduleparam
implementation in the Linux kernel. It is cross-platform compatible, easy to use, and can perform type conversions for different types of parameters. The downside is that each parameter requires a corresponding variable for storage.
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 Beitrag wurde mit ChatGPT übersetzt. Bitte FeedbackFühren Sie alle Übersehenen aus.