Arch Linux Planet

PulseAudio Mini EQ

09.02.2020
PulseAudio Mini EQ

Meine derzeitige Wohnung hat ’ne schlechte Akustik. Harte Wände, harter Boden, kein Teppich. Teure Lautsprecher lohnen sich nicht und nachdem mein altes Setup schließlich kaputt ging, habe ich einfach zwei ganz billige Speaker gekauft:

main speakers

Die sind gut genug für ein bisschen Hintergrundmusik oder Podcasts.

Aber … der Raum hat natürlich immer noch eine schlechte Akustik und jetzt sind auch noch die Lautsprecher schlecht. Äh. Ja.

Ich könnte natürlich Akustikelemente kaufen, Absorber oder Diffusoren. Bin ich aber zu faul für.

Mir ist dann kürzlich aufgefallen, dass ich die gefühlte Qualität stark verbessern kann, indem ich die Mitten reduziere. Die Audio-Nerds hassen mich dafür jetzt, aber ganz ehrlich: Ich mag den typischen HiFi-Sound, dem die Mitten fehlen. Ich finde Mitten einfach nicht besonders angenehm.

Okay, wie wende ich den EQ denn an? Man ist heute ja ohnehin quasi gezwungen, PulseAudio zu verwenden, also kann ich es auch damit machen.

Zuerst habe ich es mit PulseEffects probiert, was aber viel zu overpowered war. Dann pulseaudio-equalizer-ladspa probiert, was viel leichtgewichtiger ist. Hat auch ganz gut funktioniert, aber hat auch seine Problemchen. Zum Beispiel nimmt es an, dass man nur eine Soundkarte hat. Ich habe zwei. Eine dedizierte USB-Soundkarte mit einem Paar dedizierter Lautsprecher im PC-Gehäuse spielt ständig ratterplatter ab. Ja, sorry, mag ich halt. :-)

USB soundcard speakers inside case

pulseaudio-equalizer-ladspa hat dann ratterplatter ständig auf das falsche Output-Device geschoben.

Angeschaut, wie es funktioniert, und natürlich ist es gar nicht so kompliziert. Es benutzt das in PA bereits eingebaute Modul module-ladspa-sink, um Audio durch irgendeinen LADSPA-EQ zu routen und das war’s.

Alles klar, einfach neugeschrieben und nun habe ich PulseAudio Mini EQ. Benutzt denselben internen Mechanismus, hat aber ein anderes UI (war mir wichtig, dass ich es schnell an- und ausschalten kann – wenn ich Kopfhörer aufhabe, will ich natürlich keinen EQ) und verschiebt nur jene PA-Clients auf das virtuelle LADSPA-Device, die derzeit auf der „Hauptsoundkarte“ spielen. ratterplatter tut das nicht und wird daher nicht angefasst.

Mal gucken, wie lange ich das so benutze.

screenshot

vain
Wie C ein struct aus einer Funktion zurückgibt

In C kann man neben einfachen Dingen wie int oder char * auch ganze structs zurückgeben:

struct Foo
{
    char name[13];
    int age;
};

struct Foo
fill(void)
{
    struct Foo out;

    ...

    return out;
}

Fand ich immer etwas eigenartig, weil man Arrays nicht returnen kann – außer natürlich, sie sind wie oben in einem struct versteckt. Naja, nicht abschweifen.

Die Frage ist, was unter der Haube passiert, wenn man ein struct returned. Gemäß cdecl wird sowas wie ein int in einem Register zurückgegeben:

https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl

Ein ganzes struct passt da meistens nicht rein. Der Wikipedia-Artikel gibt auf meine Frage eigentlich auch schon die Antwort, aber, faul wie ich bin, habe ich beim ersten Mal nicht so weit gelesen. :-) Stattdessen ein kleines Experiment gemacht:

#include <stdio.h>

struct Foo
{
    char name[13];
    int age;
};

struct Foo
fill(char this, char that, char whatever)
{
    struct Foo out;

    out.name[0] = this;
    out.name[1] = that;
    out.name[2] = whatever;
    out.name[3] = 0;
    out.age = 32;

    asm("# fill a");
    return out;
    asm("# fill b");
}

int
main()
{
    struct Foo joe;
    asm("# a");
    joe = fill('J', 'o', 'e');
    asm("# b");
    printf("[%s] %d\n", joe.name, joe.age);
    return 0;
}

Den Code mit ein bisschen Inline-Assembler gespickt und dann das Ergebnis angeschaut:

clang -O0 -Wall -Wextra -o bla.S -S bla.c

Man sieht diesen Abschnitt:

#APP
# a
#NO_APP
leaq    -56(%rbp), %rdi
movl    $74, %esi
movl    $111, %edx
movl    $101, %ecx
callq   fill
movl    -40(%rbp), %ecx
movl    %ecx, -16(%rbp)
movups  -56(%rbp), %xmm0
movaps  %xmm0, -32(%rbp)
#APP
# b
#NO_APP

Zur Erinnerung: rdi, rsi, rdx und rcx sind die ersten vier Register, um Parameter an die Funktion zu übergeben. Mein fill() hat aber auf Ebene des C-Codes nur drei Parameter, also … hat der Compiler offensichtlich einen zusätzlichen dort versteckt: leaq -56(%rbp), %rdi, einen Pointer auf den lokalen Stack der aufrufenden Funktion. Nachdem fill() zurückkehrt, werden Daten von dort gelesen und nach -32(%rbp) kopiert, einer anderen Stelle auf dem lokalen Stack.

Und das ist die Antwort. Der C-Compiler fügt einen versteckten Parameter ein und die aufgerufene Funktion kann dann dorthin schreiben.

Interessanterweise benutzt clang hier mit xmm0 eines der SSE-Register für diesen Kopiervorgang. Bei gcc sieht das so aus:

#APP
# 29 "bla.c" 1
    # a
# 0 "" 2
#NO_APP
    leaq    -64(%rbp), %rax
    movl    $101, %ecx
    movl    $111, %edx
    movl    $74, %esi
    movq    %rax, %rdi
    call    fill
    movq    -64(%rbp), %rax
    movq    -56(%rbp), %rdx
    movq    %rax, -32(%rbp)
    movq    %rdx, -24(%rbp)
    movl    -48(%rbp), %eax
    movl    %eax, -16(%rbp)
#APP
# 31 "bla.c" 1
    # b
# 0 "" 2
#NO_APP

Ein paar Instruktionen mehr. Ist jetzt kein bedeutungsschwangerer Vergleich, weil wir eh -O0 benutzt haben, aber trotzdem interessant.

Noch eine Randbemerkung zu den Arrays: Ich sehe nicht so richtig einen Grund, weshalb es – in der Theorie – nicht möglich sein sollte, Arrays fester Länge zu übergeben. Immerhin geht’s ja, wenn sie in einem struct sind, weil die Größen eindeutig definiert sind. In einer kleinen Mathe-Bibliothek, die ich mal geschrieben habe, benutze ich das sehr häufig:

struct mat4 {
    GLfloat v[16];
};

struct mat4
mat4_identity(void)
{
    struct mat4 m = {0};

    m.v[0] = 1;
    m.v[5] = 1;
    m.v[10] = 1;
    m.v[15] = 1;

    return m;
}

