Documentation Perl en français
En installant du code Perl qui traite des signaux, vous vous exposer à deux types de dangers. D’abord, peu de fonctions de la bibliothèque système sont réentrantes. Si le signal provoque une interruption pendant que Perl exécute une fonction (comme malloc(3) ou printf(3)), et que votre handler de signal appelle alors la même fonction, vous pourriez obtenir un comportement non prévisible – souvent, un core dump. Ensuite, Perl n’est pas lui-même réentrant aux niveaux les plus bas. Si le signal interrompt Perl pendant qu’il change ses propres structures de données internes, un comportement non prévisible similaire peut apparaître.
Vous pouvez faire deux choses, suivant que vous êtes paranoïaque ou pragmatique. L’approche paranoïaque est d’en faire aussi peu que possible dans votre handler. Fixez une variable entière existant et ayant déjà une valeur et revenez. Ceci ne vous aide pas si vous êtes au milieu d’un appel système lent, qui se contentera de recommencer. Cela veut dire que vous devez mourir avec die pour sauter (longjump(3)) hors du handler. Même ceci est quelque peu cavalier pour le véritable paranoïaque, qui évite die dans un handler car le système est là pour vous attraper. L’approche pragmatique consiste à dire « je connais les risques, mais je préfère la facilité », et ensuite faire tout ce qu’on veut dans le handler de signal, en étant prêt à nettoyer les coredumps de temps en temps.
Interdire totalement les handlers interdirait aussi beaucoup de programmes intéressants, y compris virtuellement tout ce qui se trouve dans cette page de manuel, puisque vous ne pourriez plus écrire ne serait-ce qu’un handler SIGCHLD. Ce problème épineux devrait être réglé dans la version 5.005.
Utilisation de open() pour la CIP
l’instruction open() de base en Perl peut aussi être utilisée pour la communication inter-processus unidirectionnelle en ajoutant un symbole de tube juste avant ou après le second argument de open(). Voici comment démarrer un processus fils dans lequel vous avez l’intention d’écrire :
open(SPOOLER, « | cat -v | lpr -h 2>/dev/null »)
|| die « can’t fork: $! »;
local $SIG{PIPE} = sub { die « spooler pipe broke » }; print SPOOLER « stuff\n »;
close SPOOLER || die « bad spool: $! $? »;
Et voici comment démarrer un processus fils depuis lequel vous désirez lire :
open(STATUS, « netstat -an 2>&1 | »)
|| die « can’t fork: $! »;
while (<STATUS>) {
next if /^(tcp|udp)/; print;
}
close STATUS || die « bad netstat: $! $? »;
Si l’on peut etre certain qu’un programme specifique est un script Perl que attend des noms de fichiers dans @ARGV, le programmeur futé peut écrire quelque chose comme ceci :
% program f1 « cmd1| » – f2 « cmd2| » f3 < tmpfile
et, indépendemment du shell d’où il est appelé, le programme Perl lira dans le fichier f1, le processus cmd1, l’entrée standard (tmpfile dans ce cas), le fichier f2, la commande cmd2, et finalement dans le fichier f3. Plutôt débrouillard, hein ?
Vous pourriez remarquer que l’on pourrait utiliser des accents graves pour obtenir à peu près le même effet que l’ouverture d’un tube en lecture :
print grep { !/^(tcp|udp)/ } ‘netstat -an 2>&1‘; die « bad netstat » if $?;
Même si ceci est vrai en surface, il est bien plus efficace de traiter le fichier une ligne ou un enregistrement à la fois car alors vous n’avez pas à charger toute la chose en mémoire d’un seul coup. Cela vous donne aussi un contrôle plus fin sur tout le processus, vous laissant tuer le processus fils plus tôt si vous le voulez.
perlipc http://perl.enstimac.fr/DocFr/perlipc.html
Documentation Perl en français 6
Prenez soin de vérifier à la fois les valeurs de retour de open() et de close(). Si vous écrivez dans un tube, vous pouvez aussi piéger SIGPIPE. Sinon, pensez à ce qui se passe lorsque vous ouvrez un tube vers une commande qui n’existe pas : le open() réussira très probablement (il ne fait que refléter le succès du fork()), mais ensuite votre sortie échouera – spectaculairement. Perl ne peut pas savoir si la commande a fonctionné parce que votre commande tourne en fait dans un processus séparé dont l’exec() peut avoir échoué. Par conséquent, ceux qui lisent depuis une commande bidon retournent juste une rapide fin de fichier, ceux qui écrivent vers une commande bidon provoqueront un signal qu’ils feraient mieux d’être préparés à gérer. Considérons :
open(FH, « |bogus ») or die « can’t fork: $! »;
print FH « bang\n » or die « can’t write: $! »;
close FH or die « can’t close: $! »;
Ceci n’explosera pas avant le close, et ce sera sur un SIGPIPE. Pour l’intercepter, vous pourriez utiliser ceci :
$SIG{PIPE} = ’IGNORE’;
open(FH, « |bogus ») or die « can’t fork: $! »;
print FH « bang\n » or die « can’t write: $! »;
close FH or die « can’t close: status=$? »;
Handles de Fichiers
Le processus principal et tous les processus fils qu’il peut générer partagent les mêmes handles de fichiers STDIN, STDOUT et STDERR. Si deux processus essayent d’y accéder en même temps, des choses étranges peuvent se produire. Vous pourriez aussi fermer ou réouvrir les handles de fichiers pour le fils. Vous pouvez contourner cela en ouvrant votre tube avec open(), mais sur certains systèmes cela signifie que le processus fils ne peut pas survivre à son père.
Processus en Arrière-Plan.
Vous pouvez exécuter une commande en arrière-plan avec :
system(« cmd & »);
Les STDOUT et STDERR de la commande (et peut-être STDIN, selon votre shell) seront les mêmes que ceux du père.
Vous n’aurez pas besoin de piéger SIGCHLD à cause du double fork qui se produit (voir plus bas pour plus de détails).
Dissociation Complète du Fils et de son Père
Dans certains cas (démarrage de processus serveurs, par exemple) vous voudrez complètement dissocier le processus fils de son père. Ceci est souvent appelé une démonisation. Un démon bien élevé fera aussi un chdir() vers le répertoire root (pour qu’il n’empêche pas le démontage du système de fichiers contenant le répertoire à partir duquel il a été lancé) et redirige ses descripteurs de fichier standard vers /dev/null (pour que des sorties aléatoires ne finissent pas sur le terminal de l’utilisateur).
use POSIX ’setsid’;
sub daemonize {
chdir ’/’ or die « Can’t chdir to /: $! »;
open STDIN, ’/dev/null’ or die « Can’t read /dev/null: $! »;
open STDOUT, ’>/dev/null’
or die « Can’t write to /dev/null: $! »;
defined(my $pid = fork) or die « Can’t fork: $! »;
exit if $pid;
setsid or die « Can’t start a new session: $! »;
open STDERR, ’>&STDOUT’ or die « Can’t dup stdout: $! »;
}
Le fork() doit venir avant le setsid() pour s’assurer que vous n’êtes pas un leader de groupe de processus (le setsid() échouera si vous l’êtes). Si votre système ne possède pas la fonction setsid(), ouvrez /dev/tty et utilisez dessus l’ioctl() TIOCNOTTY à la place. Voir tty(4) pour plus de détails.
Les utilisateurs non unixiens devraient jeter un oeil sur leur module Votre_OS::Process pour avoir d’autres solutions.
perlipc http://perl.enstimac.fr/DocFr/perlipc.html
Documentation Perl en français 7
Ouvertures Sûres d’un Tube
Une autre approche intéressante de la CIP est de rendre votre simple programme multiprocessus et de communiquer entre (ou même parmi) eux. La fonction open() acceptera un fichier en argument pour « -j » ou « j- » afin de faire une chose très intéressante : elle forkera un fils connecté au descripteur de fichier que vous venez d’ouvrir. Le fils exécute le même programme que le parent. C’est utile par exemple pour ouvrir de façon sécurisée un fichier lorsque l’on s’exécute sous un UID ou un GID présumé. Si vous ouvrez un tube vers minus, vous pouvez écrire dans le descripteur de fichier que vous avez ouvert et votre fils le trouvera dans son STDIN. Si vous ouvrez un tube depuis minus, vous pouvez lire depuis le descripteur de fichier tout ce que votre fils écrit dans son STDOUT.
1 NAME/NOM
2 DESCRIPTION
3 Signaux
4 Tubes nommés
4.1 AVERTISSEMENT
5 Utilisation de open() pour la CIP
5.1 Handles de Fichiers
5.2 Processus en Arrière-Plan
5.3 Dissociation Complète du Fils et de son Père
5.4 Ouvertures Sûres d’un Tube
5.5 Communication Bidirectionnelle avec un autre Processus
5.6 Communication Bidirectionnelle avec Vous-même
6 Sockets : Communication Client/Serveur
6.1 Terminateur de Ligne Internet
6.2 Clients et Serveurs TCP Internet
6.3 Clients et Serveurs TCP du Domaine Unix
7 Clients TCP avec IO::Socket
7.1 Un Client Simple
7.2 Un Client Webget
7.3 Client Interactif avec IO::Socket
8 Serveurs TCP avec IO::Socket
9 UDP : Transfert de Message
10 CIP du SysV
11 NOTES
12 BUGS
13 VOIR AUSSI
14 AUTEUR
15 TRADUCTION
15.1 Version
15.2 Traducteur
15.3 Relecture
16 À propos de ce document