Datum:
Servus, es klemmt bei einem kleinen Programm (WinXP prof, Watcom C 1.9):
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> char fbuf[500000]; main ( int argc, char *argv[] ) { int st, size; memset( fbuf, '*', sizeof( fbuf)); st = setmode( fileno( stdout), O_BINARY); printf( "Zeile %d\n", st); size = 200000; fwrite( fbuf, size, 1, stdout); fputs( "Noch eine Zeile\n", stdout); size = 0xff; fwrite( fbuf, size, 1, stdout); return 0; } |
Auf der console aufgerufen kommt die Ausgabe: G:\test>usget Zeile 256 Noch eine Zeile ************************************************************************ ******** ************************************************************************ ******** ************************************************************************ ******** *************** G:\test> dh, fwrite gibt nichts aus, return-Code ist 0 (probeweise mal in W98 probiert, da schreibet fwrite das komplett raus). Entferne ich setmode() gibt fwrite() alles aus. Der obige Code ist radikal zum Testen zusammengestrichen. Das Original gibt mir ein PDF über den Apache aus, ich muß also stdout auf binär umschalten. Komischerweise funktioniert das auch in einem anderen Programm - nur das obige Primitivbeispiel zickt. Die Ausgaben mit printf() und fputs() funktionieren ohne Probleme auf stdout, nur fwrite() nicht. Ich blick's nicht mehr. Mit dem vorhergehenden Release sind mir da nie Probleme aufgefallen. Das vollständige Programm hat schon einige 100.000 PDF abgeschickt, Probleme wären da aufgefallen. Es zickt seit dem Umstieg von Watcom C32 v 11 auf Open Watcom 1.9. In welcher Richtung kann ich noch suchen ? PS: Gegooglet habe ich, überall wird setmode für diesen Zweck empfohlen.
Datum:
Ich denke das Problem ist die ANzahl der Zeichen. Mach die halt kleiner. Du kriegst ja sowieso von fwrite die ANzahl der geschriebenen Zeichen zurück und musst das ganze in eine Schleife packen, bis alles ausgegeben ist. So gesehen sind das dann halt einfach nur ein paar Aufrufe mehr. Warum bei dir da jetzt die Beschränkung existiert, kann ich dir auch nicht sagen. Ich hab aber im Laufe der Zeit gelernt, Datenfelder mit moderaten Größen zu benutzen. Irgendwo gibt es dann immer wieder mal eine Beschränkung, weil irgendwer vergessen hat, einen 16-Bit int als unsigned zu machen, von 16 Bit auf 32 Bit hochzugehen, etc.
size = 200000;
chunk_size = min( size, 4096 ); // nicht mehr als 4K auf einmal
total = 0;
do( total += fwrite( &fbuf[total], chunk_size, 1, stdout) )
{
} while( total < size );
|
Sicherheitshalber noch eine Abfrage auf einen Returnwert von fwrite von 0 einbauen, der weißt immer auf einen Fehler hin. Die maximale Chunk Size konfigurierbar machen. Das ist nichts weswegen man da jetzt in Panik verfallen muss.
Datum:
ja mit einer richten fehlerbehandlung bekommt man auch einen Fehler:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string> #include <io.h> #include <errno.h> char fbuf[500000]; int main ( int argc, char *argv[] ) { int st, size; memset( fbuf, '*', sizeof( fbuf)); st = _setmode( fileno( stdout), O_BINARY); printf( "Zeile %d\n", st); size = 100000; _set_errno( 0 ); int r = fwrite( fbuf, size, 1, stdout); if ( r <> size ) printf("r=%d errno=%d error:%s\n", r, _errno, _strerror(NULL) ); fputs( "Noch eine Zeile\n", stdout); size = 0xff; fwrite( fbuf, size, 1, stdout); return 0; } |
r=0 errno=4198958 error:Not enough space
Datum:
Danke, hmmmm ... Wie gesagt, ich verwende fwrite() schon ewig, teilweise mit erheblich mehr Zeichen wie schlappen 200.000. Irgendwie vermute ich ja auch einen Bug in der C-Lib. ohne setmode: einwandfreie Funktion mit setmode: fwrite gibt nur bis 0xffff Bytes aus (bei einem Kollegen nur 0xa9fff (experimentell ermittelt)). Sobald auf eine Datei umgeleitet wird (zB "usget >q") wird isatty() FALSE und es geht auch. Alternativ fbuf mit fputc() ausgeben ist auch ok (overkill). Das gleiche Programm funktioniert auf W98 problemlos. -- Im Original holt das Programm ein PDF aus einem Container und sendet den über CGI an den Apache Webserver. Aufgerufen wird es über spawn. Als Workaround habe ich die identische Funktion in das Programm rüberkopiert (spawn entfällt dadurch), alles funktioniert bestens. Es spinnt ;)
Datum:
Peter II schrieb: > r=0 errno=4198958 error:Not enough space Die Meldungen habe ich wegen der besseren Übersichtlichkeit rausgelassen - habe die auch. Nur, was sollte ein primitives write groß an Speicher brauchen ? Es soll die Daten ja nur auf einen Stream schieben. Wie oben gesagt: Auf Win XP mit 4 GB mault er über low mem, auf meiner W98-Kiste mit 640 KB macht er's.
Datum:
Joachim Drechsel schrieb: > Peter II schrieb: >> r=0 errno=4198958 error:Not enough space > > Die Meldungen habe ich wegen der besseren Übersichtlichkeit > rausgelassen - habe die auch. Nur, was sollte ein primitives > write groß an Speicher brauchen ? Es soll die Daten ja nur > auf einen Stream schieben. Irgendwelche Caches, Buffer, Speicherfelder um den Aufruf asynchron machen zu können, .... Was auch immer da intern passiert, es ist nichts was du jetzt gross beeinflussen kannst. Wie gesagt: du musst ja sowieso in einer Schleife mit dem Returnwert von fwrite arbeiten (*). Von daher ist das nur ein Zahlenwert, den du jetzt eben kleiner machen musst. Spielt auch keine grosse Rolle. Denn die eigentliche Ausgabe dauert ein vielfaches dessen, was dir die paar Aufrufe kosten. (*) das allerdings musst du auf jeden Fall. Die Annahme, du könntest beliebig grosse Speicherbereiche mit lediglich 1 Aufruf rausblasen, ist gefährlich! Die Macher von C haben sich bei den Returnwerten schon was gedacht (zumindest meistens).
Datum:
Karl Heinz Buchegger schrieb: > Was auch immer da intern passiert, es ist nichts was du jetzt gross > beeinflussen kannst. Vermutlich das Hauptproblem ... Ok, ich danke auch und baue mir einen fwrite-Ersatz zusammen. Auswürfeln mit Blockgröße 4KB sollte gehen. Grüße Joe.
Datum:
Warum nimmst du überhaupt gepufferte Ausgabe? Um Datenblöcke durchzuschieben hat das doch nur unnötigen Overhead. Vielleicht wäre ungepuffertes Schreiben nach fd=1 schneller und unkomplizierter. Das Vermischen von gepuffertem Zugriff (fwrite) und ungepuffertem (setmode) ist inhärent fragwürdig, auch wenn es manchmal gutgeht.
Datum:
Ich muß die Konvertierung von \n nach \r\n abschalten. Das ist der einzige Grund. Mit write() statt fwrite() kommt das gleiche heraus.
Datum:
Umgebaut auf 4 KB-Blöcke, der gleiche Fehler (egal ob ich write() oder fwrite() zum Schreiben der Blöcke verwende). Ein Test mit Borland C ergab das gleiche Problem. Blockweise schreibt er das jetzt immerhin in de console aber nicht zum Indianer. (herumhüpf - lautlach - der Wahn droht - Katze fangen - da kommt die weiße Jacke - Augenrollen). Ich lasse das jetzt mal gären. Vielleicht kommt mir irgendwann die Erleuchtung. --- Vermutlich covern beide die Windows-API-Funktionen. Vielleicht klemmt es da (bei beiden gleich ?).
Datum:
Nachtrag (falls es jemanden interessiert): Ich habe mir die Quellcodes bei Watcom heruntergeladen und mal verfolgt wie die das machen. Wie bei einem cross platform-Projekt zu erwarten eine Unzahl an #defines um alles auseinanderzuhalten. fwrite verwendet bei einem Textstream schlicht fputc(), bei binary streams wird es interessant: dort wird write() aufgerufen, write() packt das in 512 Byte Häppchen und schiebt es an os_write() weiter. os_write() übergibt es dann an die Win32-Funktion WriteFile(). Die stößt sich an den 200 KB und gibt die Meldung "F³r diesen Befehl ist nicht gen³gend Speicher verf³gbar." aus. The WriteFile function may fail with ERROR_INVALID_USER_BUFFER or ERROR_NOT_ENOUGH_MEMORY whenever there are too many outstanding asynchronous I/O requests. Na ja ... Im Moment habe ich noch 2 GB Speicher frei. Borland macht das offensichtlich genauso, deshalb das identische Verhalten. Von einer Grenze der Datenmenge steht im Helpfile nichts.
Datum:
Joachim Drechsel schrieb: > fwrite verwendet bei einem Textstream schlicht fputc(), bei binary > streams wird es interessant: dort wird write() aufgerufen, write() > packt das in 512 Byte Häppchen und schiebt es an os_write() weiter. Aber merkt dein fwrite() überhaupt etwas davon, daß du es binär ausgeben willst? Vom deinem setmode() merken die f...-Funktionen doch nichts, und sonst könnten sie es doch nur beim Öffnen merken - was du aber bei stdout ja nicht tust. (Ceterum censeo... ich halte es nach wie vor für keine gute Idee, gepufferte und ungepufferte IO zu mischen)
Datum:
Klaus Wachtler schrieb: > (Ceterum censeo... ich halte es nach wie vor für keine gute Idee, > gepufferte und ungepufferte IO zu mischen) Gerade DAS passiert aber in der C-Lib.
Datum:
Nein. Die C-Lib macht für die f...-Funktionen die Pufferung, und weiß was sie darunter mit den ungepufferten Funktionen anstellt; natürlich nutzt sie letztlich dann die ungepufferten für die eigentliche Übertragung. Aber an den Bibliotheksfunktionen vorbei etwas auf der ungepufferten Seite vorbei zu manipulieren und für dieselben Dateien sonst die Pufferung zu verwenden, ist das was ich vermeiden würde, soweit möglich. Genau an diesem Beispiel sieht man es doch: du stellst etwas auf binäre Übertragung, aber die f...-Funktionen merken es vielleicht nicht und nehmen trotz der abgeschalteten Übersetzung womöglich einen Funktionsaufruf für jedes einzelne Zeichen. Wenn du eh nur Blöcke durchschiebst, brauchst du die gepufferten Funktionen doch gar nicht? Aber letztlich ist es mir natürlich egal - wenn es geht, ist es gut. Ich wollte ja nur mein übliches Mißtrauen kundtun :-)
Datum:
Klaus Wachtler schrieb: > Ich wollte ja nur mein übliches Mißtrauen kundtun :-) Was genau das ist was ich höhren möchte. Ja-Sager gibt es bekanntlicherweise genug ;) Ich klebe an den stream-Funktionen weil stdout halt ein stream ist. Übrigends wird in allen cover-Funktionen getestet ob die vorhergehende Ausgabe abgeschlossen ist ... vermutlich, damit sich buffered und unbuffered I/O nicht ins Gehege kommen.
Datum:
Joachim Drechsel schrieb: > Ich klebe an den stream-Funktionen weil stdout halt ein stream ist. Dann nimm doch als fd einfach 1 für die ungepufferten Funktionen. 0 ist klassischerweise Standardeingabe, 1 ist Standardausgabe und 2 Standardfehlerausgabe. Dein fileno( stdout) ganz oben sollte also eine schnöde 1 liefern. Als Lohn der Umstellung geht das Schreiben wohl auch noch schneller, weil fwrite() über ein paar Umwege ja letztlich auch zu einem write() kommt (im besten Fall, und nicht mit etwas Pech auch noch zeichenweise ausgibt).
Datum:
Klaus Wachtler schrieb: > Dann nimm doch als fd einfach 1 für die ungepufferten Funktionen. > 0 ist klassischerweise Standardeingabe, 1 ist Standardausgabe und 2 > Standardfehlerausgabe. > Dein fileno( stdout) ganz oben sollte also eine schnöde 1 liefern. Mache ich ja. Ich muß nur für die binäre Ausgabe stdout mit setmode() auf binary schalten (sonst kann ich zB keine Bilder oder PDFs ausgeben). Und damit fängt der Schlamassel an ... Ohne setmode() gibt es alles aus (halt ggfls falsch). Dann verhalten sich fwrite/write etc noch unterschiedlich abhängig von isatty(). Mit WriteFile() zeigt sich übrigends der anfangs genannte Fehler auch. Das Filehandle habe ich dazu mit GetStdHandle( STD_OUTPUT_HANDLE) geholt. Die in der Watcom surce vorhandene Funktion __getOSHandle() habe ich nicht gefunden.
Datum:
Ich störe mich ja auch nicht am setmode(), sondern am anschließenden fwrite() :-) Mein Vorschlag geht dahin, das fwrite( stdout, ... ) durch write( 1, ... ) zu ersetzen. Aber wie gesagt, ich will mich nicht einmischen - du wirst wissen, was du willst.
Datum:
Klaus Wachtler schrieb: > Ich störe mich ja auch nicht am setmode(), sondern am anschließenden > fwrite() :-) > Mein Vorschlag geht dahin, das fwrite( stdout, ... ) durch write( 1, ... > ) zu ersetzen. Alles probiert. write() berücksichtigt leider auch den mode. Ich habe das im Quelltext verfolgt. fwrite verwendet write, write verwendet dann WriteFile.