Damit kann man dann irgendwo im Code das Folgende schreiben, was in meinen Augen die Lesbarkeit gegenüber der manuellen Benutzung von Pointern deutlich erhöht:

void
something(void)
{
    struct mat4 a;

    a = mat4_identity();
}

Oder gar:

void
something(void)
{
    struct vec3 r, t;
    struct mat4 a;
    struct quat4 q;

    a = quaternion_to_rotational_matrix(q);
    r = mat4_mult_vec3(a, t);
}
vain

Das von Arch Linux mitgeliefert rsync hat bislang, entgegen einem sonst verfolgen Prinzip, eine in der Codebasis von rsync mitgelieferte und dort mehr schlecht als recht gewartete angepasste Version der zlib verwendet, um so die Komaptibilität zu mit der Option --compress erstellten Repos zu wahren. Diese Option hat es bis zu rsync Version 3.1.0 gegeben. Die Nachfolgeversion rsync 3.1.1 ist nun auch schon fünfeinhalb Jahre alt.

Die Arch-Entwickler haben sich daher entschieden, des Option nicht mehr zu unterstützen. Wer eine Synchronisation von Verzeicnissen machen will, die mit --compress erstellt wurden, wird also Probleme bekommen und sollte rsync nicht aktualisieren.

Zwar liegen hier seit Monaten mehrere fertige Artikel technischer Natur auf der Platte, nur kann ich davon noch nichts veröffentlichen, daher zu Abwechslung mal etwas aus dem Unterhaltungssektor.

Vor einigen Tagen habe ich mir X4: Foundations gekauft, da der Einstieg im ersten Szenario etwas anstrengend ist stelle ich nun als …

Thorsten Töpper
Ein paar Gedanken zu for, goto, Lesbarkeit

nullprogram.com ist eines der besten Blogs, die ich kenne. Quasi jeder Artikel ist interessant und ich lerne etwas neues.

Heute hat er einen Artikel zu purgeable memory veröffentlicht. Kurze Zusammenfassung des Konzepts: Es geht um Speicher, der vom Kernel freigegeben (nicht geswappt!) werden kann, falls es die globale Arbeitslast im System erfordert. Der eigene Code muss damit natürlich umgehen können.

Die Idee dahinter ist: Eine der Berechnungen, die der Code durchführt, dauert eine Weile und nimmt spürbar Speicher in Anspruch. Das Ergebnis will man mehrfach verwenden, zwischen den Verwendungen liegen aber größere zeitliche Pausen. Außerdem ist es schneller, die Berechnung einfach nochmal durchzuführen, als auf den Kernel zu warten, bis der irgendwas aus dem Swap wieder rausgeholt hat.

Als Beispiel gibt es diesen Codeschnipsel:

/* Version 0: Original code */

uint32_t *texture = 0;
struct png *png = png_load("texture.png");
if (!png) die();

/* ... */

for (;;) {
    if (!texture) {
        texture = purgeable_alloc(png->width * png->height * 4);
        if (!texture) die();
        png_decode_rgba(png, texture);
    } else if (!purgeable_lock(texture)) {
        purgeable_free(texture);
        texture = 0;
        continue;
    }
    glTexImage2D(
        GL_TEXTURE_2D, 0,
        GL_RGBA, png->width, png->height, 0,
        GL_RGBA, GL_UNSIGNED_BYTE, texture
    );
    purgeable_unlock(texture);
    break;
}

Eine Textur wird geladen und in einem OpenGL-Kontext verwendet. texture ist also eine langlebige Variable. Man denke beispielsweise an ein Menü in einem Spiel: Das Menü wird nur ab und zu mal aufgerufen und zeigt immer dasselbe Hintergrundbild – unsere texture. Das Menü soll sich schnell öffnen, also würde man die nötigen Daten gerne im Speicher behalten. Auf der anderen Seite: Wenn das Menü nur selten aufgerufen wird, muss das dann wirklich im Speicher bleiben? Wäre nett, zwingend notwendig ist es aber eben nicht. Also könnte man purgeable memory dafür verwenden.

Mir geht es jetzt um die for-Schleife da oben.

Die hat mich am Anfang ziemlich verwirrt. Warum ist da eine Schleife? Warum ist sie endlos? Müssen wir da vielleicht etwas mehrfach probieren, weil es fehlschlagen könnte? Einen Moment später sind mir dann continue und break aufgefallen und dann hat’s geklingelt.

Was eigentlich passiert, ist das (Pseudocode):

if (texture not loaded)
    load texture;
else /* texture was once loaded */
    if (kernel has purged the memory in the meantime)
        mark texture as "not loaded";
        TRY AGAIN FROM THE TOP;

use texture in OpenGL;
mark texture memory as purgeable;

Mit anderen Worten, die for-Schleife wird benutzt, um ein goto zu vermeiden.

Ein Vorteil dabei ist, dass die geschweiften Klammern unmissverständlich klarmachen, wo dieser Abschnitt beginnt und endet. Es kann keine unerwarteten Sprünge von woanders im Code geben, also wird eine der größten Fallen von goto ausgeschlossen.

Auf der anderen Seite habe ich den Eindruck, dass dieses Konstrukt nicht unbedingt „besseren“ Code erzeugt. Man muss es zweimal lesen und es ist auch gar keine Schleife. Es ist eher ein „probier’s nochmal“-Mechanismus.

Wenn man’s mal verstanden hat, ist es trivial. Es ist aber eines dieser Dinge, die so eine kleine „Barriere“ erzeugen. Wenn solche Tricks an sehr vielen Stellen benutzt werden, wird der Code unzugänglicher und es kann auch recht schwer für neue Leute im Team werden. Ich bin da natürlich keine Ausnahme und mache sowas auch. Klar, wenn man Code selbst schreibt, versteht man immer alles und alles ist trivial. Man vergisst zu leicht, dass man selbst auch irgendwann wieder „neu im Team“ ist, einfach weil man nach ein paar Jahren, in denen man den Code nicht angefasst hat, diese Tricks auch alle wieder vergessen hat. Das ist jetzt keine neue Erkenntnis, aber sie gerät eben allzu leicht in Vergessenheit.

Mit einem goto sähe es so aus:

/* Version 1: goto */

load_texture:
    if (!texture) {
        texture = purgeable_alloc(png->width * png->height * 4);
        if (!texture) die();
        png_decode_rgba(png, texture);
    } else if (!purgeable_lock(texture)) {
        purgeable_free(texture);
        texture = 0;
        goto load_texture;
    }
    glTexImage2D(
        GL_TEXTURE_2D, 0,
        GL_RGBA, png->width, png->height, 0,
        GL_RGBA, GL_UNSIGNED_BYTE, texture
    );
    purgeable_unlock(texture);

Ist das besser? Ich denke schon. Es gibt keine Verwirrung über eine Schleife mehr. Die unzutreffenden Vokabeln continue und break sind weg. Man liest das Label zuerst, ist also gewahr, dass da ein goto kommen wird. Label und goto sind auch recht nah beieinander. In meinen Augen ist das durchaus besser verständlich.

