Servus,
es klemmt bei einem kleinen Programm (WinXP prof, Watcom C 1.9):
1
#include<stdio.h>
2
#include<stdlib.h>
3
#include<fcntl.h>
4
5
charfbuf[500000];
6
7
main(
8
intargc,
9
char*argv[]
10
)
11
{
12
intst,size;
13
14
memset(fbuf,'*',sizeof(fbuf));
15
16
st=setmode(fileno(stdout),O_BINARY);
17
printf("Zeile %d\n",st);
18
19
size=200000;
20
fwrite(fbuf,size,1,stdout);
21
fputs("Noch eine Zeile\n",stdout);
22
23
size=0xff;
24
fwrite(fbuf,size,1,stdout);
25
26
return0;
27
}
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.
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.
1
size=200000;
2
chunk_size=min(size,4096);// nicht mehr als 4K auf einmal
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.
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 ;)
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.
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).
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.
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.
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 ?).
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.
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)
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.
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 :-)
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.
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).
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.
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.
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.