Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : C - Utiliser les printf() sous AmigaOS
(Article écrit par Stéphane Saragaglia et extrait de GuruMed.net - août 2002, mis à jour en décembre 2003)
|
|
Je vous propose un premier article qui traite de l'utilisation en langage C des entrées/sorties et du multitâche
dans les applications Amiga. Cet article ne décrit pas ces aspects de manière exhaustive, mais expose
des cas d'utilisation, et attire l'attention sur quelques pièges.
On ne rigole pas, c'est plus compliqué que ça en a l'air... ;). D'abord, il faut différencier "printf()"
et "Printf()", et ensuite tenir compte des contextes d'appel...
Fonctions d'entrées/sorties des bibliothèques C et AmigaDOS
Les fonctions de la bibliothèque stdio du langage C (printf(), puts(), scanf(), gets()...) utilisent
les variables globales d'entrées/sorties standard stdin/stdout/stderr.
Par contre, les fonctions de la bibliothèque système dos.library (Write(), Printf(), PutStr(), Read()...)
utilisent les champs pr_CIS/pr_COS des processus desquelles elles sont appelées.
Cela a plusieurs impacts :
- Les fichiers d'entrées/sorties stdin/stdout et pr_CIS/pr_COS peuvent être différents.
- Les fonctions de la dos.library ne sont utilisables que depuis des "Process" (processus), en opposition avec les "Task" (tâches).
- Les fonctions de la bibliothèque stdio doivent être utilisées avec précautions dans les applications multi-processus.
Valeur des flux d'entrées/sorties au lancement d'un exécutable depuis le Shell ou le Workbench
Les valeurs de stdin/stdout et pr_CIS/pr_COS au démarrage d'une application dépendent du compilateur utilisé
et du code de démarrage qu'il rajoute. Ci-dessous est présenté un exemple de code de démarrage qui initialise
les flux (prélevé de newgccstart.lha sur Aminet). Attention,
cette archive ne contient que des ébauches de prototypes d'essais, mais ça donne une idée de ce qu'on peut y
trouver. ;)
newgccstart.lha
void __initstdio(void)
{
BPTR window;
if(_WBenchMsg!=NULL) // Executable lance depuis le WB
{
if((window=Open("CON:\\AUTO",MODE_NEWFILE))==NULL) // Creation d'une console
exit(RETURN_FAIL);
SelectInput(window); // Redirection des entrees/sorties de la dos.library vers cette console.
SelectOutput(window); //
}
stdin->file=Input();
stdout->file=Output();
if((stderr->file=((struct Process *)FindTask(NULL))->pr_CES)==NULL)
stderr->file=stdout->file;
}
|
Ainsi, on remarque dans cet exemple que si l'exécutable est lancé depuis le Workbench, une console est
créée, et les flux d'entrées/sorties de la dos.library sont redirigés vers cette console.
On constate également que stdin/stdout/stderr de la bibliothèque stdio sont associés aux flux
de la dos.library ; printf() et Printf() utiliseront donc la même sortie au lancement du programme.
Soit le simple programme "printf1.c" :
/* gcc -Wall -noixemul printf1.c -o printf1 -lamiga */
#include <stdlib.h>
#include <stdio.h>
#include <proto/dos.h>
int main(void)
{
printf("printf : Salut les codeurs !\n");
Printf("Printf : Salut les codeurs !\n");
exit(EXIT_SUCCESS);
}
|
Lancement depuis le Shell : on retrouve bien les deux messages dans le Shell. Les flux d'entrées/sorties
de la dos.library sont initialisés par le CLI, les flux sont dirigés vers sa console. Sauf cas particulier,
le compilateur génère, comme vu ci-dessus, un exécutable qui associe aussi stdout/stdin vers les flux de
la dos.library au démarrage. On a donc les deux messages.
Lancement depuis le Workbench : une fenêtre de sortie apparaît, dans laquelle s'affichent les messages.
C'est le compilateur qui l'a créée !
Valeur des flux d'entrées/sorties au lancement d'un Process depuis un exécutable
Les deux chapitres ci-dessous donnent des informations sur l'initialisation des entrées/sorties de Process
créés au sein d'applications.
Lancement d'un "Process-Fonction"
La fonction de lancement de Process-fonction utilisée ci-dessous est CreateNewProcTags(). Cette fonction
permet de lancer un nouveau processus tout en initialisant les flux d'entrées/sorties du Process créé.
Soit le simple programme suivant "printf2.c" qui crée un nouveau Process dans lequel printf() et Printf()
sont appelés :
/* gcc -Wall -noixemul printf2.c -o printf2 -lamiga */
#include <stdlib.h>
#include <stdio.h>
#include <dos/dostags.h>
#include <proto/dos.h>
void MaTache(void);
int main(void)
{
struct Process *proc = NULL;
proc = CreateNewProcTags(NP_Entry, MaTache, // Point d'entrée du Process
NP_Name, "Mon Process", // Nom du Process
#ifdef __MORPHOS__
NP_CodeType, MACHINE_PPC,
#endif
TAG_DONE);
Delay(50); // On n'a pas interet a terminer le pere avant le fils, sinon, c'est le plantage
// (entres autres choses, les flux stdin/stdout seront fermes alors que le process fils s'en sert :( )
// C'est un contournement, ne faites pas ca dans vos programmes ;)
exit(EXIT_SUCCESS);
}
void MaTache(void)
{
printf("printf : Salut les codeurs !\n");
Printf("Printf : Salut les codeurs !\n");
}
|
Lancement depuis Shell et Workbech : seul le message "printf" est affiché, car pr_CIS/pr_COS du processus
fils ne sont pas définis (NULL), alors que stdin/stdout reste le même pour le Process fils et le Process père...
Soit le même programme "printf3.c" avec l'initialisation de pr_CIS/pr_COS pour le Process lancé par CreateNewProcTags() :
/* gcc -Wall -noixemul printf3.c -o printf3 -lamiga */
#include <stdlib.h>
#include <stdio.h>
#include <dos/dostags.h>
#include <proto/dos.h>
void MaTache(void);
int main(void)
{
struct Process *proc = NULL;
proc = CreateNewProcTags(NP_Entry, MaTache, // Point d'entree du Process
NP_Name, "Mon Process", // Nom du Process
NP_Input, Input(), // Mise a jour du flux pr_CIS pour le processus fils avec celui du pere
NP_CloseInput, FALSE, // Fermeture du flux a la fin du process fils : on ne ferme pas, sinon ça ferme aussi le flux du process pere
NP_Output, Output(), // Mise a jour du flux pr_COS pour le processus fils avec celui du pere
NP_CloseOutput, FALSE, // Fermeture a la fin du process fils : on ne ferme pas, sinon ça ferme aussi le flux du process pere
#ifdef __MORPHOS__
NP_CodeType, MACHINE_PPC,
#endif
TAG_DONE);
Delay(50); // On n'a pas interet a terminer le pere avant le fils, sinon, c'est le plantage
// (entres autres choses, les flux stdin/stdout seront fermes alors que le process fils s'en sert :( )
// C'est un contournement, ne faites pas ca dans vos programmes ;)
exit(EXIT_SUCCESS);
}
void MaTache(void)
{
printf("printf : Salut les codeurs !\n");
Printf("Printf : Salut les codeurs !\n");
}
|
Lancement depuis le Shell et le Workbench : affichage de "printf" et "Printf". Chouette, hein ?
Les fonctions InPut() et OutPut() de la dos.library retournent les champs pr_CIS/pr_COS du processus
père (tels qu'initialisés). CreateNewProcTags() utilise alors ces valeurs pour initialiser les champs
pr_CIS/pr_COS du processus fils.
Lancement d'un "Process-exécutable"
Ce chapitre décrit le lancement d'un Process-exécutable, et met en évidence les différences avec le
lancement d'un Process-fonction.
Soit le simple programme suivant, printf6.c, qui lance printf1 :
/* gcc -Wall -noixemul printf6.c -o printf6 -lamiga */
#include <stdlib.h>
#include <stdio.h>
#include <
#include <proto/dos.h>
#include <proto/exec.h>
int main(void)
{
BPTR seg_list = NULL;
struct Process *proc = NULL;
// Chargement de l'executable en memoire : printf1 qui affiche des messages avec Printf() et printf()
// -------------------
seg_list = LoadSeg("printf1");
if(seg_list != 0)
{
// Execution de la tache printf1
// -------------------
proc = CreateNewProcTags(NP_Seglist, seg_list,
NP_Name, "Tache printf1",
//NP_Output, Output(),
//NP_CloseOutput, FALSE,
NP_Cli, TRUE,
NP_Arguments, "",
#ifdef __MORPHOS__
NP_CodeType, MACHINE_PPC,
#endif
TAG_DONE);
Delay(50); // On n'a pas interet a terminer le pere avant le fils, sinon, c'est le plantage
// (entres autres choses, les flux stdin/stdout seront fermes alors que le process fils s'en sert :( )
// C'est un contournement, ne faites pas ca dans vos programmes ;)
UnLoadSeg(seg_list);
}
exit(EXIT_SUCCESS);
}
|
Sous Shell et Workbench : il n'y a aucun affichage :(.
De la même manière que pour l'exemple printf2, il n'y pas de "Printf" car pr_CIS/pr_COS du processus fils
ne sont pas initialisés. Par contre, contrairement à l'exemple printf2, stdin/stdout du processus fils ne
sont pas identiques à ceux du processus père, puisqu'il s'agit du lancement d'un Process-exécutable qui
initialise lui-même stdin/stdout. Comme stdin/stdout du processus fils sont initialisés à partir de pr_CIS/pr_COS
du processus fils (ici NULL), il n'y a pas de message "printf". Si on décommente les lignes NP_Output et NP_CloseOutput,
on obtient les affichages... :).
Modifier la valeur des flux
Ce chapitre décrit une manière de changer dans ses applications stdin/stdout et pr_CIS/pr_COS.
Soit le simple programme printf4.c :
/* gcc -Wall -noixemul printf4.c -o printf4 -lamiga */
#include <stdlib.h>
#include <stdio.h>
#include <dos/dostags.h>
#include <
void MaTache(void);
int main(void)
{
struct Process *proc = NULL;
proc = CreateNewProcTags(NP_Entry, MaTache, // Point d'entrée du Process
NP_Name, "Mon Process", // Nom du Process
NP_Input, Input(), // Mise a jour du flux pr_CIS pour le processus fils avec celui du pere
NP_CloseInput, FALSE, // Fermeture a la fin du process fils : on ne ferme pas, sinon ça ferme aussi le flux du process pere
NP_Output, Output(), // Mise a jour du flux pr_COS pour le processus fils avec celui du pere
NP_CloseOutput, FALSE, // Fermeture a la fin du process fils : on ne ferme pas, sinon ça ferme aussi le flux du process pere
#ifdef __MORPHOS__
NP_CodeType, MACHINE_PPC,
#endif
TAG_DONE);
Delay(200); // On n'a pas interet a terminer le pere avant le fils, sinon, c'est le plantage
// (entres autres choses, les flux stdin/stdout seront fermes alors que le process fils s'en sert :( )
// C'est un contournement, ne faites pas ca dans vos programmes ;)
exit(EXIT_SUCCESS);
}
void MaTache(void)
{
BPTR con_desc = NULL;
BPTR old_output = NULL;
con_desc = Open("CON:80/450/798/250/Alister Output/CLOSE/AUTO", MODE_READWRITE); // Ouverture d'une premiere console
if(con_desc == NULL)
{
printf("cannot open CON:\n");
return;
}
old_output = SelectOutput(con_desc); // Association du flux de sortie de la dos.library (pr_COS) avec la premiere console
freopen("CON:80/450/798/250/Alister Output/CLOSE/AUTO", "w", stdout); // Ouverture d'une deuxieme console, et association avec stdout
printf("printf : Salut les codeurs !\n");
Printf("Printf : Salut les codeurs !\n");
Delay(100);
fclose(stdout);
stdout = NULL;
if(old_output != NULL) (void)SelectOutput(old_output); // Bien remettre l'ancien flux qui sera ferme par le process pere lors de sa terminaison
if(con_desc != NULL) Close(con_desc);
}
|
Sous Shell et Workbench : affichage de "printf" et "Printf" dans deux consoles séparées... C'est drôle, hein ?
Une variante aurait pu être d'ouvrir les nouveaux fichiers de sortie dans le processus père, et de passer
en paramètre à la création du processus fils la nouvelle sortie dos.library.
Cet exemple montre également qu'il peut être dangereux de jouer avec stdout/stdin dans des applications
multitâches, car ces fichiers d'entrées/sorties sont utilisés au niveau "application", et pas "Process".
|