Ich benutze goto ab und zu, bin aber immer vorsichtig. Es muss einen guten Grund dafür geben. Es muss dabei helfen, Code zu schreiben, der besser verständlich ist. In diesem Fall hier wäre das in meinen Augen gegeben.

Die offensichtliche Frage ist aber, ob wir den Sprung überhaupt brauchen. Wie wäre es damit:

/* Version 2: Restructured code */

if (texture && !purgeable_lock(texture)) {
    purgeable_free(texture);
    texture = 0;
}

if (!texture) {
    texture = purgeable_alloc(png->width * png->height * 4);
    if (!texture) die();
    png_decode_rgba(png, texture);
}
glTexImage2D(
    GL_TEXTURE_2D, 0,
    GL_RGBA, png->width, png->height, 0,
    GL_RGBA, GL_UNSIGNED_BYTE, texture
);
purgeable_unlock(texture);

Der Code wurde umstrukturiert und es gibt jetzt kein „probier’s nochmal“ mehr. Der Pseudocode dazu sieht so aus:

if (kernel has purged our memory)
    reset pointer;

if (texture not loaded)
    load texture;
use texture in OpenGL;
mark texture memory as purgeable;

Ein bisschen hässlich ist, dass der Check für texture == NULL dupliziert ist. Es ist außerdem wichtig, dass man es nicht als else if schreibt, weswegen ich die leere Zeile drinhabe.

Geht es noch prägnanter? Was wir ja eigentlich machen wollen, sieht so aus:

if (texture not available)
    load texture;
use texture in OpenGL;
mark texture memory as purgeable;

Es gibt keine Schleife, keinen Sprung, nichts. Die Absicht ist klar formuliert. Kriegen wir das hin? Im Moment nicht. Nehmen wir aber mal an, die API der Bibliothek würde sich leicht ändern:

  • NULL-Pointer unterstützen: purgeable_lock() würde einfach False oder 0 zurückgeben.
  • Bei fehlgeschlagenen Locking-Versuchen automatisch den Speicher freigeben (wie im ursprünglichen Artikel vorgeschlagen).

Dann könnte man das machen:

/* Version 3: Altered API */

if (!purgeable_lock(texture)) {
    texture = purgeable_alloc(png->width * png->height * 4);
    if (!texture) die();
    png_decode_rgba(png, texture);
}
glTexImage2D(
    GL_TEXTURE_2D, 0,
    GL_RGBA, png->width, png->height, 0,
    GL_RGBA, GL_UNSIGNED_BYTE, texture
);
purgeable_unlock(texture);

Das ist die knappste und prägnanteste Variante, die ich finden konnte.

Auf der anderen Seite ist das wieder eine Grauzone. In Variante 3 muss man nämlich mehr mitdenken:

  • Falls texture == NULL ist, tut die Bibliothek nichts und returned False. Wir müssen jetzt das PNG decoden.
  • Falls texture != NULL ist:
    • Locking hat geklappt? Alles gut, war ja schon decoded.
    • Locking fehlschlagen? Dann müssen wir das PNG erneut decoden.

Je älter ich werde, desto mehr stört mich sowas. Es ist nur ein Funktionsaufruf, ich muss aber mehrere Situationen und Codepfade im Kopf durchsimulieren. Wenn man nicht aufpasst, vergisst man den Fall mit NULL vielleicht sogar. Selbst, wenn wir die Bibliothek also wie oben geändert hätten, würde ich den Code eher so schreiben:

/* Version 4: Altered API and explicit check */

if (!texture || !purgeable_lock(texture)) {
    texture = purgeable_alloc(png->width * png->height * 4);
    if (!texture) die();
    png_decode_rgba(png, texture);
}
glTexImage2D(
    GL_TEXTURE_2D, 0,
    GL_RGBA, png->width, png->height, 0,
    GL_RGBA, GL_UNSIGNED_BYTE, texture
);
purgeable_unlock(texture);

Die Prüfung auf texture == NULL ist dabei nicht zwingend notwendig, weil das ja auch die Bibliothek für uns macht. Ich glaube trotzdem, dass es sinnvoll ist, das explizit auszuschreiben, weil der Leser jetzt sofort weiß, dass es bei diesem if um zwei verschiedene Fälle geht: Das PNG wird decoded, wenn es das allererste Mal ist oder falls der Speicher weggeworfen wurde.

Als Schlussbemerkung: Ich schreibe lieber texture == NULL statt !texture, aus genau denselben Gründen. Es ist ein Pointer und wir prüfen, ob er NULL ist. Länger, aber dafür glasklar.

vain

Im Zuge einer Aufräumaktion unter den Xorg-Paketen kann es zu Fehlermeldungen kommen:

:: installing xorgproto (2019.2-2) breaks dependency 'inputproto' required by lib32-libxi
:: installing xorgproto (2019.2-2) breaks dependency 'dmxproto' required by libdmx
:: installing xorgproto (2019.2-2) breaks dependency 'xf86dgaproto' required by libxxf86dga

Dann muss manuell eingegriffen werden:

pacman -Rdd libdmx libxxf86dga && pacman -Syu

Den primus_vk-Paketen vor Version 1.3-1 fehlten enige soname-Verlinkungen. Die ist in 1.3-1 korrigiert worden. Dadurch müssen schon vorhandene Dateien, die zu keinem Paket gehören, und somit durch pacman nicht getrackt werden, überschrieben werden.

Wenn Ihr also eine Fehlermeldung folgender Art bekommt:

primus_vk: /usr/lib/libnv_vulkan_wrapper.so.1 existiert im Filesystem
primus_vk: /usr/lib/libprimus_vk.so.1 existiert im Filesystem

so ist die --overwrite-Option von Pacman zu verwenden,

pacman -Syu --overwrite=/usr/lib/libnv_vulkan_wrapper.so.1,/usr/lib/libprimus_vk.so.1

, oder die den Konflikt auslösenden Dateien sind manuell zu löschen.

Alle offiziellen Kernel-Pakete (linux, linux-lts, linux-zen and linux-hardened) installieren von sich aus keine eigentlichen Kernel mehr nach /boot.

Dieser Installationsschritt wird über mkinitcpio-hooks erledigt. Normalerweise sollte hier kein manuelles Eingreifen nötig sein. Ausnahme sind unter Umständen Neuinstallationen.

Dahinter steckt das Bestreben, die Kernelpakete in sich geschlossener zu halten und den Boot-Prozess flexibler gestaltbar zu halten.

Zur Zeit hat mkinitcpio Hooks zum Hinzufügen der Kernel von und des Löschens aus /boot. In naher Zukunft wir auch dracut solche Hooks besitzen und mkinitcpio nach und nach ersetzen.

Der Kompressionsalgorithmus zstd bietet gegenüber xz schnellere Kompression und Dekompression, ohne bei der Kompressionsrate gegenüber xz deutlich zurückzufallen. Die Einführung wird Paketinstallationen mit pacman also ohne spürbare Nachteile schneller machen.

Das kürzlich herausgebrachte Release 5.2 bringt alle Wrkzeuge mit sich, um Pakete mit zstd zu komprimieren. Dazu wird eine Version von libarchive benötigt, die Unterstützung für zstd bietet. Diese ist 3.3.1, und sie ist
vor über einem Jahr (September 2018) in die Repos gekommen. Wer also seit einem Jahr kein Update mehr gemacht hat, sollte dies schleunigst tun.

