Accueil
Java Standard Edition
Java EE 5
Visual Basic .Net 2005
Visual C++ .Net 2005
Visual C# .Net 2005
Cours ASP .Net 2.0
Postgresql
Linux
Visual Studio 2008
ASP 3.0 Classique
Cours Javascript - DOM - DHTML
Cours Ajax
VBA
Assembleur
Perl
Membres
L'auteur du site
Nouveautés sur le site
Contacts
Plan du site
Exécution d'un programme
Les archives Jar
Les classes abstraites
Les interfaces
Les tableaux
La généricité
Les énumérations
Les classes internes
Classes anonymes et internes locales
E/S(1):InputStream et OutputStream
E/S(2):FileInputStream et FileOutputStream
E/S(3):Reader et Writer
E/S(4):FilterInputStream et FilterOutputStream
E/S(5):Les filtres d'octets: PrintStream
E/S(6):Les filtres d'octets: DataInputStream et DataOutputStream
E/S(7):Les filtres d'octets: BufferedInputStream et BufferedOutputStream
E/S(8):Flux de caractères: PrintWriter
E/S(9):Flux de caractères: FilterReader et FilterWriter
E/S(10):Flux de caractères: InputStreamReader, OutputStreamWriter, StreamDecoder, StreamEncoder
E/S(11):Flux de caractères: BufferedReader et BufferedWriter
E/S(12):Flux de caractères: FileReader et FileWriter
La classe String (java.lang)
Les collections: L'interface Collection(java.lang)
Les collections(2): L'interface List(java.util)
Les collections(3): AbstractCollection(java.util)
Les collections(4): AbstractList(java.util)
La bibliothèque Swing en Java
Les bases de données en Java
JDBC ( Java Database Connectivity )
Les interfaces graphiques
Les fichiers de configuration en Java
INSTALLATION JAVA EE 5, JRE 6, ECLIPSE, TOMCAT, ETC SOUS LINUX
INSTALLATION JAVA EE 5, JRE 6, ECLIPSE, TOMCAT, ETC SOUS WINDOWS
Les applications Web en java
Les filtres Java (javax.servlet.Filter)
I Généralités
I.1 Le formulaire principal
I.2 Les objets créés par Visual
I.3 Les variables références
I.4 Le garbage collector
II Créer évènements
II.1 Rappel évènements
II.2 Procédure à suivre
II.2.1 Créer son EventArgs
II.2.2 Créer EmetEvent
II.2.3 Déclarations autres
I Généralités
I.1 Applications winforms
I.2 Applications MFC
I.3 Objets managés ou pas
I.4 Objets non managés
I.5 Objets managés - handle
I.6 Le top-level ^
II Créer évènements
II.1 Rappel évènements
II.2 Procédure à suivre
II.2.1 Créer son EventArgs
II.2.2 Créer EmetEvent
II.2.3 Déclarations autres
I Généralités
I.1 Puissant et Accessible
I.2 Créer ses classes
II Créer évènements
II.1 Rappel évènements
II.2 Procédure à suivre
III Les services Windows
IV Le .net remoting
V Communication Tcp avec TcpClient et TcpListener
II.2.1 Créer son EventArgs
II.2.2 Créer EmetEvent
II.2.3 Déclarations autres
I Généralités
I.1 Un EDI formidable
I.2 Inclure C# ou VB
I.3 L'objet Response
I.4 Les évènements
II ASP .net et les bdd
II.1 Essayer plusieurs fois la requête
I 2.1 Fichiers distincts
I.2.2 Avec la balise script
I.2.3 Inclure réellement
I.2.4 Avec Response.Write()
I.3.1 La méthode Response.Redirect()
I.4.1 Résoudre problème post
Installation Postgre Linux
Cours Postgresql
Le Shell Unix( Linux, Ubuntu)
Les scripts C-Shell
Programmation système Unix
Reseau Linux
Les iptables
Windows Presentation Foundation(WPF)
Le Framework 3.0
Windows Workflow Foundation(WF)
ASP 3.0 Classique
Cours Javascript - DOM - DHTML
Chat Ajax
VBA Excel 2003
Assembleur
Perl
Inscription
Liste membres
Livre d'or
Forum
Accueil
>
Linux
>
Programmation système Unix
____________________________________________________________________________________________________
Connexion
LA PROGRAMMATION SYSTEME UNIX
testé sur linux Fedora Core 8
26/12/2007
I) Installation des outils de compilation
I.1) Fedora Core 8
su
yum groupinstall « Development Tools »
installe le groupe de paquetages de développement( 47 paquetages lors de mon exécution , 67M en tout), dont gcc, make, etc, et pas seulement pour le langage c et c++.
Autre possibilité : paquetage par paquetage, par exemple
yum install gcc
I.1.b) Compilation
Ecrivez le petit programme suivant, par exemple avec gedit :
#include <stdio.h>
int main( ) /* on ne peut pas mettre void sous unix, le compilateur ne le prendra pas */
{
printf( «Hello World\n») ;
return 0 ;
}
puis
g++ -c test.c
remarquez que sans l’option –c, le .o n’est pas produit
En mettant l’option –c, on dit explicitement ce qu’on veut. Il est vrai qu’on
aurait pu le déduire du fait de l’extension .c, mais n’est-ce pas mieux d’exprimer
explicitement ce qu’on veut ? De plus, si on se sert du .c pour détecter ce qu’on veut
faire, ça oblige à réserver l’extension .c uniquement aux programmes c.
On obtient test.o
Puis
g++ -o test test.o
Il est logique de préciser pour quels fichiers .o on veut faire l’édition des liens.
En effet, imaginez que vous ayez d’autres .o dans le même répertoire !
Il est logique aussi de préciser le nom de l’exécutable qu’on veut donner.
Le fichier exécutable « test » est alors généré. Il est en vert clair lorque vous
faites un ls( car c’est un fichier exécutable).
Pour l’exécuter, faites ./test( et non pas « test », qui ne marche pas).
« Hello World » s’affiche alors.
Remarque : on aurait pu faire directement, en une ligne :
g++ -o test test.c
g++ sera alors capable se débrouiller pour comprendre qu’il faut compiler le .c pour obtenir test.o
- Compilation avec makefile : ceci est un exemple du makefile correspondant :
#26/12/2007 Exemple de makefile qui marche.
#A sauvegarder sous le nom "makefile". Le mettre dans le même répertoire que le
.c. Puis faire "make", ou alors « make --makefile nomDuMakefile » si votre makefile
ne s’appelle pas makefile.
#Si on veut : compilateur c: CC = g++, mais sert à rien ici, car on a précisé g++
testGeneralC: testGeneralC.o
g++ -o testGeneralC testGeneralC.o #mettre une tabulation, sinon il fait une erreur
"séparateur manquant"
testGeneralC.o: testGeneralC.c
g++ -c testGeneralC.c #mettre une tabulation, sinon il fait une erreur "séparateur manquant"
II)LES PROCESSUS
II.1) L’état d’un processus
Un processus Unix, comme chacun d’entre nous( et comme tout ce qui est vivant, d’ailleurs) se trouve toujours dans un certain état.
Les différents états:
Nouveau : le processus est en train de se créer
Elu : le processus est en cours d’exécution
En attente : le processus attend qu’un évènement(au sens large, peut être la réception d’un signal) se produise.
Prêt : le processus attend d’être exécuté par un processeur
Terminé : l’exécution du processus est terminée
a)L’ordonnanceur( sheduler)
L’ordonnanceur( scheduler) est au niveau le plus bas du système. C’est lui qui gère l’exécution des processus, selon leur priorité, etc
II.2)Les fonctions systèmes sur les processus
a) pid_t getpid( void) / unistd.h
#include <unistd.h>
pid_t getpid( void)
Description: retourne le pid( qui est l’identifiant du processus, c’est le numéro du processus) du processus courant.
Le type pid_t est en réalité un type numérique, _t comme type. Cela apporte de l’abstraction( un peu comme la programmation objet), et de la sécurité.
Exemple de programme qui retourne le pid du processus(du programme):
//26/12/07 Sabri Koffler
//But: affiche le pid du processus
#include <unistd.h>
#include <iostream.h> //pour le cout
int main()
{
pid_t pid;
pid = getpid();
cout<<"Le pid du processus est "<<pid<<"\n";
return 0;
}
b) pid_t getppid( void) / unistd.h
#include <unistd.h>
pid_t getppid( void)
Description: retourne le pid( qui est l’identifiant du processus, c’est le numéro du processus) du processus père.
Peut être utile quand on est dans un processus fils créé par un fork, et qu’on veut le pid du père.
c) char * getcwd( char *buf, size_t n) / unistd.h
#include <unistd.h>
char * getcwd( char *buf, size_t n)
buf est une chaine de n caractères qui contiendra le répertoire courant ( chemin absolu).
Description: Retourne le répertoire courant.
Cela prouve qu’un processus est rattaché à une console, à un terminal. Car comme plusieurs terminals peuvent être ouverts, il y aurait imprécision pour savoir de quel terminal on parle. Ici, pas d’ambiguëté, car on sait qu’il s’agit du terminal rattaché au processus, c'est-à-dire le terminal père du processus. On peut imaginer une fenêtre terminal comme une machine qui exécute un linux. Donc notre programme a été lancé par une « machine » linux. Il est donc normal que getcwd retourne le répertoire de travail de cette « machine » linux !
Remarque : getcwd ne fait pas d’allocation mémoire. Elle demande un buffer prêt à recevoir le résultat. Si le buffer n’est pas assez grand, elle retourne NULL. A nous de tester éventuellement cette valeur de retour, et de réessayer avec un buffer plus grand si problème.
d)int chdir( const char * ref) (non testé)
#include <unistd.h>
int chdir( const char * ref) //A remarquer le const pour sécuriser, car le nom du répertoire est en entrée seulement
Description: Changement du répertoire courant, c’est-à-dire équivalent à la commande cd.
Cela prouve aussi qu’un processus est rattaché à une console, à un terminal.
e) pid_t fork(void) / unistd.h
pid_t fork(void) / unistd.h
Retourne -1 si le processus n’a pas pu être créé
pid du nouveau processus si on est dans le père
0 si on est dans le processus fils
Description : Dès qu’un fork est rencontré lors de l’exécution,
*création d’un processus jumeau
*ce processus jumeau ne s’exécute pas au début : il s’exécute « après »( mais en tenant compte quand même du résultat du fork) le fork. D’ailleurs impossible que ce soit juste avant fork, car ca créerait des processus en boucle infinie !!! Le fork en question, est donc présent dans le processus père, et dans le processus fils. La différence, c’est que ce fork va retourner une valeur différente, suivant qu’on se trouve dans le père, ou dans le fils. Grâce à cette valeur, on va pouvoir savoir, dans chacun des 2 processus, dans quel processus on est.
Le fork( ) retourne 0 s’il est dans le fils. Il retourne le pid du fils, quand il est dans le père.
Le père et le fils exécutent le même code en mémoire. Les données du fils sont une copie des données du père, c’est-à-dire qu’elles lui sont propres.
ea) Intérêt du fork
L’intérêt du fork est de faire de la programmation parralèle. Schéma classique pour faire 2 processus qui tournent en parallèle :
if (fork( ) !=0) {
TraitPere( ) ;
}
else
{
TraitFils( );
}
exit(0) ; // dans stdlib.h
void TraitPere( )
{
…
}
void TraitementFils
{
…
}
f) pid_t wait( int * pointeur_status) / <sys/types.h > <sys/wait.h>
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait( int * pointeur_status);
retourne -1( et errno = ECHILD) si le processus ne possédait aucun fils, ou sinon retourne le pid du fils zombi
status : si on lui a donné une valeur différente de NULL, contiendra des informations sur la façon dont le processus fils s’est terminé.
Description : wait suspend l’exécution du processus en cours jusqu’à ce qu’un des enfants se termine.Un processus est zombi jusqu’à ce que son père apprenne sa terminaison.
Donc le processus père se retrouve en état d’attente(mieux qu’une boucle d’attente), il attend qu’un de ses fils se termine( c’est-à-dire qu’un de ses fils devienne zombi). Le fils qui est terminé peut alors sortir de son état zombi, et se terminer réellement. Il disparaît des tables du système, et toutes les ressources utilisées par ce fils sont libérées. Le wait est donc synchrone par défaut.
Explication: en programmation multitâche, on a certes besoin de savoir faire tourner deux processus en même temps, et c'est ce que c'est faire le fork. Mais on peut avoir aussi un besoin de synchroniser ces processus, c'est à dire que l'un attend l'autre. Les fonctions wait et waitpid répondent à ce besoin.
Avoir une fonction qui est capable d'attendre qu'un certain processus est terminé, c'est suffisant pour répondre à tous les besoins de synchronisation. En effet, si on veut que le processus A attende un traitement B qui doit s'effectuer parallèlement à son traitement A, on n'a juste qu'à placer le traitement B dans un processus fils parallèle, et à faire un wait dans le processus A!
fa) Intérêt du wait
- Un intérêt pour le père de tuer tous les fils avant de finir lui-même. Faire une boucle si plusieurs fils :
while ( wait( &status) != -1) ;
Remarque : à mons avis, tous les processus zombi fils sont tués quand le père se termine, même si on n’a pas fait de wait dans le père.
- Un intérêt si on veut faire une synchronisation en temps du père
Par exemple, le père fait d’abord un traitement 1, puis doit faire un traitement 2 quand le fils a terminé son traitement. Ici, faire
FoncPere
{
Traitement 1 ;
wait( ) ; /* mieux gérer si on veut, avec le -1 etc */
Traitement 2 ;
}
- Un intérêt du wait : si le père finit, il va tuer tous ses fils, même s’ils ne sont pas zombi !. Donc indispensable le wait ici.
g) pid_t waitpid( pid_t pid, int * pointeur_status, int option) / <sys/types.h> <sys/wait.h>
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid( pid_t pid, int * pointeur_status, int option) ;
retourne :
-1 : en cas d’erreur, en mode bloquant ou non bloquant
0 : en cas d’échec, c'est-à-dire, si on se trouve en mode non bloquant, que le processus demandé existe, mais n’est pas terminé.
pid : le numéro du processus zombi fils dont il était question
Le paramètre pid :
Pid interprétation
<-1 tout processus fils dans le groupe |pid|
-1 tout processus fils( identique au wait classique)
0 tout processus fils dans le même groupe que le processus faisant le wait
>0 processus fils ayant comme identité pid
Option Signification
WNOHANG le processus du wait n’est pas bloqué, mode non bloquant
WUNTRACED le processus du wait est bloqué, mode bloquant
Description : c’est un wait étendu. On peut choisir un pid particulier à attendre, c'est-à-dire un processus particulier à attendre. Ou bien on peut choisir un groupe particulier de processus( tout processus de ce groupe, dans ce cas). On peut même obtenir le même comportement que le wait, si on veut. Le choix du processus à attendre est donc très large, et ce choix s’effectue au moyen du paramètre appelé pid ci-dessus.
Une autre particularité du wait_pid est qu’on peut choisir si on est en mode synchrone ou asynchrone. Le mode synchrone est comme le wait classique, c'est-à-dire on attend jusqu’à obtention de ce qu’on veut, c’est le mode bloquant. Le mode asynchrone est le mode non bloquant : on n’attend pas que le processus pid devienne zombi.
II.3) Les tubes de communication
II.3.a) Généralités
Nous avons déjà vu deux besoins de la programmation en parallèle :
- pouvoir exécuter deux processus en même temps. Le fork répond à cela
- pouvoir synchroniser les processus qui tournent en même temps : le wait répond à cela
Mais nous avons encore un besoin : les processus parallèles doivent pouvoir avoir des ressources communes. Sinon ce serait deux processus complètement indépendants, ce qui est rarement le cas. Permettre à deux processus de communiquer, c'est-à-dire à un processus de donner une information à un autre processus, est un moyen de répondre au besoin des ressources communes. Les tubes de communication permettent de remplir ce rôle.
Les tubes de communication
2 types de tubes sous unix :
- les tubes ordinaires
- les tubes nommés
Pour les 2 types :
* Ce mécanisme appartient au système de gestion des fichiers
. On désigne le tube par un descripteur
. Manipulation par read et write.
* Communication unidirectionnelle( 1 entrée et 1 sortie). On écoute à l’entrée et on lit à la sortie.
* Une information ne peut être lue qu’une seule fois.
* Communication en stream( flot continu de caractères)
* En réalité, deux flux à l’intérieur du tube : un flux écriture, et un flux lecture. Car la lecture d’une information n’a pas forcément lieu au même moment qu’elle est écrite( sinon un seule flux suffirait). C’est une lecture en différé.
* Capacité finie : tube peut être plein.
* Fifo
* Un tube peut avoir plusieurs lecteurs et plusieurs écrivains.
Si le nombre de lecteurs=0 alors pas d’écriture. Si le nombre d’écrivains=0, alors fin de fichier.
II.3.b) Les tubes ordinaires
- un fichier = un nœud dans le système de gestion des fichiers.
- Un tube ordinaire n’a pas de nom : open impossible, on le voit pas avec ls –l, etc.
Le descripteur ne peut être obtenu que de 2 façons :
. appel à la fonction pipe
. par héritage
Les processus qui veulent communiquer par tube doivent :
. être un descendant du créateur du tube( du processus créateur du tube)
. connaître les descripteurs du tube( donc les processus doivent
être créés après le tube). Le père a le droit d’utiliser un tube créé par lui, d’après exemple.
II.3.b.a) Création tube ordinaire
#include <unistd.h>
int pipe (int p[2]) ;
un tube possède 2 entrées dans la table des fichiers ouverts:
p[0] : decripteur en lecture
p[1] : descripteur en écriture
pipe retourne 0 si le tube a été créé, -1 sinon.
II.3. b.b) Lecture (tubes ordinaires)
Lecture des caractères que le tube contient.
#include <stdio.h>
int read( int desc, char * buf, int n) ;
desc: descripteur du fichier en lecture( p[0])
buf : pointeur sur un tableau de caractères
n : nombre de caractères à lire
read renvoie le nombre de caractères lus, qui est taille buffer si taille buffer <n.
La lecture est bloquante par défaut, le processus est mis en sommeil ( jusqu’à qu’il ait eu ce qu’il voulait ).
La lecture peut être non bloquante si l’indicateur O_NONBLOCK est positionné.
- Situations d’inter-blocage possible : un processus bloqué est réveillé seulement si un écrivain se met à écrire(remarque : « inter » pas justifié ici). Vrai situation d’interblocage possible : 2 processus attendent pour renvoyer à l’autre, 2 pipes.
II.3. b.c) Ecriture( tubes ordinaires)
#include <stdio.h>
int write (int desc, char * buf, int n) ;
desc: descripteur du tube en écriture( p[1]);
buf : pointeur sur un tableau de caractères
n : le nombre de caractères à écrire. 1 caractère = 1 octet. Donc possibilité d’utiliser sizeof si besoin.
II.3. b.d) Fermeture( tubes ordinaires)
close( p[0]) ; :fermeture du tube en lecture
close( p[1]) ; : fermeture du tube en écriture
Par exemple, un processus utilise un pipe uniquement en écriture : il fait un close( p[0]) ; : il ferme le tube en lecture( il est fermé en lecture seulement pour ce processus).
Autre exemple : un processus ferme le tube en écriture, juste à la fin du processus, juste avant le exit( après avoir fait les write qu’il veut).
RETOUR HAUT