Linux entfernt die `strncpy`-API nach 6 Jahren und mehr als 360 Patches
(phoronix.com/news)- In Linux 7.2 verschwinden die Einsatzstellen der internen
strncpy-API im Kernel, womit die seit Langem zur Abschaffung vorgesehene String-Kopier-Schnittstelle endgültig entfernt wird strncpy()kopiert zwar eine vorgegebene Anzahl an Bytes, doch das Verhalten bei der NUL-Terminierung ist nicht intuitiv, weshalb die Funktion im Kernel über Jahre eine Fehlerquelle blieb- Die Eigenschaft, den Zielpuffer unnötig mit Nullen aufzufüllen, verursachte zudem Performance-Probleme; um das zu beseitigen, waren rund 6 Jahre und 362 Commits nötig
- Im Merge vom Freitag wurde nicht nur die API selbst entfernt, sondern auch die letzte architekturspezifische per-CPU-Implementierung
- Kernel-Code muss nun je nach Einsatzzweck zwischen Ersatzfunktionen wie
strscpy(),strscpy_pad(),strtomem_pad(),memcpy_and_pad()undmemcpy()wählen
strncpy verschwindet in Linux 7.2
- Linux 7.2 entfernt die seit Langem zur Abschaffung vorgesehene
strncpy-API endgültig aus dem Kernel - Nach 6 Jahren Aufräumarbeiten bleibt im Kernel-internen Code keine Nutzung der
strncpy-Schnittstelle mehr übrig - Diese Änderung ist mehr als ein bloßer Funktionstausch; sie kommt einer kernelweiten Bereinigung veralteter Praktiken beim Kopieren von Strings gleich
Umfang der Arbeiten bis zur Entfernung
- Für die Entfernung von
strncpywaren etwa 362 Commits nötig - Die Arbeiten verliefen schrittweise, indem Nutzungen von
strncpyim Kernel nach und nach beseitigt wurden - Mit Linux 7.2 erreicht diese Bereinigung nun ihren Abschluss
Warum strncpy im Kernel problematisch war
strncpygalt im Linux-Kernel über Jahre als anhaltende Fehlerquelle- Besonders zwei Verhaltensweisen waren problematisch
- Bedeutung und Verhalten der NUL-Terminierung sind nicht intuitiv, wodurch leicht Fehler entstehen
- Das redundante Auffüllen des Zielpuffers mit Nullen verursacht unnötige Performance-Kosten
Der tatsächliche Entfernen-Merge
- Der am Freitag erfolgte Merge entfernt die
strncpy-API - Im selben Merge verschwindet auch die letzte architekturspezifische per-CPU-
strncpy-Implementierung
Ersatz-APIs für Kernel-Code
- Anstelle von
strncpymuss nun je nach Ziel und Abschlussbedingung die passende Funktion gewählt werdenstrscpy(): für NUL-terminierte Zielestrscpy_pad(): für NUL-terminierte Ziele, wenn Null-Padding erforderlich iststrtomem_pad(): für nicht NUL-terminierte Felder mit fester Breitememcpy_and_pad(): für begrenzte Kopien mit explizitem Paddingmemcpy(): für Speicherkopien mit bekannter Länge
1 Kommentare
Hacker-News-Kommentare
Früher hat man sich gern darüber lustig gemacht, dass die Linux-Kernel-Entwickler, angeblich C-Entwickler auf Weltniveau, keine
stringbuffer- oderstringview-Typen bauen konnten. Damals gab es zu diesem Thema aber noch keinen Konsens wie heute, also ist das bis zu einem gewissen Grad verständlich.Dennis Ritchie war jemand, der die richtige Richtung schon gesehen hatte, und schlug 1990 einen Fat-Pointer-Typ für C vor. Wäre das in C99 aufgenommen worden, wäre es eine perfekte Ergänzung gewesen; hätte das Komitee es übernommen, hätte die Welt durchaus anders aussehen können.
2007 gab es mit Walter Brights Text „C's greatest mistake“ eine zweite Chance, in dem im Wesentlichen dieselbe Idee wie bei Ritchie, nämlich Slices/Stringviews, klarer erklärt wurde, aber auch das schaffte es nicht in C11. Jetzt sind wir bei C23, und immer noch gibt es das nicht; stattdessen haben wir
_Genericund VLA bekommen, also wirkt es fast so, als solle man einfach fröhlich weiterfeiern.Beim Suchen bin ich auch auf einen Reddit-Post zum selben Thema gestoßen, und die Fahrradschuppen-Debatte war ziemlich lustig: https://www.reddit.com/r/C_Programming/comments/90uq7c/cs_bi...
Ich frage mich, warum das Verhalten, dass C-Arrays zu Zeigern zerfallen, überhaupt so entworfen wurde. Es gibt die Erklärung, dass man damit B-Code mit minimalen Änderungen nach C kompilieren wollte; in B definierte eine Array-Deklaration offenbar tatsächlich sowohl einen Zeiger als auch ein Array, und dieser Zeiger wurde so initialisiert, dass er auf das erste Element des Arrays zeigte.
Das größere Problem heute ist, dass die C-Standardbibliothek immer noch in der K&R-Ära feststeckt und nicht einmal Sprachmerkmale wie Strukturargumente oder Rückgabewerte, die schon in C99 hinzugekommen sind, in ihren APIs widerspiegelt. Schon allein Bereichsstrukturen als Zeiger-/Größen-Paare in der Standardbibliothek plus neue oder modernisierte String-Funktionen, die diese verwenden, würden die Lage deutlich verbessern.
strncpy im Linux-Kernel sei jahrelang eine „hartnäckige Bugquelle“ gewesen – wegen kontraintuitiver Semantik, der Behandlung der NUL-Terminierung und der Performance-Probleme durch unnötiges Auffüllen des Ziels mit Nullen.
Jedes Mal, wenn ich um ein Review von C-Code gebeten wurde, habe ich nach
strncpygesucht, und dort habe ich immer Bugs gefunden.Es gibt Dinge, die mich seit 40 Jahren nerven. NUL-terminierte Strings, und inzwischen auch Nicht-UTF-8-Strings bei der Ein- und Ausgabe.
Dasselbe gilt für die Konvention, Zeilenenden als LF, CR oder CRLF zu behandeln, und für die Trennung von Feldern durch Pipes oder Kommas. Hätte man eindeutige ASCII-Zeichen wie GS, FS oder RS verwendet, wäre die Kodierung und Dekodierung von Zeilenenden ein Problem der Ein-/Ausgabe geworden, und HT/VT/CR/LF/FF hätten ganz wörtlich Code für die Ausgabe bleiben können.
Das ganze unschöne Escaping von komma-separierten Daten fiel weg, wodurch alles viel einfacher wurde.
Der Unicode-Standard sagt, dass nicht nur CR, LF, CRLF und diese Zeichen, sondern auch Vertical Tab und Form Feed als Zeilentrenner behandelt werden sollen.
Zeilenenden wie LF, CR und CRLF sind auch eine Betriebssystemkonvention, und es ist besser, wenn Programmiersprachen nicht versuchen, das „richtige“ Zeilenende zu erraten. Das schafft mehr Probleme, als es löst, und wieder einmal ist es größtenteils ein Windows-spezifisches Problem, bei dem Microsoft Windows endlich ins aktuelle Jahrhundert holen müsste.
Als ich zuletzt in bash mit CSV-Dateien arbeiten musste, habe ich sie intern zur Verarbeitung in RS und FS umgewandelt.
Statt
strncpysolle man im Linux-Kernel-Codestrscpy()für NUL-terminierte Ziele,strscpy_pad()für NUL-terminierte Ziele mit benötigtem Null-Padding,strtomem_pad()für nicht NUL-terminierte Felder fester Breite,memcpy_and_pad()für grenzbasierte Kopien mit explizitem Padding undmemcpy()für Speicher-Kopien mit bekannter Länge verwenden.Das klingt wie ein Albtraum, und ich weiß nicht, ob es wirklich so kompliziert sein muss.
Ich finde es besser, wenn beim Lesen des Codes allein durch die gewählte Funktion klar wird, was beabsichtigt ist.
strncpykorrekt zu verwenden war schon immer kompliziert.Genau in solcher langwierigen Fleißarbeit findet die eigentliche Arbeit des Systems Engineerings statt.
Solche großen Infrastrukturprojekte, die Linux über den gesamten Prozess hinweg praktisch nutzbar halten und gleichzeitig verlässlicher machen, laufen nicht über Monate, sondern über Jahrzehnte.
Aber ich bin mir nicht sicher, ob man in diesem Tempo langfristig wirklich bedeutenden Fortschritt erzielen kann. Das ist weniger eine Beschwerde als vielmehr ein Paradox kritischer Infrastruktur.
Eine großartige Arbeit, die einen auch demütig macht. Erstaunlich, dass so viele Menschen dazu beigetragen haben
„Tolle neue Features“ bekommen leicht Anerkennung, aber bei etwas so Grundlegendem wie dem Kernel kann es sogar wichtiger sein, schlechte Funktionen zu entfernen
Wenn in 50 Jahren die Menschen vergessen haben, wie man Quellcode liest, während sich Claude-/Codex-Abfälle still auftürmen und den Großteil der Energie der Erde verbrennen, werden solche Arbeiten wohl wie Legenden aus dem „Gründungszeitalter“ wirken
und ist außerdem die einzige Person, die weiß, was die Unix-Epoche ist
Ich halte nullterminierte Strings für den größten Fehler der Computergeschichte. Pascal-Strings waren viel sicherer
Es ist immer noch ein Pointer auf ein nullterminiertes Zeichenarray, aber direkt vor dem ersten Byte, auf das der Pointer zeigt, steht ein Längenfeld. Unter der Annahme, dass keine eingebetteten NUL-Zeichen vorkommen, ist das auch mit C-Strings kompatibel, und Funktionen für den Typ BSTR können den Längenwert nutzen
Eine Zeit lang hätten sich vielleicht sogar 16 Bit als übertrieben angefühlt, und heute könnten 32 Bit zu klein wirken. C gilt als Sprache mit „starker Typisierung“, ist aber ausgerechnet an wichtigen Stellen ziemlich lax
Ich habe seit über 30 Jahren keinen Pascal-bezogenen Code mehr geschrieben, aber ich erinnere mich dunkel, dass ich selbst damals das String-System für zu umständlich hielt
Es gibt so viel Schmerz und vergeudete Mühe nur deshalb, weil es keinen String-Datentyp gibt
strncpygroßflächig refaktorieren, damit er diesen Typ und die zugehörigen Funktionen verwendetIch frage mich, was daran so schwierig war, die Verwendungen von strncpy umzuschreiben, dass es 6 Jahre gedauert hat
Ob es einfach so weit verbreitet war, ob man es nur im Rahmen einer langfristigen Arbeit ersetzt hat, wenn dieselben Dateien ohnehin angefasst wurden, oder ob es noch andere Schwierigkeiten gab, würde mich interessieren
Ich musste einmal Code in einer Win32-App betreuen, der mit Leerzeichen aufgefüllte Strings verwendete. Der Ziel-String wurde mit Leerzeichen aufgefüllt, aber im letzten Byte stand trotzdem noch ein Nullzeichen
Für Dinge wie Länge und Kopieren musste man spezielle Versionen der String-Funktionen verwenden. Warum das so war, weiß ich nicht, aber die Codebasis war so alt, dass es auch aus dem Verhalten von Pascal-Structs stammen könnte