Auch Benutzerskripte, in der die Dateierweiterung pkg.tar.xz irgendwie hardkodiert ist, sollten angepasst werden - oder besser so umgeschrieben werden, dass die Dateiendung egal ist. Die neue Endung wird pkg.tar.zst sein.

Die Gruppe base wurde durch ein gleichnamiges Metapaket ersetzt.
Alle Benutzer sind angehalten, dieses Paket mittels

pacman -Syu base

zu installieren.
Systeme ohne dieses Paket werden im offiziellen Forum nicht mehr unterstützt.

Mein Weg zu Arch

Ich möchte euch hier mal meinen Weg zu Archlinux darlegen. Vielleicht findet es der ein oder andere ja ein wenig Interessant.

Bis vor 2 Monaten habe ich meine Linuxserver unter Proxmox mit Debian betrieben. Einen kleinen Teil lief und läuft immer noch auf einem Gentoo (CS1.6 und CSS Dedicated Server).
Ich habe unter Debian beispielweise Samba als PDC betrieben, NFS-Server, Apache2, MySQL uvm.. Man glaubt nicht wieviel man irgendwann am Laufen hat, wenn man gut zurecht kommt.

Nun bekam ich aber das erste mal mit Debian Probleme. Benutzte Debian schon seit Version 3. Ich wollte einen Versionsupgrade machen von Version 9.9 auf Version 10.
Das lief einigermaßen gut, einigermaßen. Stuzig wurde ich erst, als ich wie gewohnt per RSYNC meine Daten auf meinen Backupsystem übertragen wollte, merkte ich, dass das Tool rsync einfach weg war.
Hmmm ok. Kann ja mal passieren, dachte ich mir. Nachinstalliert und gut dachte ich.
Als ich aber dann merkte, dass mein Mailserver (Postfix, Dovecot, Amavisd-NEW, Spamassassin, ClamAV) auch nicht mehr seinen Dienst verrichtete, dachte ich mir warum hat mich das System nicht über Änderungen der Configfiles benachrichtigt, wie immer?
Da war ich dann doch soweit. Ich habe das erste mal drüber nachgedacht über einen Wechsel. Aber es auch gleich wieder verworfen.
Nun waren noch 3 PCs dran und noch 4 Laptops. Ich mache es kurz und knapp. Bei jedem dieser Systeme hat der Versionsupgrade überhaupt nicht funktioniert. Ich war mehr als verdutzt. Regelrecht frustriert. Ich wollte es nicht glauben.
Was war nur los mit meinem geliebten Debian? Das kenne ich so nicht. Ich bin vorgegangen wie immer. Alles lief schief. Ich hatte nie vorher Probleme mir Versionsupgrades. Und jetzt bei jedem System. Oh mein Gott. Jetzt habe ich echt Arbeit vor mir.

Jetzt habe ich Recherche betrieben. Ich habe mich sehr schnell dazu entschieden: Ich brauche ein Rolling Release Linux...
Klar, dachte ich als erstes an mein hochgeschätztes Gentoo. Aber Aufgrund der doch relativ großen Anzahl der Pakete, die ich für meinen Hauptserver brauche schnell wieder verworfen. Läuft ja nicht auf Xeons in meinem Fall.
Nur, wenn ich nichts anderes finde wäre ich auf Gentoo gegangen und hätte Compiliert bis die Schwarte kracht. Ich finde Gentoo megatoll. Optimieren, einschränken, einfach tolle Distri. Aber auf einem C2750 virtuell 4vCPUs, neee, eher nicht. Außerdem habe ich ja ein Gentoo laufen, wie vorher schon erwähnt.
Ich mache es nun kurz. Alle möglichen RollingRelease Linuxe virtuell mal angetestet. Aber... Die einzige "andere" RollingRelease-Distri, die in meinen Augen für mich funktioniert mir auch gefällt war ARCHLINUX. Die Entscheidung war gefallen.

Also neue virtuelle Hülle angelegt und begonnen:
Das tolle Wiki von Archlinux genutzt und die Installation begonnen. Jaaaa, ich bin natürlich voll in die erste Hürde ungebremst reingerannt.
Ich will mein System mit LVM2 und ich will /usr als eigene Partition. Der ein oder andere kann es sich schon denken. Ich habe nach der Installation eine Neustart gemacht und das System ist gecrashed. Denn es konnte weder mit LVM2 etwas anfangen, noch /usr finden.
Man kann natürlich darüber streiten, ob das nun so sein muss oder nicht. Aber ich will es so, mein System, meine Regeln. Ich will LVM2, /usr ist eine eigene Partition.
Wieder Recherche und irgendwie habe ich es einfach nicht hinbekommen. Ich war irgendwie zu blöd, da irgendwas zu finden. Also habe ich mir das System im CHROOT genauer angesehen. Nach einer gewissen Zeit habe ich dann etwas gefunden.
Im nachhinein war es total klar. Ich musste dem System/Kernel ja sagen, dass ich /usr als eigene Partition betreibe und auch darauf hinweisen, dass ich LVM2 nutze.
Jetzt lief es:
Hier nun mein Aufbau der HDD:

Dateisystem                      Typ      Größe Benutzt Verf. Verw% Eingehängt auf
dev                              devtmpfs  4,2G       0  4,2G    0% /dev
run                              tmpfs     4,2G    799k  4,2G    1% /run
/dev/sda6                        btrfs      33G     95M   30G    1% /
/dev/mapper/vg_arch-lv_usr       btrfs      54G    2,9G   49G    6% /usr
tmpfs                            tmpfs     4,2G     13k  4,2G    1% /dev/shm
tmpfs                            tmpfs     4,2G       0  4,2G    0% /sys/fs/cgroup
/dev/sda1                        ext2      723M     75M  641M   11% /boot
/dev/mapper/vg_arch-lv_var       ext4       32G    4,2G   28G   14% /var
/dev/mapper/vg_arch-lv_root      ext4       22G     47M   21G    1% /root
/dev/mapper/vg_arch-lv_home      ext4       43G    113M   42G    1% /home
/dev/mapper/vg_arch-lv_tmp       ext4       27G    122M   26G    1% /tmp
/dev/mapper/vg_arch-lv_usr_share ext4       53G    2,6G   50G    5% /usr/share
/dev/mapper/vg_arch-lv_srv       ext4       43G    342M   42G    1% /srv
/dev/mapper/vg_arch-lv_var_log   ext4       32G    1,6G   30G    6% /var/log
/dev/mapper/vg_data-lv_smb2      ext4      2,2T    206G  2,0T   10% /smb2
/dev/mapper/vg_arch-lv_backup    xfs        89G     13G   76G   15% /backup
/dev/mapper/vg_data-lv_smb1      xfs       1,6G     36M  1,6G    3% /smb1
/dev/mapper/vg_arch-lv_var_tmp   btrfs      27G     18M   25G    1% /var/tmp
/dev/mapper/vg_arch-lv_opt       btrfs      38G    211M   36G    1% /opt
/dev/mapper/vg_arch-lv_usr_local jfs        33G    233M   32G    1% /usr/local
/dev/mapper/vg_data-lv_smb3      btrfs      11T    5,0T  5,7T   47% /smb3

