Buffer für read()



  • Nabend,

    seit Stunden versuche ich, einen Buffer für read() zu schreiben. Vielleicht kann mir ja jemand helfen. Und zwar schreibe ich ein TCP-Client, der Zeichen empfängt und diese in einer Variable speichert, deren Speicher dynamisch reserviert werden soll.

    #define BUFSIZE 1024
    
    write(srv_sock, request, strlen(request));
    
    char buffer[BUFSIZE]
    char *result;
    
    read(srv_sock, buffer, BUFSIZE);
    {
      ?
    }
    

    Ich möchte die empfangenen Daten in der Variable result speichern.
    Ich danke jedem, der mit hilft!

    MfG
    Fauna



  • Nunja, ohne Datenstruktur musst du wohl oder übel immer mit realloc() das result vergrößern.

    MfG SideWinder



  • Machst du ca. so:
    Wenn buffer voll, result mit realloc um BUFSIZE vergrößern und die Zeichen von buffer nach result kopieren, oder, wenn read fertig ( es fehlt eine Abbruchbedingung ),
    result mit realloc um die Anzahl der in buffer eingelesenen Zeichen vergrößern und diese restlichen Zeichen kopieren.



  • malloc und realloc ist bei ansi-c das Stichwort.
    Ich würde wenn dann volle 4K pages reallozieren.

    Auch ist eine weitere Kopie nicht erforderlich,
    man kann read() einen Zeiger direkt auf den reallozierten Puffer geben.

    Das ist zwar nicht gerade trivial aber machbar.

    irgendwie soetwas (ohne error handling, not tested) ...

    char*  pBuffer = malloc (4096);
    size_t nBufferLen = 0;
    size_t nBufferMaxLen = 4096;
    
    int newLen;
    
    while (1)
    {
      newLen = read(srv_sock, pBuffer + nBufferLen, nBufferMaxLen - nBufferLen);
      if (newLen > 0)
        nBufferLen += newLen;
    
      if (nBufferMaxLen - nBufferLen < 4096)
      {
        pBuffer = realloc (pBuffer, nBufferMaxLen + 4096);
        nBufferMaxLen += 4096;
      }
    
      ...
    }
    

    Viel Erfolg, gruß Frank



  • Frank Erdorf schrieb:

    Auch ist eine weitere Kopie nicht erforderlich,
    man kann read() einen Zeiger direkt auf den reallozierten Puffer geben.

    ja, das ist die bessere variante, ohne zu kopieren, direkt den reservierten speicher zu benutzen.



  • B.B. schrieb:

    Frank Erdorf schrieb:

    Auch ist eine weitere Kopie nicht erforderlich,
    man kann read() einen Zeiger direkt auf den reallozierten Puffer geben.

    ja, das ist die bessere variante, ohne zu kopieren, direkt den reservierten speicher zu benutzen.

    Hättest du ein Beispiel für mich??



  • B.B. schrieb:

    ja, das ist die bessere variante, ohne zu kopieren, direkt den reservierten speicher zu benutzen.

    Eine verkettete Liste ist bei großen Datenmengen besser, weil dann realloc ne ganz schöne Bremse ist.
    Falls du trotzdem ein Array brauchst, kannst du die Liste zum Schluss in ein Array kopieren.



  • Hallo Fauna
    Das Beispiel hatte ich schon gepostet,
    ist wohl nicht so leicht zu durchschauen,
    bitte versuch es mal zu durchschauen.

    Noch etwas:
    Realloc ist keine Bremse, wenn man in 4K Pages alloziert.
    Wenn der virtuelle Memorymanager gut ist ändert sich nur
    eventuell die virtuelle Adresse und das interne Mapping.
    Moderne virtuelle Memorymanager z.B. xp und linux sind gut.

    Ist der virtuelle Memorymanager (nachweislich) nicht gut,
    kann man ein eigenes Paging, wie schon erwähnt,
    z.B. in einer verketten Liste aufziehen.

    Auch das hilft unnötige Kopien zu vermeiden.

    Gruß Frank



  • Frank Erdorf schrieb:

    Noch etwas:
    Realloc ist keine Bremse, wenn man in 4K Pages alloziert.
    Wenn der virtuelle Memorymanager gut ist ändert sich nur
    eventuell die virtuelle Adresse und das interne Mapping.
    Moderne virtuelle Memorymanager z.B. xp und linux sind gut.

    Ist der virtuelle Memorymanager (nachweislich) nicht gut,
    kann man ein eigenes Paging, wie schon erwähnt,
    z.B. in einer verketten Liste aufziehen.

    Gruß Frank

    Da haben wir schonmal drei Probleme: zweimal wenn und einmal eventuell.
    Deinem Motto nach haben wir die Situation:
    Lieber Kunde, probieren Sie bitte diese Version. Sollte diese Version zu langsam sein, dann schicken wir Ihnen die ungebremste.



  • Frank Erdorf schrieb:

    Noch etwas:
    Realloc ist keine Bremse, wenn man in 4K Pages alloziert.
    Wenn der virtuelle Memorymanager gut ist ändert sich nur
    eventuell die virtuelle Adresse und das interne Mapping.
    Moderne virtuelle Memorymanager z.B. xp und linux sind gut.

    Echt? Naja, ich bevorzuge 8k-Seiten, das sollte aber nicht das Problem sein.
    Also kann ich davon ausgehen, daß wenn ich nur ganzahlige Vielfache von 8k nehme, realloc niemals NULL zurückgibt und darüberhinaus keine Zeit für's Kopieren verbraten wird?
    Ahh, man mremap.
    Jetzt nur noch ein Gegenstück in der WinAPI aufspüren.



  • Also kann ich davon ausgehen, daß wenn ich nur ganzahlige Vielfache von 8k nehme, realloc niemals NULL zurückgibt und darüberhinaus keine Zeit für's Kopieren verbraten wird?

    Selbstverstandlich kann realloc NULL bei einer OOM Condition zurückgeben ...

    Ich gehe zumindest davon aus,
    das die Jungs, die moderne OS programmieren was drauf haben
    und halte zudem wenig davon mir ein eigenes Paging aufzuziehen,
    wo das doch der VMM selbst viel besser machen kann ..

    Tatsächlich musste ich das aber auch schon machen, zu DOS Zeiten.
    Wenn ich die gleiche Software jetzt profile ist das eigene Paging immer in den top 30.
    Was solls, es wird so bleiben,
    der Kunde wird solch eine Umstellung sowie nicht zahlen ...

    mremap

    Genau das kommt der Sache schon nahe.

    Für win32 findest du vieleicht was unter CreateFileMapping ...

    mmap und CreateFileMapping sind schon tolle Funktionen,
    Richtig intressant wird es aber erst,
    wenn man ein serialisiertes Software Desgin macht,
    dann kann man seine Strukturen/Klassen direkt vom VMM füllen lassen,
    pageweise, so wie gerade benötigt ...
    effizienter geht nicht.

    Gruß Frank



  • Ja, bei Dateien von der Platte habe ich auch keine Probleme. Da kann ich leicht nichtkopierende Substrings draufsetzen und damit Zeilenweise lesen und weiterverarbeiten oder rekursiven Absteigsparser machen und dies und das, alles wird endschnell. Als CreateFileMapping rauskam, hatte ich mich draufgestürzt, damals mit nur einem Kern, ich konnte mir leicht per Refcounting oder so merken, welche Views genutzt werden und ungenutzte freigeben. Naja, das bedeutet heute Inter-Thread-Kommunikation. 😞 Aber ich kann mir viele Fälle, wo man aus der Anwendungslogik heraus sagen kann, daß alle Daten von 0 bis ${hier} nicht mehr benötigt werden und kann die Substrings extrem dumm machen.

    Im Moment plane ich einen HTTP-Server. Nur Privateprojekt, den Aufwand muß ich bei keinem Arbeitgeber rechtfertigen. Da wären die absolut dummen Substrings nicht so gut, wenn die Anfrage mal zufällig mehr als 4k hat. Aber den Weg der doppelt verketteten Liste von Eingangspuffern mit Refcounting von den Substrings und Wegwerfung bei Nichtreferenzierung mag ich nicht gehen; wegen der nötigen Locks oder des nicht gerigend Aufwands für lockfree Strukturen. Ein quasi instantanes Pufferwachsen käme mir jetzt gelegen, dann kann ich als Default bei 4k bleiben und bei Bedarf billig wachsen. Aus der Anwendungslogik kommt immer ein sehr klares Signal, bis wohin alles weggeworfen werden kann, wenn die Anfrage bearbeitet wurde.

    Klar,wenn der Speicher weg ist gib's NULL. Das meinte ich nicht.

    Da es einfach ist, mremap zu benutzen, mache ich das mal. Ich gehe von 64-Bit aus, wo der virtuelle Speicher nicht ausgeht, also mremap nicht wegen Fragmentierung fehlt.



  • Frank Erdorf schrieb:

    Ich gehe zumindest davon aus, das die Jungs, die moderne OS programmieren was drauf haben

    Ich auch, mremap ist mir total entgangen.

    Frank Erdorf schrieb:

    wenn man ein serialisiertes Software Desgin macht, dann kann man seine Strukturen/Klassen direkt vom VMM füllen lassen, pageweise, so wie gerade benötigt ... effizienter geht nicht.

    Ja, man bestellt foo[0] und die nächsten 2MB werden netterweise von alleine eingelagert mit UDMA-Tricks ohne Prozessorlast. Ich habe mich immer davor gedrückt, nach den Plattformabhängigkeiten der vptrs den Code zu verzeigen, und infolgedessen doch relativ normal serialisiert (halt noch Zahlen in hex, um Multiplikationen mit 1/10 zu sparen, Pascal-Strings, und so).



  • Im Grunde genommen geht es noch einfacher:

    #define lBufferMax = 1024 * 1024 * 128;  /* maximum 128 MB */
    void*  pBuffer = malloc (lBufferMax);
    size_t lBuffer = 0;
    

    man wird erstaunt sein wenn man auf modernen OS die Zeit misst,
    die fürs malloc (128 MB) benötigt werden: 0 Zeit!

    Was ist los ?
    pBuffer ist in diesem Moment nur eine Art versprechen, rein virtuell!
    Das eigentliche Memory Paging erfolgt erst beim Zugriff und
    nur soviel wie auch tatsächlich zugegriffen wird + prefetch in Pagesize (4K, ...).

    Einen Nachteil hat das aber schon,
    normalerweise muss der VMM, wenn er pBuffer != NULL liefert
    für die 128MB entweder (relativ freies) echtes RAM oder eben SWAP in der Hinterhand haben (nicht auslagern, das ist erstmal nicht nötig!).

    SWAP ist in diesem Fall soetwas wie die Goldreserven,
    die, wenn der virtuell herausgegeben Speicher tatsächlich benutzt wird angegriffen werden.

    Bei Linux kann man das Verhalten des VMM wunderbar einstellen,
    so daß nocht nicht mal in der geforderten Größenodnung SWAP bereit stehen muss.
    Zugegeben das erinnert an das Verhalten von Banken,
    die ins 'Trudeln' kommen wenn die Leute ihr Geld wieder haben wollen.
    Bei linux kommt dann der OOM Killer, der zweifelsfrei für freien Speicher sorgt,
    indem Prozesse abschießt.

    Tatsächlich haben wir für unsere Systeme sogenanntes overcommit eingestellt,
    weil etliche Applikationen (z.B. Java) Faktoren mehr Speicher anfordern als
    nacher benutzen.
    mmap ist auch so ein Kandiat, der Speicher der gesamten Datei wird virtuell reserviert, tatsächlich gemappt wird nur das was auch benötigt wird (in 4K Schritten).

    Tja wir konnten auf unseren Systemen nicht genügend SWAP für alle Eventualitäten auf dem Flash Massenspeicher reservieren,
    darum eben overcommit + ein Programm was permanent den realen Speicherverbrauch,
    Swap, ... ermittelt und visualisiert ...

    Interesannt ist auch das Grenzverhalten bei overcommit.
    Man erhält u.a. einen scheinbar gültigen pointer und
    wenn man dann darauf zugreift einen segmentation fault 😃

    Gruß Frank


Anmelden zum Antworten