Programmation système
Module OS
Le module OS (Operating System, système d’exploitation en français) permet de manipuler des informations système. Pour avoir accès à cette librairie de la bibliothèque standard, il faut réaliser son importation :
import os
Cette bibliothèque possède différentes fonctionnalités que nous allons passer en revue.
Elle permet de reconnaître le répertoire courant grâce à la méthode getcwd() :
In [3]: os.getcwd()
Out[3]: '/home/pi/Documents/Redaction/PythonRaspberry/Chap8/script'
La méthode listdir() liste le répertoire courant :
In [4]: os.listdir()
Out[4]:
['__pycache__',
'client.py',
'instruction_system.py',
'joblib.py',
'intercep_signal.py',
'threading.py',
'pararallel_joblib.py',
'.ropeproject',
'serveur.py',
'obtenir_arguments2.py',
'parallel_multiprocessing.py',
'obtenir_arguments.py']
Il est possible également de créer des répertoires avec makedirs(). L’exemple qui suit crée le répertoire tmp :
In [5]: os.makedirs('tmp')
Pour vérifier que le répertoire tmp a bien été créé, utilisons l’instruction...
Flux standards
Les flux standards ont été manipulés tout au long de cet ouvrage, grâce aux fonctions print() et input().
Les flux standards sont en Python au nombre de trois :
-
l’entrée standard
-
la sortie standard
-
l’erreur standard
L’entrée standard est utilisée lors de l’appel de la fonction input(), elle sert donc à transmettre des informations entre l’utilisateur et le système. Par défaut, l’entrée standard est le clavier.
De la même manière, la sortie standard permet de véhiculer des informations entre le système et l’utilisateur. Par défaut, ce flux est dérivé vers l’écran de l’utilisateur.
L’erreur standard est utilisée pour propager l’information lors d’une erreur, d’un crash de votre code. Ce flux est également redirigé vers l’écran de l’utilisateur.
Même si les flux possèdent un état par défaut, il est possible de les personnaliser. Par exemple, au lieu d’avoir la redirection de la sortie standard vers l’écran, il est possible de rediriger ce flux vers un fichier.
Les flux se manipulent grâce à la librairie sys que nous importons de la manière suivante :
import sys
Pour écrire quelque chose dans la sortie standard stdout, nous utilisons...
Signaux
Les signaux sont des moyens dont dispose le système pour communiquer avec les applications. Les signaux peuvent également être utilisés pour faire communiquer les différents programmes et réaliser des synchronisations.
Il est possible en Python d’intercepter des signaux pour réaliser certaines tâches spécifiques : nettoyage d’un log, exécution d’un programme particulier, sauvegarde d’une configuration…
Afin d’illustrer le procédé, nous allons intercepter le message de fermeture du programme SIGINT (SIGINT est un message transmis par le système d’exploitation à certaines applications pour signifier qu’une tâche va être interrompue).
Pour cela, il faut créer une fonction qui sera appelée lorsque nous intercepterons le message :
def reaction_intercept(signal, frame):
print("Message intercepté \n")
print("Le script sera fermé dans 5 sec\n")
time.sleep(5)
print("Fermeture")
sys.exit(0)
Cette fonction est très simple, puisqu’elle consiste en l’affichage d’un message et une pause de 5 secondes. Puis, nous sortons explicitement du programme avec sys.exit(0). Le paramètre 0 indique au système que la sortie s’est réalisée...
Interpréter les arguments de la ligne de commande
Un script Python est appelé par l’utilisateur
-
Soit par le biais de l’interpréteur :
python mon_script.py
-
Soit avec l’interpréteur interactif IPython :
ipython mon_script.py
-
Soit dans un environnement interactif IPython :
%run mon_script.py
Il est possible de passer des arguments en ligne de commande afin de donner des valeurs nécessaires à l’exécution d’un programme : elles influeront sur son comportement, déclencheront différentes actions/fonctions.
Pour cela, nous disposons de la librairie sys :
import sys
Les arguments passés au moment de l’exécution du script sont accessibles via la variable argv :
sys.argv
Il s’agit ni plus ni moins de la liste des arguments. Voici un premier script qui retourne des informations sur la liste des arguments :
import sys
print("Le premier argument est le nom du script: {}".format(sys.argv[0]))
print("Le nombre d'arguments est: {}".format(len(sys.argv)))
print("Et ces arguments sont: {}".format(str(sys.argv)))
Appelons le script de la manière suivante :
obtenir_arguments.py arg1 2 arg3
Nous obtenons le résultat suivant :
In [3]: %run obtenir_arguments.py arg1 2 arg3
Le premier argument est le nom du script: obtenir_arguments.py ...
Exécuter une commande système
Un autre usage utile de Python est de réaliser des scripts pour l’administration système. Il est courant d’appeler des commandes système afin de bénéficier de leur fonctionnalités sans avoir à recoder.
On utilise donc les commandes système dans un but de capitalisation ou pour simplement gagner du temps de conception et développement.
Pour utiliser les commandes système, il faut importer la librairie os.
Import os
L’appel des fonctions se fait à l’aide de system(). Par exemple, pour lister un répertoire, il suffit de passer en paramètre une chaîne de caractères qui contient l’instruction ls :
os.system("ls")
Ce qui produit le résultat :
In [6]: os.system("ls")
intercep_signal.py obtenir_arguments2.py obtenir_arguments.py
Out[6]: 0
Il est également possible d’ajouter des paramètres à Is, par exemple ls -la :
In [7]: os.system("ls -la")
total 24
drwxr-xr-x 3 pi pi 4096 mars 21 06:56 .
drwxr-xr-x 3 pi pi 4096 mars 21 07:07 ..
-rw-r--r-- 1 pi pi 364 mars 20 23:35 intercep_signal.py
-rw-r--r-- 1 pi pi 544 mars 21 06:56 obtenir_arguments2.py
-rw-r--r-- 1 pi pi 207 mars 21 06:25 obtenir_arguments.py
drwxr-xr-x 2 pi pi 4096 mars 20...
Réseau
Python possède dans sa bibliothèque standard des objets permettant de manipuler des services et technologies réseau de manière simple. Ceci est intéressant pour l’utilisateur qui voudrait faire communiquer différents Raspberry Pi disposés au sein d’un même réseau.
Imaginons un jardin équipé de plusieurs capteurs d’humidité. Chaque capteur est relié à un Raspberry Pi. Afin de récupérer l’information de tous les capteurs, un PC maître va accéder aux différents capteurs à l’aide du réseau. Pour réaliser cette tâche, le plus simple est d’utiliser des sockets.
Un socket est un objet qui permet de connecter un client et un serveur selon un protocole spécifié qui est généralement TCP ou UDP et qui permet donc in fine d’échanger des informations.
Pour illustrer le fonctionnement de cette structure client-serveur, nous allons nous placer dans le cas où il y a un seul client et un seul serveur.
Dans une structure client-serveur, le client est disponible à la connexion d’un client. Il est donc en attente d’une requête de connexion. Dès qu’une requête de connexion est demandée, il accepte la requête du client après une éventuelle identification du client. Dès que la connexion est établie avec le client, des données peuvent être échangées avec celui-ci. Le serveur finira sa tâche par une fermeture de connexion.
Pour ce qui est du client, il se connecte au serveur et, après acceptation de sa demande de connexion, il peut échanger des informations avec le serveur, puis fermer sa connexion.
Pour illustrer ce fonctionnement, nous allons utiliser le principe en local sur un Raspberry Pi qui possède à la fois le client et le serveur.
Nous nous appuierons sur TCP, qui reste un protocole standard, en nous aidant d’un socket.
Commençons par importer la librairie socket :
import socket as skt
Ensuite, créons un socket à proprement parler :
s_connexion = skt.socket(skt.AF_INET, skt.SOCK_STREAM)
Deux paramètres sont utilisés pour...
Programmation multithreading et calcul parallèle
La grande majorité des processeurs sont multithreading, Ils possèdent même dans la plupart des cas plusieurs cœurs, on parle alors de processeur multicœur. C’est le cas du Raspberry Pi 3 B+ qui possède quatre cœurs.
Cette évolution des processeurs aboutissant à des capacités de calculs multitâches importantes est le fruit de l’évolution du matériel informatique. En effet, dans le milieu des années 2000, la montée en fréquence des processeurs s’est ralentie, voire stoppée pour des raisons purement technologiques. Il était devenu très difficile de concevoir des processeurs toujours plus rapides avec des technologies permettant de proposer des prix intéressants pour le grand public.
La solution trouvée par les acteurs du secteur a été de paralléliser le calcul en ajoutant la possibilité d’exécuter toujours plus de tâches en parallèle, soit en utilisant une taille de pipeline plus importante, soit, plus récemment, en utilisant plusieurs cœurs au sein d’un processeur. Ces cœurs pouvant communiquer les uns avec les autres, bien évidemment.
En Python, il existe deux façons d’exploiter l’architecture multitâche du processeur. La première, en exécutant des tâches en parallèle grâce aux threads. La seconde en utilisant une librairie qui envoie sur chaque processeur ou cœur (selon l’architecture) une partie des calculs.
1. Utiliser les threads
La création d’un thread (une tâche) en Python est assez concise. Il y a a minima trois étapes.
-
l’importation du module nécessaire :
from threading import Thread
-
la création d’un thread :
t = Thread(target=worker, args=(i,))
-
le démarrage d’un thread :
t.start()
Voici un exemple complet :
import random
import time
from threading import Thread
def worker(i):
temps = random.randrange(5)
print("Le thread {} dormira {} sec".format(i, temps))
time.sleep(temps)
print("Le thread {} a fini de dormir".format(i)) ...
Conclusion
Ce chapitre a fait le tour des éléments système permettant d’interagir avec Raspbian à l’aide de Python.
Les éléments détaillés ici permettent de manipuler facilement la structure de dossiers et de fichier du système d’exploitation du Raspberry Pi. L’utilisation des sockets vous permet de réaliser rapidement des applications client-serveur basées sur le protocole TCP.
Enfin, différentes méthodes de gestion des tâches concurrentes ont été passées en revue. Ainsi, les multithreading et le calcul parallèle pourront être implantés au besoin dans vos applications.