Die meisten Serverdienste habe ich ohne jeglichen Probleme zum Laufen bekommen. Horde/IMP war nicht ein Problem, welches durch ARCH bekommen habe, sondern durch die Pakete selbst. Ich musste es durch pear installieren. Beim Debian gab es in den Repos Pakete. War dadurch bei Debian einfacher, aber ich habe es ja geschafft.
Nachdem ich so ungefährt 2 Wochen mit ARCH gearbeitet habe, habe ich mich auch dafür entschieden es auf den 3 PCs und den 4 Laptops zu installieren. Bei einem System hatte ich dann Probleme mit der Deutschen Lokalisierung auf der Console.
Naja, mein Fehler. Mir wurde hier im Forum super geholfen. An dieser Stelle noch mal vielen Dank für jegliche Hilfe, die ich da bekommen habe. Auch, wenn meine Fragen mal nicht so dolle waren.
Meine Hauptdistri ist nun ARCH. Ich habe aber dennoch andere Distributionen am Laufen. Mein Jumpserver läuft unter FreeBSD (komme ich super zurecht mit), Gentoo für die Spieleserver, CentOS für mein Gitlab mit SELinux, und ein virtuelles Debian, wo ich beobachten will wie es da weiter geht.

Kurzum: Ich mag Debian immer noch. Ich hoffe es zumindest auf meinem Urarltlaptop (Thinkpad T41p 32Bit Pentium M) mal wieder sinnvoll nutzen zu können. Jetzt läuft erstmal Archlinux32 auf dem Uraltlaptop.

Ich bin auf jeden Fall echt froh, dass es Archlinux gibt. :-)

Aufgrund eines Paketierungsfehlers in Versionen vor 1.6-2 erfordert eine Aktualisierung des Paketes libbloom unter Umständen einen manuellen Eingriff. Hintergrund ist, dass in älteren Versionen ein symbolischer Link nicht enthalten war, der nun ab 1.6-2  enthalten ist, was einen Dateikonflikt auslöst.

Wird die Datei

/usr/lib/libbloom.so.1

also als schon existierend angemeckert, so sollte sie gelöscht oder mittels pacman mit der --overwrite Option überschrieben werden.

Die Aktualisierung von mariadb auf 10.4.6-1 oder nachfolgende Versionen bringt die Notwendigkeit mit sich, manuell einzugreifen. Arch Linux übernimmt ein neues Ablageortesystem vom MariaDB-Projekt.

Dir Haupt-Konfigurationsdate wurde von

 /etc/mysql/my.cnf 

nach

 /etc/my.cnf 

verschoben. Die nachgelagerten Konfigurationsdateien wurden folglich analog von

 /etc/mysql/my.cnf.d/

nach

/etc/my.cnf.d

verschoben. Hat man hier Anpassungen vorgenommen, müssen die Dateien und Verzeichnisse entsprechend verschoben werden.

Instanziierte Dienste werden nicht mehr in seperaten Dateien konfiguriert, sondern in der Hauptkonfigurationsdatei.                       

Wie immer bei einer Aktualisierung bei mariadb ist die DB anschließen zu stoppen und neu zu starten.

systemctl restart mariadb.service && mariadb-upgrade -u root -p
grep.asm – Schritt 3: Reguläre Ausdrücke erkennen

Voriges Posting dieser Serie.

Okay, was ist hier los, warum dauert das so lange? Hat viele Gründe, zwei davon sind besonders ausschlaggebend.

Der erste ist RSI. Der menschliche Körper mag es wohl nicht so sehr, 8 bis 16 Stunden pro Tag Tastatur und Maus zu bedienen, tagein, tagaus, für ein paar Jahrzehnte, viel davon unter Stress – und dann noch ein bisschen was oben drauf. Eines Tages waren da also Schmerzen in den Händen und die gingen nicht mehr weg. Vorboten habe ich zumindest keine wahrgenommen. Meh. Der Heilprozess ist noch im Gange und diesen Text schreibe ich mit einem albernen Workaround im Schneckentempo, aber es ist besser als nichts. Vielleicht irgendwann später mal mehr zu diesem Thema.

Das ist natürlich auch der Grund, weshalb dieses Posting kürzer und oberflächlicher ist, als mir lieb wäre.

Der andere Grund ist schlichtweg Motivation. Ich habe dieses Projekt (teilweise) angefangen, weil die Komplexität und die Menge an Abstraktion heutiger Software ermüdend sind. In Assembler zu programmieren, führt mich ein bisschen näher an „die Maschine“ heran. Man bekommt hier noch am ehesten mit, was eigentlich passiert. (Dabei ist mir durchaus bewusst, dass es Dinge wie Register Renaming gibt und alles ist ohnehin superskalar und out-of-order, also herrscht selbst auf ASM-Ebene Lug und Trug.) Eine Regex-Engine kann aber natürlich beliebig kompliziert werden. Will ich das wirklich in ASM implementieren? Wird das nicht ein riesiges Monstrum? Genau das, was ich vermeiden wollte?

Mir war das von Anfang an klar und ich hatte einfach gehofft, dass mir unterwegs etwas Cleveres einfallen würde.

Nope.

Dann hab’ ich endlich das hier gefunden:

https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html

Lustigerweise waren Brian Kernighan und Rob Pike auf der Suche nach einer einfachen Regex-Engine und alle existierenden waren zu groß. Also schrieb Rob Pike drei sehr kurze C-Funktionen. Damit kann man zwar nicht alle regulären Ausdrücke erkennen (natürlich), aber doch eine sehr nützliche Teilmenge:

c    matches any literal character c
.    matches any single character
^    matches the beginning of the input string
$    matches the end of the input string
*    matches zero or more occurrences of the previous character

All das in diesen drei kurzen Funktionen. Das ist einfach „wow“. Oder wie Brian Kernighan schreibt: „a superb example of beautiful code“.

Faul, wie ich bin, habe ich eine ASM-Version seiner Routinen in grep.asm eingebaut.

Und damit sind wir am Ziel angekommen. grep.asm ist, von etwaigen Bugfixes abgesehen, erst einmal fertig. Der Tag blogpost-step-3 zeigt auf die Version dieses Postings.

vain

Die propietären Grafikkartentreiber der Nvidia-340xx-Serie (und allem, was damit zusammenhängt) wurden aus den Repos entfernt, da eine vernünftige Betreuung durch die Arch-Entwickler nicht mehr gewährleistet werden kann (der letzte Rechner im Besitz segnet gerade das Zeitliche).

Benutzer einer entsprechenden Hardware wird empfohlen, auf die freien Treiber (nouveau-Paket) zu wechseln.

https://wiki.archlinux.org/index.php/No … _installed

Die PKGBUILDs wurden übrigens nicht ins AUR verschoben. Wer sie dennoch braucht, muss sie sich mittels "asp"
besorgen. Das Hochladen ins AUR ist nicht verboten.

Wie im englischen Forum beschrieben, startet systemd-timesyncd nach einem Update auf systemd-242.0-1 ggf. nicht mehr.
Es muss dann ein manueller Eingriff erfolgen:

# rm -rf /var/lib/systemd/timesync /var/lib/private/systemd/timesync

Es wurde ein neues monatliches Installationsimage veröffentlicht. Dieses trägt die Bezeichnung 2019.04.01. Anders als bei den letzten Images wird ab sofort der Kernel in Version 5.0 verwendet, was für eine höhere Hardwarekompatibilität sorgen kann. Der Download ist wie immer auf der Downloadseite in verschiedenen Formaten verfügbar.

Der Download ist für neue Arch-Nutzer interessant, oder für alle, die gern ein aktuelles Installations- bzw. Live-Image von Arch haben möchten. Für bestehende Installationen ist der Download wie immer nicht nötig.

Am Ziel angekommen

28.03.2019

Hallo liebe Community,

vielleicht kurz etwas zu meiner Geschichte. Den ersten Kontakt mit Arch Linux hatte ich vor zwei Jahren, leider habe ich es geschafft das System so zu zerschießen, dass es mir nicht mehr möglich war es irgendwie wieder zu fixen. Nach einer Zeit des Distrohopings kann ich nun stolz verkünden, das Arch Linux schon seit einigen Monaten auf meinen Produktivsystemen läuft. Und ganz ehrlich ich möchte es nicht mehr missen. Kein System lief bei mir bis jetzt so problemlos und dabei mit so aktuellen Paketen. Noch dazu habe ich bei Arch Linux so viele Freiheiten wie fast nirgends (abgesehen von Gentoo und LfS). Dafür auch nochmal von mir ein großen Dank an die Arch Linux Community. Werde versuchen mich mit einzubringen und somit etwas zurückzugeben.

Auch neu hier ...

27.03.2019

... und bin seit einer Woche mit Arch Linux auf einem meiner "älteren" Schlepptops mit Arch Linux unterwegs (T61) - und was soll ich sagen:
Insgesamt bin ich angenehm überrrascht!

Zu mir
Bj 61, Geek. Bastel gerne am Gerät wink

Nach längerer Linux_Abstinenz (letzte Versuche waren mit LM Sonya, oder Sylvia wink -  und vorherigen Ubuntu-, Debian- Katastrophen und meinen bescheidenen Suse ~5.3 Kernel-Bau-Experimenten gg. Ende der 90iger, bin ich
über den ct'uplink Podcast 25.1 vom 01.12.2018 auf ArchLinux gestolpert.
Hätte nicht gedacht, dass ich mich jemals wieder an eine "hand-made" Linux_Distro heranwage, es mich sogar wieder interessiert und mit den üblichen Stolpersteinen doch relativ geschmeidig zurecht komme wink

Aktuell sind ein paar Probleme aufgetaucht und an das Advanced Dock wage ich mich noch nicht heran. Aber mit solch einer netten, hilfsbereiten und versierten Arch Community kann ja nix mehr schiefgehen wink

Nun denn Willkommen und auf ein neues Linux-Abenteuer smile
feba

---------------------------------------------------------------------------------------------------------------------------------------
T61, Intel 965PM model 8898, cpu T5750, IGMA X3100, 4GB, SSD256, , TP Advanced Dock

Das richtige Partitionierungsprogramm

Seit der jüngsten Aktualisierung erstellt das grub-Paket nicht mehr automatisch eine Konfigurationsdatei in /boot/grub. Auch das Verzeichnis selbst fällt durch diese Änderung weg.


Siehe auch hier.

Dank an drcux, dem dies aufgefallen war.

Sehr cool !
Der neue lts-kernel 4.19.xx zeigt mir jetzt 0 Fehler nach dem Booten auf meinem System.
(journalctl -xb -p err)

grep.asm – Schritt 2: Ein besseres cat

Voriges Posting dieser Serie.

Sorry für die lange Wartezeit. Geht nicht wirklich schnell voran im Moment. Tja.

Auch dieses Posting braucht wieder einen Disclaimer: Da ich kein Assembler-Experte bin, bin ich über jede Korrektur und jeden Verbesserungshinweis dankbar!

Mehr Daten auf einmal lesen

Eines der großen Probleme der vorherigen Version war, dass wir ein Byte nach dem anderen gelesen haben. Dadurch wurde der Code zwar ein bisschen einfacher, aber das ist wirklich langsam. Wenn man erwartet, mehr als nur ein paar Bytes zu lesen, dann sollte man das anders machen. Mein bevelbar liest von STDIN und liest in der Tat Byte für Byte, aber da fließen eben auch nicht viele Daten. Die meiste Zeit über schläft bevelbar, also interessiert es keinen.

Anders bei einem grep. Mein voriger Code hat etwa 1.8 Sekunden gebraucht, um eine Datei von 4 MB zu verarbeiten. Das ist echt nicht gut. Jetzt dauert es nur noch etwa 60 Millisekunden, was immer noch nicht schnell ist, aber schonmal deutlich besser als vorher.

Dieser Performance-Sprung liegt daran, dass jetzt eben nicht mehr Byte für Byte gelesen wird. Man erinnere sich, dass „Byte für Byte lesen“ bedeutet, für jedes Byte einen Syscall absetzen zu müssen. Jeder Syscall ist mit viel Overhead verbunden und daher kommt es zu einer unnötigen Verzögerung. Um das zu vermeiden, allokiert das Programm jetzt einen großen Puffer von 4 MB und setzt nur noch einen read()-Syscall ab. (Idealerweise. read muss nicht alle angeforderten Bytes tatsächlich lesen, sondern kann auch schon vorher zurückkehren. Dann muss man es nochmal aufrufen, um den Rest zu bekommen.)

Faul bin ich aber immer noch: Das Programm liest den gesamten Input, bis ein End-of-File erreicht wird. Das heißt, wenn man durch eine Datei von 1 GB Größe greppen will, dann braucht man 1 GB an Speicher. Das lässt sich später aber leicht optimieren – ich habe das Programm schon so organisiert, dass sich die anderen Funktionen nicht mehr um dieses Implementierungsdetail kümmern müssen. Die arbeiten alle mit einem „struct“, in welchem beschrieben ist: „Hier ist ein Puffer, die aktuelle Zeile fängt hier an und ist so lang.“

Okay, so sieht die Hauptschleife jetzt (ohne Kommentare) aus:

.text
.global _start

_start:
    pushq %rbp
    movq %rsp, %rbp

    subq $64, %rsp
    movq %rbp, %rdi
    call init_buffer_info

    .Lreadwriteloop:
        movq %rbp, %rdi
        call next_line

        cmpq $0, %rax
        je .Lreadwriteloop_end

        movq %rbp, %rdi
        call test_match

        cmpq $0, %rax
        je .Lreadwriteloop

        movq %rbp, %rdi
        call print_current

        jmp .Lreadwriteloop

    .Lreadwriteloop_end:
        movq $0, %rdi
        call exit

In Pseudocode:

- Initialize a thing called "buffer info".
- Loop:
    - Read a line. (Update "buffer info" and mark the current line.)
    - Exit if no line left.
    - Test if the current line matches a pattern.
    - Loop again, if it doesn't.
    - Print the current line.

Im Moment wird beim ersten Aufruf von next_line() alles in einen großen Puffer eingelesen. Spätere Aufrufe aktualisieren dann nur noch Pointer und Counter. Wie man aber sehen kann, ist das für die Hauptschleife und die Funktionen egal.

Wieso? Auf dem lokalen Stack von _start wird Speicher für 64 Bytes allokiert. Dort drin speichern wir dann ein „struct“ namens „buffer info“. „struct“ meint hier im Wesentlichen dasselbe wie ein struct in C. Es enthält diverse Felder:

 -8(%rbp), 8 = pointer to data (char *)
-16(%rbp), 8 = allocated size of entire buffer (size_t)
-24(%rbp), 8 = current fill of buffer (size_t)
-32(%rbp), 8 = offset for current line in buffer (size_t)
-40(%rbp), 8 = length of current line (size_t)

So ziemlich alle Funktionen arbeiten mit diesem struct: next_line() sorgt dafür, dass der Pointer und die Counter auf die aktuelle Zeile zeigen; test_match() arbeitet nur auf der aktuellen Zeile; print_current() gibt ausschließlich die aktuelle Zeile aus.

Die Details von next_line() überspringen wir jetzt. Da sind viele Kommentare drin. Ähnlich wie bei der vorigen Version verwaltet es Speicher mit dem Syscall brk() und liest Daten mit read().

Mit rep stosb Speicher initialisieren

Eine frühe Version von init_buffer_info() sah so aus:

movq $0, -8(%rdi)
movq $0, -16(%rdi)
movq $0, -24(%rdi)
movq $0, -32(%rdi)
movq $0, -40(%rdi)

Heißt also, es wird nur ein Haufen Nullen in den Speicher geschrieben. Das sind jetzt nur fünf Instruktionen, die nur ein Mal während der gesamten Laufzeit des Programms aufgerufen werden, also spielt’s sehr wahrscheinlich keine Rolle, wie man das macht. Die Frage stellte sich aber trotzdem: Was, wenn man ein paar Megabytes an Speicher initialisieren wollte? Oder mehr?

Klar, man kann es so machen (für len >= 1):

movq $0, %rcx
movq $pointer, %rdi
.Lloop_start:
    movb $0, (%rdi)
    inc %rdi
    inc %rcx
    cmpq $len, %rcx
    jne .Lloop_start

Aber gibt’s da keinen besseren Weg? Da ich nun nicht allzu viel Erfahrung mit Assembler habe, ist das beste, was ich finden konnte, der „rep“-Mechanismus, der mit „string instructions“ zusammenarbeitet.

In diesem konkreten Fall benutzen wir mal rep stosb:

movq $pointer, %rdi
movq $40, %rcx
movb $0, %al
rep stosb

Wir schreiben also eine 40 in %rcx, dann ein NULL-Byte nach %al. stosb liest dann dieses Byte aus %al aus und schreibt es im Speicher an die Stelle von %rdi. (Segmentierung ignorieren wir.) stosb erhöht dann auch automatisch den Pointer %rdi um eins, damit beim nächsten Aufruf das folgende Byte im String angefasst wird. Und wie ruft man stosb nochmal auf? rep erledigt das. rep lässt die Instruktion immer und immer wieder laufen, bis irgendwann %rcx den Wert 0 erreicht.

Das ist also eine sehr knappe Form von memset().

Noch ein kleines Detail. String-Operationen wie stosb können den Wert in %rdi erhöhen, wie oben gesehen, oder verringern. Das hängt davon ab, ob das „direction flag“ gesetzt ist. Mit std kann man es setzen oder mit cld löschen.

Noch mehr rep: Mit repne scasb ein strlen() implementieren

Okay, unser grep liest also einen großen Haufen Daten und schreibt sie in einen Puffer. Den muss es dann natürlich durchsuchen, um die einzelnen Zeilen herauszufischen – weil grep ein zeilenorientiertes Programm ist.

Man erinnere sich an das struct vom Anfang:

 -8(%rbp), 8 = pointer to data (char *)
...
-32(%rbp), 8 = offset for current line in buffer (size_t)
-40(%rbp), 8 = length of current line (size_t)

Bei jedem Aufruf muss next_line() also offset erhöhen und length aktualisieren. Das tut es, indem es bei pointer + offset startet und dann jedes folgende Byte prüft, ob es ein Newline-Character ist. Sobald einer gefunden ist, wird die beobachtete Länge in length abgelegt.

Das ist im Wesentlichen dasselbe wie das traditionelle strlen(), bloß dass eben auf ein Newline geprüft wird und nicht auf ein NULL-Byte.

Nun könnte man das natürlich so programmieren, dass man einfach manuell eine Schleife schreibt. Genau das hatte ich zuerst auch gemacht und das sah so aus:

current_line_length:
    /* Find the length of the current line.
     *
     * in:
     *   %rdi, pointer to base of info struct
     *
     * out:
     *   %rax, current line length */

    movq $0, %rax
    movq -8(%rdi), %rbx
    movq -24(%rdi), %rcx
    movq -32(%rdi), %rdx
    addq %rdx, %rbx

    .Lfind_next_newline_loop:
        cmpb $10, (%rbx)
        je .Lfind_next_newline_found

        incq %rax
        incq %rbx
        incq %rdx
        cmpq %rdx, %rcx
        je .Lfind_next_newline_exceeded_bounds_check

        jmp .Lfind_next_newline_loop

    .Lfind_next_newline_found:
        incq %rax
        ret

    .Lfind_next_newline_exceeded_bounds_check:
        ret

Ziemlich lang und ausführlich.

Hier kann man dann wieder ein rep-Konstrukt stattdessen verwenden:

current_line_length:
    movq -8(%rdi), %rdx
    movq -32(%rdi), %rbx
    addq %rbx, %rdx

    movq -24(%rdi), %rcx
    movq -32(%rdi), %rbx
    subq %rbx, %rcx
    movq %rcx, %rbx

    movq %rdx, %rdi
    movb $10, %al
    cld
    repne scasb

    subq %rcx, %rbx
    movq %rbx, %rax
    ret

Diesmal benutzen wir repne statt wie oben rep. Das wiederholt ebenfalls die Instruktion, bis %rcx 0 erreicht hat, prüft aber zusätzlich auch das „Zero Flag“, wie man bei Intel in der Dokumentation zum x86 Instruction Set nachlesen kann.

Jetzt muss man ein kleines Detail kennen: Wenn man ein cmp %rax, %rbx ausführt, zieht die CPU die beiden Register voneinander ab. Ein darauffolgendes je („jump if equal“) ist dann dafür gedacht, den Sprung durchzuführen, wenn die beiden Werte gleich waren – oder anders gesagt, wenn eine Subtraktion 0 ergeben hat. Dafür wird das Zero Flag benutzt. Wenn es also gesetzt ist (Subtraktion ergab 0), dann waren die beiden Werte gleich.

Wenn die Intel-Doku also von „repeat while ZF flag is clear“ spricht, ist das dasselbe, wie wenn man sagen würde „repeat while a comparison did not determine two values to be equal“. Deswegen ist auch repnz („repeat while not zero“) nur ein Alias für repne („repeat while not equal“).

Gut. Wir wissen jetzt also, was repne tut. Jetzt brauchen wir noch eine String-Operation ähnlich zu stosb, was wir in init_buffer_info() benutzt haben, aber diesmal soll sie den String nicht verändern, sondern nur Bytes daraus mit einem Wert vergleichen. Und das erledigt dann scasb: Das ist kurz für „scan string, length byte“. Das führt ein cmpb %al, (%rdi) aus und erhöhrt/verringert danach %rdi. Das repne drumherum wertet dann das Ergebnis dieses cmp aus (und prüft, ob %rcx die 0 erreicht hat).

Kurzum, wir haben nun ein strlen().

Da ist immer noch eine ganze Menge „Noise“ oben im Code um das eigentliche repne scasb drumherum. Trotzdem glaube ich, dass das so besser ist als der vorherige Spaghetti-Code. Man muss nur mit rep* vertraut sein.

Fazit

Reguläre Ausdrücke sind immer noch nicht in Sicht, aber wir haben schonmal ein paar der naiven Dinge des ersten cat behoben. Der Code beachtet jetzt auch die Rückgabewerte von Syscalls.

Oh, wenn man sich den Code von grep.asm anschaut, beachte man bitte die Git-Tags. Unter blogpost-step-2 findet man den die zu diesem Blogpost zugehörige Version.

Jetzt ist es also Zeit, test_match() zu schreiben.

Fortsetzung: Diese Serie geht in grep.asm – Schritt 3: Reguläre Ausdrücke erkennen weiter.

vain

Moin

10.09.2018

Hallo
ich bin der Aziz, komme ursprünglich aus Marokko und arbeite jetzt in Deutschland.
ich habe mich dazu entschieden mich in archlinux einzuarbeiten, weil ich gerne technisch rumexperimentiere, mehr über Linux verstehen möchte und gern die volle Kontrolle über mein System habe.
Außerdem finde ich pacman und das rolling release konzept sehr interessant. Nebenbei bemerkt ist auch das wiki sehr gelungen.

Ich hoffe ich werde noch viel Freude an Archlinux haben.

PS: Deutsch ist nicht meine Muttersprache und ich arbeite erst seit zwei Jahren in Deutschland, also seid bitte verständnisvoll für sprachliche Fehler ^^.

Die Aktualisierung auf libutf8proc-2.1.1-3 erfordert das erzwungene Überschreiben von /usr/lib/libutf8proc.so.2:

pacman -Syu --overwrite usr/lib/libutf8proc.so.2

Hallo zusammen,

nachdem mein erstes Netbook, ein Acer Aspire One, viele Jahre mit Archlinux seinen treuen Dienst geleistet hat, wurde mangels Zeit und Lust, meine restliche Familie bei der Hand zu führen, ein Chromebook angeschafft, dessen einziger Zweck es war als unkomplizierte Surfmaschine zu dienen.
Das Aspire One läuft zwar noch, aber mit dem kleinen Bildschirm ist es nicht wirklich komfortabel und so wurde es immer mehr vernachlässigt, während das Chromebook weitgehend seinen Zweck erfüllte. Nun hat Google aber den Support für das gute Stück aufgekündigt und plötzlich kamen auch die ersten Abstürze, was vorher eigentlich kein Thema war.
Grund genug für mich das Chromebook, welches ohnehin von den anderen dank Smartphone und Tablet kaum noch genutzt wird, auf ein zukunftssicheres OS umzustellen. Da Archlinux schon auf meinem alten Desktop und dem Netbook hervorragende Dienste geleistet hatte, viel die Wahl nicht schwer. Besonders als ich das Chromebook auch noch im umfangreichen Wiki gefunden hatte.
Abgesehen von der Fummelei, das Gerät überhaupt für ein anders OS bootfähig zu machen, war ich doch erstaunt wie gut die Installation geklappt hat. Die grauen Zellen sind zwar etwas eingerostet, aber nach und nach kommt die Erinnerung zurück und es hat mal wieder Spaß gemacht etwas tiefer im System zu basteln.

In diesem Sinne ein großes Dankeschön an alle Beteiligten für dieses wunderbare System.

Der SMTP Server Postfix bietet sehr umfangreiche Möglichkeiten zur Reduzierung der Anzahl von Spam-Mails die es bis zum User-Account schaffen. Dieser Artikel befasst sich mit dem Szenario wie man bei verschiedenen Diensten im Internet eine spezielle E-Mail Adresse verwenden und nach einem Leak schon durch Postfix an diese Adresse gesendete …

Thorsten Töpper

Pacman 5.1.0

29.05.2018

Das neue Paket zu pacman enthält nicht mehr alle Tools, die im Vorgängerpaket noch enthalten waren. Diese wurden in das Paket pacman-contrib ausgelagert.

Edit: Link korrigiert.

DSGVO

12.05.2018

In knapp 2 Wochen ist es soweit, denn am 25.05.2018 tritt die Datenschutz-Grundverordnung (DSGVO) in Kraft.
Ich habe mich nun auch mal damit auseinandergesetzt und aus den ganzen Aenderungen Konsequenzen gezogen:

  • Alle iframes entfernt (YouTube, Vimeo, GitHub, Twitter, ...) und durch Links ersetzt
  • Unter Datenschutz ein bisschen ausfuehrlicher beschrieben, was ich speichere
  • Meine Content Security Policy (CSP) angepasst (u.a. mit Hilfe des CSP Evaluators von Google und ReportURI)
  • Den Header Referrer-Policy von no-referrer-when-downgrade zu strict-origin-when-cross-origin geaendert (Erklaerung)

Damit ist meine Website noch ein bisschen schneller und datenschutzfreundlicher geworden und ihr koennt nun selbst entscheiden, ob ihr euch das Verlinkte ansehen wollt oder nicht.
Da ja nun auch keine externen Videos mehr eingebunden werden, koennen diese trotzdem recht datenschutzfreundlich angeguckt werden, z.B. mit youtube-dl.

Es steht allerdings noch die Anpassung/Anonymisierung der IP-Speicherung durch Nginx aus.
Allerdings bin ich mir hier noch nicht sicher, ob und wie ich diese durchfuehre.

Fuer alle, die etwas mehr einsetzen als ich (z.B. Cookies), gibt es hier ein paar Links (auch die weiteren Links beachten!):

Interessant ist in dem Zusammenhang das Tool Webbkoll, was eine URL auf diverse Dinge, z.B. TLS, Referrer, Cookies, CSP und Third-parties testet.

Ergebnis fuer yhaupenthal.org von Webbkoll

Angestiftet durch den Hinweis "Referrers partially leaked" hatte ich kurzzeitig die Policy auf no-referrer gesetzt, mich dann aber doch wieder umentschieden, da so fremde Websites nicht einsehen koennen, dass Leute von mir kommen, was ich dann zwecks Bekanntheitsgrad vermeiden moechte. (Convince me otherwise!)

Die letzten Jahre war es hier recht ruhig. Das hatte verschiedene Gründe, unter anderem weil ich mich mit dem zuvor verwendeten octopress nicht wirklich anfreunden konnte. Ruby ist keine Sprache, welche ich häufig verwende und da octopress nach Aktualisierungen auf meinen Systemen leider häufiger Probleme mit inkompatiblen Gems hatte, war …

Thorsten Töpper