Notes de cours CSC209 : C et programmation
Notes de cours CSC209 : C et programmation
Jenci Wei
Hiver 2022
1
Contenu
1 Entrée Sortie et compilation 5
1.1 Printing Values (printf) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Compiling À partir de la ligne de commande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Reading Entrée (scanf) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
4Functions 12
4.1 Calling une fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.2 Écriture Fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3 Fonction Exécution. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5 itération 14
5.1 Pour Boucles14
5.2 Pendant Boucles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5.3 Pause et Continuer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7 Tableaux 16
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
7.1 Introduction
7.2 Accès Éléments de tableau 16
8 points 18
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
8.1 Introduction
8.2 Attribution à des pointeurs déréférencés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
8.3 Pointers as Parameters to Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
8.4 Pointeur Arithmétique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
8.5 Pointeur aux pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
9 °C Modèle de mémoire 21
9.1 Code et segments de pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
9.2 Tas et Segments Mondiaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Page 2
10 Mémoire Dynamique 22
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
10.1 Introduction
10.2 Libération Mémoire allouée dynamiquement 22
10.3 Retour Une adresse avec un pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
10.4 Imbriqué Structures de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
12 Cords 25
12.1 Introduction.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
12.2 Initialisation Chaînes et littéraux de chaînes .......................... 25
12.3 Taille et Longueur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
12.4 Copie Chaines26
12,5 Concaténation Chansons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
12.6 Recherche Avec des cordes27
13 Structures 28
13.1 Intro .............................................. 28
13.2 Utilisation Structures dans les Fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
15 Flux 32
15.1 Introduction.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
15.2 Redirection 32
16 Fichiers 33
16.1 Introduction33
16.2 Lecture Depuis les fichiers33
16.3 Le fscanfFonction33
16.4 Écriture vers les fichiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
18 Compilation 37
18.1 Le Chaîne d'outils de compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
18.2 En-tête Fichiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
18.3 En-tête Variables de fichier38
18.4 Fichiers Make . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
19 Utiles CFeatures 41
19.1 Type défini 41
19.2 Macros 41
20 Le préprocesseur C 43
Page 3
21FuPointeurs de fonction 45
22 Appel système 46
23 Erreurs et Errno 47
24 processus 48
24.1 Processus Modèles48
24.2 Création Processus avec Fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
24.3 Processus Relation et Résiliation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
24.4 Zombies and Orphans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
24,5 Course Différents programmes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
25 Tuyaux 52
25.1 Non tamponné I/O52
aux tuyaux. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
25.2 Introduction
25.3 Concurrence et des tuyaux. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
25.4 Redirection Entrée et Sortie avecdup2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
26 Signaux 54
26.1 Introduction.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
26.2 Signal Manipulation54
28 Multiplexage I/O 59
28.1 Le Problème avec les lectures bloquantes 59
28,2sélectionner59
29 Prises 60
29.1 Intro 60
29.2 Prise Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
29.3 Cadre Établir une connexion63
29.4 Douille Communication. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
30 Programmation Shell 67
Page 4
1 Entrée, Sortie et Compilation
1.1 Printing Values (printf)
Tobeabelotusepnrif:taddthesatndardnipu-otuptutlbirary
#include <stdio.h>
intn = 50;
printf("%d\n", n);// 50
•Le nombre de paramètres après la chaîne doit être égal au nombre de spécificateurs de format
1. Listez les fichiers en utilisant ls -F, et les fichiers exécutables auront un astérisque à la fin, par exemple [Link]*
2. Listez les fichiers en utilisant ls -l, et les fichiers exécutables auront x comme permission d'exécution
Page 5
1.3 Lecture de l'entrée (scanf)
Incluez la bibliothèque standard io pour pouvoir utiliser scanf :
#include <stdio.h>
doublen
scanf("%lf", &n);
•donne l'adresse mémoire de la variable n, puis scanf place la valeur que l'utilisateur saisit dans celle-ci
emplacement
•Peut avoir plusieurs spécificateurs de format dans un appel ascanf
Page 6
2 Types, Variables et Instructions d'Attribution
2.1 Program
Programme : Une séquence d'instructions pour un ordinateur
#include <stdio.h>
intmain() {
printf("Bonjour le monde!");
retour0;
}
•int main(): la fonction principale est exécutée lorsque nous exécutons un programme
•#include <stdio.h> spécifie que nous voulons que notre programme puisse utiliser l'entrée et la sortie
–Cette ligne n'est pas une instruction, elle ajoute du code d'un autre fichier à notre code
Rend des instructions telles que printf disponibles pour notre programme.
Les instructions sont envoyées au processeur de l'ordinateur de manière séquentielle dans l'ordre spécifié par notre code
2.2 Variables
L'information est stockée dans la mémoire, qui a des millions de « cellules », et chaque cellule a une adresse et contient un
information
Un programme stocke une valeur en réservant une petite section de mémoire et en lui donnant un nom
•Variable : un morceau de mémoire nommé, ou en d'autres termes, un espace réservé pour ce morceau de mémoire
intn;
sensible
Donner une valeur à une variable : utiliser une déclaration d'assignation
n = 200;
Page 7
Prenez la valeur du Membre de Droite et stockez-la dans la variable du Membre de Gauche
Expressions : façons d'effectuer des calculs, en utilisant des opérateurs arithmétiques et logiques
Opérations arithmétiques en C :
addition +
soustraction -
multiplication *
division /
modulo %
Par exemple.
intx, y;
x = 2;// 2
y = (x + 2) * (x + 5); // 28
doublen = 32;// 32
n = 99.5;// 99.5
intn = 9 % 4;// 1
snake_case
camelCase
Page 8
Évitez les longues files d'attente
Si nous utilisons une deuxième ligne pour une déclaration, ajoutons une indentation.
Commentaire
Les commentaires ne sont pas exécutés comme des instructions.
// Commentaire en ligne
charc
charc =un;//a
c = 98;//'b'
c = c + 1;//'c'
Page 9
3 Expressions booléennes et conditionnels
3.1 Instructions conditionnelles
Bloc if-else
•Si la condition est vraie, le bloc if est exécuté
Si la condition est fausse, le bloc else est exécuté
si(3 > 0) {
printf("1");
}else{
printf("2");
}
Opérateurs de comparaison
moins que <
plus grand que >
égal à ==
supérieur ou égal à >=
moins que ou égal à <=
pas égal à !=
Opérateur logique OU
•||
•Évalue vrai quand au moins un côté de l'opérateur est vrai
Opérateur NON
•!
•Négation de la condition
Page 10
3.3 Structuration des instructions If
Si seulement le bloc (pas de bloc else)
Instruction if imbriquée
Sinon si
•Comporte de la même manière qu'une instruction if imbriquée, mais plus lisible
•La première fois qu'une condition est vraie, le bloc correspondant est exécuté, puis plus aucune condition.
sont vérifiés
si(gpa == 4.0) {
printf("A+ ou A\n");
} else if (gpa >= 3.7) {
printf("A-\n");
}else{
printf("Pas A\n");
}
intx, y, z;
x = 4 < 5; // vrai
y = 5 < 4; // faux
z = 2 < 3 || 5 < 4;// vrai
printf("%d %d %d\n", x, y, z);// 1 0 1
si(0) {
printf("0\n");
} // ne s'exécutera pas
si(1) {
printf("1\n");
}
si(2) {
printf("2\n");
}// 2
Page 11
4Fonctions
4.1 Appeler une fonction
Pour utiliser une fonction, nous l'appelons en utilisant son nom, suivi d'un ensemble de parenthèses. À l'intérieur des parenthèses, nous
fournir les arguments (entrées) à la fonction.
printf imprime le premier argument et insère les valeurs des arguments suivants aux spécificateurs de format
dans le premier argument
•Function signature: combination of the name, return type, and list of argument types
Par exemple.
doublemy_fmax(doublex, doubley);
Le nom de chaque argument (c'est-à-dire xandy) peut être omis dans la déclaration de la fonction
Définissez ensuite la fonction, c'est-à-dire écrivez les instructions qui seront exécutées lorsque nous appellerons la fonction
Page 12
/* Renvoie la plus grande des deux valeurs données, x ou y. */
doublemy_fmax(doublex,doubley) {
si(x > y) {
returnx;
}else{
retour;
}
}
Alternativement, nous pouvons déclarer et définir la fonction en même temps avant de l'utiliser.
Le programme obtiendra la valeur de la variable, puis copiera cette valeur dans l'espace.
alloué pour les paramètres
Passer par valeur
Nous avons deux ensembles de variables : l'un provenant de la fonction principale et l'autre de la fonction appelée.
Les deux ensembles sont complètement séparés l'un de l'autre
Page 13
5 Itération
5.1 Boucles For
Structure de boucle For :
–Habituellement utilisé pour définir une variable qui est mise à jour à travers la boucle
tant que(CONDITION) {
CORPS DE LA BOUCLE
•Si la condition est évaluée à vrai, alors le corps de la boucle s'évalue à nouveau
faire{
CORPS DE LA BOUCLE
}tant que(CONDITION)
Page 14
6 Types et Conversions de Type
6.1 Types numériques
Lorsque nous assignons une valeur double à un entier, la partie fractionnaire est éliminée ou tronquée.
doubled = 4.8;
inti = d;// 4
Lorsque nous assignons une valeur entière à un double, alors la valeur correcte peut être représentée.
inti = 17;
doublé = i;// 17.000000
sizeof donne combien d'octets sont utilisés par le compilateur pour une certaine variable
•La valeur résultante peut être imprimée avec le spécificateur de format %lu
•Par exemple, la taille d'un int est de 4, la taille d'un double est de 8
Le plus grand entier qu'un int peut représenter est la constante INT MAX
Essayer de représenter un grand entier avec un flottant entraînera une perte de précision.
inti = 21247000000;
floatf = i;// 2147000064.000000
Si un type peut contenir toute valeur qu'un autre type peut représenter, alors le premier type est plus large que le second.
Lorsqu'on convertit un type en un type plus large, on peut obtenir la valeur attendue.
Lorsqu'on convertit un type plus large en un autre type, les octets de haut ordre peuvent être perdus.
6.2 Casting
Lorsque nous effectuons une division sur deux entiers, le type du résultat est entier
inti = 5;
intj = 10;
doublek = i / j;// 0,000000 car l'int 5/10 évalue d'abord à 0
Page 15
7 Tableaux
7.1 Introduction
floatgpa[10];
Nous pouvons accéder à un élément d'un tableau en fournissant le nom du tableau suivi de l'index spécifique.
accéder entre crochets
gpa[0] = 3.7;
Les adresses mémoire pour chaque élément de ce tableau sont séparées par 4 octets.
L'espace pour un tableau est alloué lorsque le tableau est déclaré, et tous les éléments sont alloués en un seul
lieu
Par conséquent, les tableaux ne peuvent pas changer de taille.
Peut alternativement créer un nouvel array plus grand et copier tous les éléments de l'array original dedans.
Une fois que nous connaissons l'adresse où le tableau commence et la taille de chaque élément, nous pouvons calculer le
adresse de chaque élément
L'adresse du tableau est l'adresse de l'élément-0
–Prenant le dernier exemple :
adresse de arr[1]
= adresse de arr + 4
= adresse de arr + 1 *sizeof(int)
Page 16
–En général:
Si nous tentons d'accéder à un élément du tableau dont l'indice dépasse la taille du tableau, nous obtenons
quelque chose d'inattendu
C ne vérifie généralement pas si un accès au tableau est dans les limites du tableau
Nous obtenons ce qui est conservé dans la mémoire après la fin du tableau, cela pourrait être n'importe quelles données.
•Si nous essayons d'assigner ce qui est en dehors d'un tableau à une valeur, cela pourrait remplacer la valeur d'un autre
la variable est utilisée, ou provoquer un défaut de segmentation (c'est-à-dire que l'adresse accédée n'était pas légale)
Page 17
8 Indications
8.1 Introduction
inti = 5;
printf("Adresse de i : %p\n", &i)
Si une variable est un pointeur, alors sa valeur est une adresse mémoire
Lorsqu'on déclare un pointeur, nous devons spécifier le type de la valeur stockée à cette adresse mémoire.
int*pt;
pt = &i;
ptpoints toi
Lorsque l'opérateur * est appliqué à un pointeur, il s'évalue à la valeur de la mémoire à laquelle le pointeur pointe.
à
printf("%d\n", *pt);// 5
•Déréférencer le pointeur
inti = 7;
int*pt = &i;// i = *pt = 7
*pt = 9;// i = *pt = 9
Page 18
8.3 Pointers as Parameters to Functions
Les variables de fonction sont des variables locales
Changer cela n'a aucun effet sur l'argument qui lui a donné la valeur initiale.
Utilisez des parenthèses pour vous assurer que l'ordre des opérations est conforme à vos souhaits.
Lorsque nous avons l'intention de passer un tableau à une fonction, nous pourrions déclarer int sum(int arr[]) ou int sum(int
*arr)
•Ce dernier représente mieux ce que fait le compilateur
•If the size of the array is not fixed, the size should also be passed in as a parameter
–Puisque l'utilisation de sizeof sur le tableau donne la taille du pointeur
taper k;
type *p = &k;
intn;
p = p + n; // p + (sizeof(type) * n)
Avec cela, nous pourrions accéder aux éléments du tableau en utilisant l'arithmétique des pointeurs
•p[k] == *(p + k)
inti = 1;
int*pt = &i;
int**pt_ptr = &pt;
•pthas typeint*, donc pt ptr devrait avoir le typeint**, c'est-à-dire un pointeur versint*
Page 19
int*r = *pt_ptr;// &i
intk = **pt_ptr;// 1, équivalent à int k = *r
int***pt_ptr_ptr = &pt_ptr;
intx = ***pt_ptr_ptr;// 1
Page 20
Modèle de mémoire C 9
•Ce tableau de mémoire est divisé en segments, où chaque segment stocke un type particulier de données.
•Couches :
Tampon
Code
Données mondiales
Tas
Pile
SO
Une fois que le code est compilé, il est stocké dans le segment de code de la mémoire.
•Au fur et à mesure que le code s'exécute, il appelle diverses fonctions.
•Each function invocation is allocated space in the stack segment to store local variables
Le segment de pile
L'appel de fonction le plus récent est en haut de la pile
Les fonctions sont supprimées dans l'ordre dernier entré, premier sorti.
L'espace pour les fonctions est alloué sous forme de frames de pile, qui ont suffisamment de mémoire pour stocker toutes les variables locales.
variables
Une fois qu'une fonction a terminé son exécution, nous retirons son cadre de pile de la pile et retournons une valeur à
l'appelant
Le sommet de la pile est toujours la fonction actuellement en cours d'exécution.
•Littéraux de chaîne, par exemple, lorsque nous écrivons char *ptr = "Hi", alors "Hi" est stocké dans le segment de données global.
Chaque fois que nous libérons un morceau de mémoire alloué, il est marqué comme étant disponible pour allocation.
•Par exemple, accéder à une variable ou un pointeur non initialisé, qui pointe vers l'adresse zéro
Page 21
10 Mémoire Dynamique
10.1 Intro
À l'intérieur d'une fonction, nous pouvons allouer de l'espace pour des variables sur le tas afin qu'elles puissent durer au-delà du retour
déclaration des fonctions dans lesquelles elles sont déclarées
void*malloc(size_t taille);
int*ptr = malloc(sizeof(int));
•Si la fuite de mémoire continue, le programme rencontrera finalement une erreur hors mémoire : ENOMEM
voidlibérer(void* ptr);
Cela désalloue l'intégralité du bloc qui a été alloué dans l'appel malloc qui a renvoyé cette adresse.
Un pointeur qui pointe vers une mémoire qui a déjà été libérée est un pointeur pendu.
Lorsque nous modifions ce pointeur dans la fonction, le pointeur à l'extérieur n'est pas affecté.
Dans le cas où nous voulons modifier un pointeur, nous passons un pointeur de deuxième niveau à la fonction
Page 22
10.4 Structures de données imbriquées
Lorsque nous avons un tableau imbriqué dans le tas, par exemple
•Si nous libérons d'abord le pointeur extérieur, alors les pointeurs intérieurs deviennent des pointeurs pendants.
Page 23
11 Arguments de ligne de commande
char*s ="17";
Nous pouvons utiliser la fonction strtol pour convertir une chaîne en l'entier qu'elle représente.
•Syntax:
intmain(intargc,char**argv)
Page 24
12 cordes
12.1 Introduction
•Ne voudrait imprimer rien d'autre qui suit le texte stocké en mémoire
Chaîne C : un tableau de caractères qui a un caractère nul immédiatement après le dernier caractère du texte
•Produit un tableau avec les cinq premiers caractères contenant "bonjour" et les caractères restants comme null
personnages
Nous pouvons également donner les caractères de la chaîne entre guillemets, par exemple
chartext[20] ="hello";
chartext[] ="hello";
Le compilateur allouera une taille de mémoire égale à la longueur de la chaîne plus un caractère supplémentaire.
pour le terminateur nul
Pour créer un littéral de chaîne, utilisez la notation de pointeur au lieu de la notation de tableau :
bonjour
"bonjour" est une littérale de chaîne, qui est une constante qui ne peut pas être modifiée
Page 25
12.3 Taille et Longueur
L'utilisation de sizeof sur une chaîne donne le nombre d'octets occupés par le tableau.
#include <string.h>
strlen renvoie le nombre de caractères dans la chaîne, sans inclure le terminateur nul
Prototype pour strlen :
•size est un type entier non signé qui peut être traité comme un entier
Prototype:
char*strcpy(char*s1,const char*s2)
s1 n'est pas requis d'être une chaîne, mais s2 doit être une chaîne
•strcpy est une fonction non sécurisée, c'est-à-dire que si nous avons s1 qui n'a pas assez de taille pour contenir le contenu de s2, différent
les machines pourraient produire des résultats différents
•Prototype :
char*strncpy(char*s1,const char*s2,intn)
•nindique le nombre maximum de caractères que cela peut contenir, y compris les caractères nuls.
• Pourrait encore être dangereux, puisque le premier caractère de s2 pourrait ne pas se terminer par un terminateur nul.
Pour garantir que cette fonction est sûre, nous ajoutons explicitement un caractère nul à la fin de s1
•Prototype:
char*strcat(char*s1,const char*s2)
Ajoute s2 à la fin de s1
Page 26
•strcat est une fonction non sécurisée car s1 peut ne pas avoir assez d'espace pour stocker tout le contenu
•Prototype:
char*strncat(char*s1,const char*s2,intn)
•nis le nombre maximum de caractères, sans inclure le terminateur nul, qui doit être copié à partir de
s2 à la fin de s1
•strncatalways ajoute un terminateur nul à s1
•habituellement réglé sur sizeof(s1) - strlen(s1) - 1
Prototype :
char*strchr(const char*s,intc)
•Renvoie le pointeur vers le caractère qui est trouvé, ou null si le caractère n'est pas trouvé
L'indice peut être déterminé par l'arithmétique des pointeurs
Prototype :
• Si s2 est trouvé dans s1, alors retourne un pointeur vers le caractère de s1 qui commence la correspondance avec s2
Page 27
13 Structures
13.1 Introduction
Les structures, c'est-à-dire les structures, stockent des collections de données associées.
Exemple de structure :
structétudiant {
charfirst_name[20];
charlast_name[20];
intyear;
gpa flottant;
};
•Le tag de structure donne un nom au type de structure afin que nous puissions définir plus tard des variables de ce type
Nous pouvons déclarer des variables de type struct student, par exemple.
structstudent bon_étudiant;
strcpy(good_student.first_name, "Jo");
strcpy(good_student.last_name, "Smith");
good_student.year = 2;
good_student.gpa = 3.2;
Lorsque nous passons une structure à une fonction, la fonction reçoit une copie de la structure.
Tout changement que la fonction effectue est seulement un changement sur la copie, pas sur la structure originale.
Deux façons de conserver les modifications d'une structure par une fonction :
Cette méthode copie la structure deux fois, c'est-à-dire lorsque la fonction est appelée et à l'instruction de retour.
Page 28
•Pas préférable car les structures peuvent être grandes
Page 29
Structures liées et itération
14.1 Intro
Différences entre les tableaux et les structures liées :
Liste chaînée
•Stocke une séquence d'éléments
•Has a front pointer which holds the address of the first node in the list
•Has nodes which are analogous to elements of array
Chaque nœud contient les données stockées et le pointeur suivant, qui pointe vers le nœud suivant
structnode {
intvalue;
structnode *suivant;
};
•Commencez à partir d'une liste vide, c'est-à-dire que le pointeur avant est nul
typedef structnode {
...
} Noeud;
Alors il ne serait pas nécessaire d'utiliser la structure node chaque fois que nous faisons référence à ce type.
Parcourir la liste
•Commencez à l'avant puis suivez une trace de pointeurs suivants
La valeur nulle au pointeur du dernier nœud nous indique quand arrêter.
Page 30
•Modèle de parcours :
2. Duplicate the link to the new node after the insertion point
3. Remplacez le lien original par le lien vers le nouveau nœud
Testing
•Avoir quatre cas : milieu, début, fin, index illégal
•Écrivez le code pour les trois derniers cas limites en conséquence
Page 31
15 Ruisseaux
15.1 Intro
flux d'entrée : source de données qui fournit des entrées à notre programme
•scanf lit depuis l'entrée standard, qui est un flux d'entrée réglé sur le clavier
Flux de sortie
•printf écrit sur la sortie standard, qui est un flux de sortie réglé sur l'écran
–Utilisé pour la sortie normale du programme
L'entrée standard, la sortie standard et l'erreur standard s'ouvrent automatiquement lorsqu'un programme s'exécute
15.2 Redirection
Pour rediriger l'entrée standard, utilisez le <symbole lors de l'exécution du programme, par exemple ../[Link] < [Link]
•Si un fichier existe déjà, et que nous utilisons la redirection de sortie avec ce nom de fichier, alors ce fichier serait
écrasé
Limitation : un seul fichier peut être utilisé pour la redirection d'entrée ou de sortie
Page 32
16 Fichiers
16.1 Introduction
•moderequires une chaîne qui indique ce que nous voulons faire avec le fichier :
Mode chaîne Emplacement par défaut
r Fichier ouvert pour lecture
“w” Fichier ouvert en écriture
a Fichier ouvert en mode ajout
•Renvoie un pointeur de fichier que nous utiliserons lorsque nous voudrons fermer le fichier, lire à partir du fichier ou écrire à partir de
le fichier
• Si l'ouverture échoue, elle retourne null
Pour fermer un fichier, utilisez fclose
Prototype :
Prototype forfscanf:
Page 33
16.4 Écriture dans des fichiers
Lorsque nous avons l'intention d'écrire dans un fichier, utilisez 'w' ou 'a' comme mode pour fopen.
fprintf est similaire à printf, mais permet de spécifier le flux où la sortie doit aller
Lorsque notre programme écrit dans un flux, il écrit d'abord dans le tampon de fichier, un emplacement en mémoire contrôlé par
le système d'exploitation
•Nous ne savons pas ce qui arrivera au contenu écrit lorsque l'ordinateur perdra de l'électricité en plein milieu.
du programme
L'entrée/sortie pour le débogage n'est pas recommandée pour la raison ci-dessus
Pour s'assurer que des modifications ont été apportées à un flux, utilisez fflush
•Prototype:
intfflush(*FICHIER *flux)
•Demandes que le système d'exploitation écrive toutes les modifications qui sont dans son tampon
Page 34
17 Entrées/Sorties à bas niveau
Extensions pour les fichiers binaires : aucune extension, dat, jpg, mp3, etc.
Des fonctions comme fprintf, fgets, fscanf, etc. ne sont pas utiles pour les fichiers binaires, car les fichiers binaires n'ont pas de notion
de « ligne », et ces fonctions lisent et produisent du texte, pas de données binaires
Prototype :
•ptris est un pointeur vers les données que nous voulons écrire dans le fichier
•taille est la taille de chaque élément que nous écrivons dans le fichier
•nmemb est le nombre d'éléments que nous écrivons dans le fichier (c'est-à-dire 1 pour une variable individuelle, ou un nombre)
d'éléments pour un tableau)
•stream est le pointeur de fichier vers lequel nous écrirons (doit se référer à un flux ouvert en mode binaire)
Renvoie le nombre d'éléments écrits avec succès dans le fichier, ou 0 en cas d'erreur
Prototype :
•ptris est un pointeur vers la mémoire où les données du fichier seront stockées
Renvoie le nombre d'éléments lus avec succès à partir du fichier, ou 0 si aucun élément n'est lu avec succès.
Utiliser fwrite sur un ordinateur et tenter de fread sur un autre peut ne pas fonctionner correctement.
Page 35
17.4fichiersWav
Avoir deux parties :
1. Header
44 bits de données
•Contains information about thewavfile, including parameters required to properly play the file
dans un programme de musique
Pour afficher des fichiers binaires, utilisez od, qui imprime les valeurs trouvées dans un fichier binaire.
c'est-à-dire
od -A d -j 44 -t d2 [Link]
•l'offset est un compte d'octets indiquant de combien la position du fichier doit changer
• Avec une entrée invalide (par exemple, se déplacer vers une position négative), l'appel fseek peut réussir, mais un appel ultérieur
la tentative de lecture/écriture échouera
•Prototype :
Page 36
18 Compilation
18.1 La chaîne d'outils de compilation
compiler courir
Code source.c−−−−−−→exé[Link]−−−→Exécution du programme
1. Front end
obtenirProchainJeton
Analyse lexicale )−−−−−−−−−−−−−−−−−−−−−−* Analyse syntaxique
NextToken
Le code source est préparé pour le front-end par le préprocesseur
•Traduit le code source en une représentation intermédiaire indépendante du langage
2. Milieu de gamme
AST
•Analyse syntaxique−−−→Analyse sémantique
Optimise le code
3. Arrière-plan
•Vous devez assembler le code d'assemblage en code objet, qui devient l'exécutable
Nous pouvons invoquer l'assembleur en utilisant la commande
–La sortie n'est pas lisible par un humain – c'est un fichier objet qui contient des instructions de code machine.
et données
–Besoin d'une étape supplémentaire pour produire l'exécutable : liaison
•L'éditeur de liens prend un ou plusieurs fichiers objets compilés et assemblés et les combine pour créer un
fichier au format exécutable
Le fichier exécutable final est un package qui contient toutes les instructions du programme.
–Cet exécutable n'est pas portable – nous ne pouvons pas le copier sur une autre machine et attendre la même chose.
comportement
4. L'exécutable doit être chargé en mémoire avant que nous ne l'exécutons.
Le chargeur fait ce travail
compiler assembler lien courir
Code source (.c)−−−−−−→assemblage (.s)−−−−−−−→fichier objet (.o)−−−→exécutable (.out)−−−→programme
Chaque fichier est compilé en fichier objet séparément, puis ils sont combinés lors de l'étape de liaison.
Nous pouvons également utiliser la compilation séparée, où nous compilons les codes sources en fichiers objets séparément.
alors utilisez gccto les relier
Page 37
–Par exemple.
$gcc -c un.c
$gcc -c two.c
$gcc un.o deux.o
Cela peut être avantageux lorsque nous voulons modifier un fichier dans un grand projet.
– Peut également être dangereux car le fichier objet modifié peut ne pas être compatible avec les autres fichiers (par exemple.
les types de paramètres de fonction deviennent incompatibles) et le lissage pourrait encore réussir
Nous pouvons améliorer l'organisation de nos projets en utilisant des fichiers d'en-tête.
•Un en-tête doit déclarer ce que les fonctions font et quels types elles nécessitent, sans définir comment elles le font.
sont en réalité mises en œuvre
Pour utiliser le fichier d'en-tête, incluez-le dans le code source, c'est-à-dire #include "header.h"
Les guillemets doubles signifient que nous voulons utiliser le fichier d'en-tête dans le répertoire courant.
Lorsque la dclaration dans le fichier d'en-tte et la dfinition dans le fichier source ne correspondent pas, le type
un décalage sera détecté par le compilateur
•Nous n'avons pas besoin de fournir le nom du fichier d'en-tête à gcc
L'instruction #include indique au préprocesseur d'insérer le corps du fichier d'en-tête dans le code source.
code
•Lorsque nous déclarons des variables dans des fichiers d'en-tête, nous devons ajouter le mot-clé extern (qui signifie « externe »
défini)
Lorsque nous voulons qu'une variable globale existe uniquement dans un fichier, utilisons le mot-clé static.
Nous pouvons ajouter une condition de garde lors de l'importation de fichiers d'en-tête afin de ne pas faire d'importations doubles (ce qui entraîne
erreurs concernant les doublons)
•Par exemple.
#ifndef TEST_H
#define TEST_H
// ...quelques déclarations...
#endif
Si static est utilisé sur une variable locale, elle conserve alors sa valeur à travers les exécutions de la fonction.
Page 38
18.4 Fichiers Make
Lorsqu'un fichier d'en-tête change, le fichier source qui en dépend doit être recompilé.
target: dependencies...
recette
•S'il n'y a pas de dépendances, les actions ne sont exécutées que si la cible n'existe pas.
L'espace blanc avant la recette est une tabulation
•C.-à-d.
Lorsque nous exécutons la commande make, le système d'exploitation recherche le fichier Makefile et vérifie les règles qu'il contient.
S'il n'y a pas de fichier nommé test, alors l'action pour la règle test s'exécute
Si make est exécuté à nouveau, puisque le test n'a pas besoin d'être reconstruit, rien n'est fait.
- Si make a été exécuté et si l'un des tests 1. le test de cœur 2. c est modifié depuis la dernière construction, l'action
exécute
Nous pourrions utiliser des makefiles pour tirer parti de la compilation séparée.
•Par exemple
Chaque fois que timemake évalue une règle, il vérifie d'abord toutes les dépendances ; si une dépendance est également une cible dans le
makefile, il évaluera cette règle en premier avant de vérifier les dépendances
Page 39
•Par exemple.
• Le signe pourcentage signifie que chaque fichier objet qui doit être construit dépend d'un fichier source (et d'un en-tête
fichier) du même nom
•$<est une variable contenant le prénom dans la liste des dépendances
Par exemple
.PHONY: clean
clean:
rm test *.o
•Par exemple.
test:$(OBJFILES)
gcc$(OBJFILES)
• Lorsque un nouveau fichier est ajouté au projet, nous pouvons simplement mettre à jour la variable
Page 40
19 fonctionnalités C utiles
19.1 Typedef
Le typedef permet de créer des alias pour les types et est évalué à la compilation
•Par exemple, la taille est définie dans [Link]
•typedef permet de définir un nom (c'est-à-dire size_t) qui fait référence à un type existant (c'est-à-dire unsigned int)
•Par exemple.
typedef structstudent {
...
} Student;
19.2 Macros
Les macros créent des alias qui sont évalués pendant la prétraitement
•Par exemple
#define MAX_NAME_LENGTH 40
•#définir demande au préprocesseur de remplacer les occurrences de LONGUEUR MAXIMALE DU NOM par 40
Améliore la lisibilité
Les constantes peuvent être facilement mises à jour
•[Link].
Utilisation :
Page 41
•Se comporte comme une fonction, mais de manière plus efficace car cela se produit avant la compilation
•Ne peut pas y avoir d'espace entre le nom de la macro et les parenthèses
•Dans la définition de la macro, placez le nom du paramètre (c'est-à-dire x) et la définition entière entre parenthèses
–Il faut s'assurer que le paramètre est complètement évalué avant d'autres opérations.
Par exemple, si 1 + 1 est substitué pour x, alors nous devons évaluer cette addition en premier.
Page 42
5 Itération
5.1 Boucles For
Structure de boucle For :
–Habituellement utilisé pour définir une variable qui est mise à jour à travers la boucle
tant que(CONDITION) {
CORPS DE LA BOUCLE
•Si la condition est évaluée à vrai, alors le corps de la boucle s'évalue à nouveau
faire{
CORPS DE LA BOUCLE
}tant que(CONDITION)
Page 14
•Peut être utilisé pour définir des constantes spécifiques au système et pour inclure des bibliothèques spécifiques au système.
Nous pouvons définir des macros dans la ligne de commande en utilisant le drapeau -D
•Par exemple
où le code C contient
#ifdef DEBUG
printf("Exécution en mode de débogage au niveau %d\n", DEBUG);
#endif
Lorsque nous utilisons #include, le fichier d'en-tête est copié dans le fichier source
Nous pourrions rencontrer des problèmes lorsque la même variable est définie dans différents fichiers d'en-tête, ou lorsqu'un en-tête
le fichier est inclus deux fois (par exemple, A et B sont inclus, et B ordonne au préprocesseur d'inclure A (à nouveau))
Presque tout ce qui peut être fait avec des macros peut être fait au sein du système C
Page 44
21 Pointeurs de fonction
Nous pouvons passer des fonctions en arguments ou stocker des fonctions dans des structures
Le type d'une fonction est son type de retour et ses types de paramètres
•Par exemple
void...(int*,int)
void(*nom_func)(int*,int)
• Lorsque nous appelons une fonction via son pointeur, nous n'avons pas besoin de le déréférencer.
• Lorsque nous passons des fonctions en arguments, nous ne faisons que passer son nom, comme s'il s'agissait d'une variable normale.
Page 45
22 Appel système
Appel système : une fonction qui demande un service du système d'exploitation
•Par exemple.
voidexit(intstatus);
programme
•Les fonctions de bibliothèque, par exemple printf, scanf, fopen, fgets, appellent des appels système dans le cadre de leur fonctionnement
Page 46
23 Erreurs et Errno
Par exemple, lorsque nous utilisons fopen, le fichier peut ne pas exister ou avoir les mauvaises permissions, auquel cas le
le programme pourrait planter
−1 pour entier
Pointer nul
Pour indiquer le type d'erreur, utilisez la variable globale errnow qui stocke le type de l'erreur
•errnoest unint
Des codes numériques différents sont définis pour différents types d'erreurs.
•Par exemple, si malloc échoue, cela renvoie nul et définit errno sur ENOMEM
•Prototype:
voidperror(const char*s)
Le message inclut les arguments, suivis d'un deux-points, puis du message d'erreur correspondant.
à la valeur actuelle de errno
•Le but principal est d'afficher un message d'erreur basé sur la valeur actuelle de errno
•Ne devrait pas être utilisé comme une fonction générique de rapport d'erreur, utilisez plutôt fprintf pour l'erreur standard.
Lors de l'utilisation des appels système ou des fonctions de bibliothèque, vérifiez les erreurs (en vérifiant la valeur de retour) avant de passer à autre chose.
sur
• Lorsqu'une erreur est rencontrée, des messages d'erreur informatifs sont imprimés (plutôt qu'une faute de segmentation)
ou un résultat aléatoire)
Page 47
24 Processus
24.1 Modèles de Processus
Programme : Les instructions exécutables d'un programme
•Par exemple, code source, code machine compilé
Processus
Chaque processus a un identifiant de processus (PID)
Chaque processus est également associé à une structure de données appelée un bloc de contrôle de processus (BCP) (ou tâche
bloc de contrôle) qui stocke
Valeurs actuelles des registres importants
Inclure le pointeur de pile (SP), qui identifie le sommet de la pile
Inclure le compteur de programme (PC), qui identifie la prochaine instruction à exécuter.
–Ouvrir des descripteurs de fichiers
Le nombre de processeurs (c'est-à-dire de CPU) détermine combien de processus peuvent exécuter une instruction.
en même temps
Les processus qui s'exécutent actuellement sont dans l'état en cours d'exécution.
Les processus qui pourraient s'exécuter si un CPU était disponible sont dans l'état prêt.
Les processus qui attendent qu'un événement se produise sont dans l'état bloqué (ou état de sommeil)
•L'ordonnanceur du système d'exploitation est responsable de décider quel processus doit être exécuté et quand.
Pour dupliquer un processus, le système d'exploitation copie l'espace d'adressage du processus original, ses données et le PCB.
•En conséquence, le nouveau processus créé exécute le même code, a les mêmes valeurs pour toutes les variables.
dans la mémoire, et a la même valeur pour le compteur de programme et le pointeur de pile
Page 48
La valeur de retour de fork est différente dans les deux processus
•fork peut échouer s'il y a déjà trop de processus en cours d'exécution pour l'utilisateur, ou sur l'ensemble du système
Le système d'exploitation traite le processus parent et le processus enfant de la même manière et ne privilégie aucun d'eux.
Lorsqu'on exécute un programme, le shell attend que le processus soit terminé puis affiche une invite pour le suivant.
ordre
Lorsque le processus parent d'origine se termine, un invite de commande s'affiche.
•Puisque le shell est un processus que le système d'exploitation doit planifier, quelques processus fils pourraient imprimer une certaine sortie.
avant que le shell n'affiche son invite
•Au moment où l'invite de commande du shell est affichée, il est possible que certains processus enfants ne soient pas encore terminés.
alors ils impriment plus ensuite
Nous pouvons utiliser l'appel waitsystem pour forcer le processus parent à attendre jusqu'à ce que l'un de ses enfants se termine.
Le shell utilise l'appel système wait pour se suspendre jusqu'à ce que son enfant se termine.
Nous devons appeler waitforeachchild qui a été créé pour attendre tous les processus enfants.
•Valeur de retour de wait :
La valeur de retour du processus enfant fait partie de la valeur d'état dans wait
Page 49
Il y a d'autres aspects au statut, par exemple la partie qui représente la façon dont les processus se sont terminés, que ce soit
normalement ou par un signal (c'est-à-dire Ctrl-C)
Nous pouvons utiliser waitpid pour spécifier quel processus enfant attendre.
Ne peut attendre que les processus enfants, pas les processus non liés comme l'enfant de l'enfant.
supprimez le PCB du processus terminé jusqu'à ce qu'il sache qu'il est sûr de le nettoyer
Un processus zombie est un processus qui est mort, mais qui est toujours là en attendant que le parent le collecte.
état de résiliation
Un processus zombie est exorcisé lorsque son statut de terminaison a été collecté.
La tâche principale du processus init est d'appeler wait dans une boucle qui collecte le statut de terminaison
de tout processus qu'il a adopté
Après qu'Afterinithas a collecté l'état de terminaison d'un processus orphelin, toutes les données du processus
les structures peuvent être supprimées, et le zombie disparaît
Lorsque le processus parent a été terminé mais qu'un processus enfant ne l'a pas été, alors le processus enfant est un orphelin.
•Lorsque nous utilisons getppid sur les processus orphelins, nous obtenons 1
Le processus avec PID 1 est le processus init, c'est-à-dire le premier processus que le système d'exploitation lance.
La famille de fonctions exec remplace le processus en cours d'exécution par un exécutable différent.
Les variantes des fonctions exec accomplissent toutes la même tâche, mais diffèrent par la manière dont les arguments sont passés.
les fonctions sont interprétées
•execl : le premier argument est le chemin vers un exécutable, et les arguments restants sont la commande
arguments de ligne à l'exécutable
Si nous n'avons pas d'arguments en ligne de commande, nous passons NULL comme deuxième argument
Lorsque un programme appelle exec, le système d'exploitation fait ce qui suit :
–Lors de l'exécution de l'execl, le système d'exploitation trouve le fichier contenant le programme exécutable et le charge.
dans la mémoire où se trouve le segment de code
Page 50
Une nouvelle pile est également initialisée
Le compteur de programme et le pointeur de pile sont mis à jour afin que la prochaine instruction à exécuter soit
ce processus retourne au niveau utilisateur est la première instruction dans le nouveau programme
Lorsque le contrôle revient au processus de niveau utilisateur, le code original qui a appelé execl a disparu, donc il
ne devrait jamais revenir et les lignes suivantes execl ne devraient jamais s'exécuter
∗ execl retournera si une erreur se produit et qu'il est incapable de charger le programme
Le système d'exploitation ne crée pas un nouveau processus, au lieu de cela, le processus appelant est modifié.
execvariants
execl
–l – liste : arguments de ligne de commande passés sous forme de liste d'arguments à exécuter
•execv
–v – vecteur : arguments de ligne de commande passés sous forme de tableau de chaînes
•execlp execvp
Processus Shell
Lorsque nous tapons une commande à l'invite de commande, le shell appelle d'abord fork pour créer un nouveau processus, puis
callsexecto load a different program into the memory of the child process
Le processus shell appelle ensuite wait et se bloque jusqu'à ce que le processus fils ait fini d'exécuter.
Lorsqu'il retourne, le délai d'attente imprime un message indiquant qu'il est prêt à recevoir la prochaine commande
Page 51
25 tuyaux
25.1 Entrée/Sortie non tamponnée
Nous pouvons utiliser la commande strace pour voir quels appels système nous avons effectués.
Dans l'appel système write, 3 est passé en tant que premier paramètre (paramètre de descripteur de fichier)
Les 3 fichiers qui s'ouvrent automatiquement sont l'entrée standard, la sortie standard et l'erreur standard.
Chacun d'eux a les descripteurs de fichier 0, 1, 2, respectivement.
Les tuyaux peuvent être utilisés pour envoyer des données entre des processus liés.
Lorsqu'un programme appelle l'appel système pipe, le système d'exploitation crée les structures de données de pipe et les descripteurs de fichiers ouverts.
sur le tuyau
Ces deux descripteurs de fichier sont stockés dans le tableau de deux entiers que nous passons dans le tube
L'indice 0 du tableau est le descripteur de fichier utilisé pour lire depuis le tube
•Après le retour de l'appel de pipe, le processus peut lire et écrire sur le pipe
Lorsque fork crée une copie d'un processus existant, il crée également une copie de tous les descripteurs de fichiers ouverts.
Après l'appel à fork, les deux processus ont des descripteurs de fichiers de lecture et d'écriture sur le tube.
•Lorsque les descripteurs de fichier d'écriture sur le tuyau sont fermés, une lecture sur le tuyau renverra 0, indiquant que
il n'y a plus de données à lire depuis le tuyau
Lorsqu'un processus se termine, tous ses descripteurs de fichiers ouverts sont fermés.
Page 52
Le tube est une structure de données en file d'attente dans le système d'exploitation
Le cas 3 ne se produira pas car le système d'exploitation ne permet d'opérer qu'un seul à la fois.
•Le système d'exploitation bloque l'appel d'écriture lorsque le tuyau est plein
dup2 : un appel système qui crée une copie d'un descripteur de fichier ouvert
•Prototype de fonction :
• Lorsqu'un processus enfant est créé, même si sa table de descripteurs de fichiers est séparée de celle de son parent, ils
peut pointer vers les mêmes objets
•dup2 prend 2 indices dans la table des descripteurs de fichiers et réinitialise l'un d'eux pour faire référence au même objet fichier que le
autre
Page 53
26 Signaux
26.1 Introduction
Chaque signal est identifié par un numéro entre 1 et 31, et des constantes définies sont utilisées pour leur donner des noms.
Lorsque nous tapons Ctrl-C, le signal SIGINT est envoyé, et l'action par défaut est que le processus se termine.
•Lorsque nous tapons Ctrl-Z, le signal SIGSTOP est envoyé, et l'action par défaut est que le processus soit suspendu.
exécution
Nous pourrions utiliser une autre fenêtre de terminal pour envoyer un signal à un programme.
–Suspendre le processus :
Nous pourrions utiliser lekilllibrary et envoyer des signaux d'un programme C à un autre.
Chaque entrée dans la table des signaux contient un pointeur vers le code qui sera exécuté lorsque le système d'exploitation livrera
le signal au processus
Ce code est appelé la fonction de gestion des signaux
Nous pouvons changer le comportement d'un signal en installant une nouvelle fonction de gestion des signaux.
• L'appel du système d'actions Thesig modifie la table des signaux afin que la fonction désirée soit appelée à la place de
l'action souhaitée
–Prototype :
struct sigaction
Page 54
structsigaction {
void(*sa_handler)(int);
void(*sa_sigaction)(int, siginfo_t *,void*);
sigset_t sa_mask;
intsa_flags;
void(*sa_restorer)(void);
}
•sa handler : le pointeur de fonction pour le gestionnaire de signal que nous installons
Page 55
27 Manipulation de bits et drapeaux
27.1 Opérations Bitwise
Effectue des opérations logiques en examinant chaque bit.
ET binaire
1 et 1 == 1
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0
OU bit à bit
1 | 1 == 1
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0
1 ^ 1 == 0
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 0
~0 == 1
~1 == 0
•<<déplacer à gauche
•>>décaler à droite
Page 56
Chaque fichier a un propriétaire et un groupe
•La première colonne dans la sortie est la chaîne de permission, représentant qui peut lire, écrire ou exécuter le
fichier
Par exemple, le propriétaire peut lire, écrire et exécuter le fichier ; le groupe peut lire et exécuter le fichier ;
tout le monde peut lire et exécuter le fichier
La troisième colonne est le propriétaire du fichier
Nous pouvons définir des autorisations distinctes pour le propriétaire, le groupe et chaque autre utilisateur du système.
Exemple
Numéro de bit 8 7 6 5 4 3 2 1 0
Bit 1 1 1 1 0 1 1 0 1
Caractère de permission r w x r - x r - x
–Base-8 is convenient for permissions since each digit can be represented by 3 bits
Un octal en C s'écrit avec un zéro précédant
Pour définir les bits, nous pouvons utiliser l'opérateur binaire OU
Pour vérifier les bits, nous pouvons utiliser l'opération binaire ET.
Page 57
Cette technique s'appelle le masquage de bits
•Pour avoir un ensemble suffisamment grand, nous pouvons utiliser un tableau d'entiers non signés, où chaque élément (c'est-à-dire un entier non signé)
intindex = n / 32
intbit = n % 32
3. Effectuer l'opération
•D'autres opérations peuvent être mises en œuvre de manière similaire en utilisant des opérations binaires
Page 58
28 Multiplexage d'E/S
28.1 Le problème des lectures bloquantes
Lorsque le parent lit d’un tuyau alors qu’il n’y a rien sur le tuyau encore, l’appel read est bloqué jusqu'à ce que le
l'enfant écrit au tuyau
Si le parent lit à partir de deux tubes, et que ses appels à read sont l'un après l'autre dans une boucle :
•Si le premier enfant n'écrit rien dans le tuyau, tandis que le deuxième enfant écrit beaucoup dans le tuyau,
le parent attendrait le premier enfant pour toujours
28.2sélectionner
Appel selectSystam
•Prototype:
•L'appelant spécifie un ensemble de descripteurs de fichiers (c'est-à-dire des fds de lecture) à surveiller
•sélectionner les blocs jusqu'à ce que l'un de ces descripteurs de fichier ait des données à lire ou jusqu'à ce que la ressource ait été
fermé
Un descripteur de fichier avec des données à lire ou avec une ressource fermée est prêt
•select modifie l'ensemble des descripteurs afin que, lorsqu'il retourne, l'ensemble ne contienne que les descripteurs de fichier qui
sont prêts à lire
Il pourrait y avoir plus d'un descripteur de fichier qui est prêt
•écrire fdsanderror fdsare également des ensembles de descripteurs de fichiers que nous pouvons utiliser pour vérifier quels descripteurs de fichiers
•timeout est un pointeur vers struct timeval qui indique combien de temps select bloquera avant de retourner,
même si aucun descripteur de fichier n'est prêt
•Sinceread fdsis modifié par ceslectcall, nous ne pouvons pas simplement réutiliser l'appel de fonction, au lieu de cela, nous
besoin de réinitialiser l'ensemble avant d'appeler select à nouveau
ensemble fd
•FD ISSET vérifie si le premier paramètre (descripteur de fichier) se trouve dans le second paramètre (pointeur vers
fd ensemble)
Page 59
29 Prises
29.1 Introduction
Internet
Chaque machine dispose d'un protocole Internet, ou adresse IP, qui est utilisé pour lui envoyer un message depuis n'importe où.
d'autres machines connectées à Internet
Une machine peut exécuter de nombreux processus différents qui doivent communiquer via Internet.
Pour spécifier le programme, en plus de l'adresse IP, nous avons également besoin du port.
•L'emplacement complet d'un programme s'exécutant sur une machine connectée à Internet est l'adresse de la machine plus
le port
•Les messages envoyés d'une machine sont encapsulés dans des paquets, qui contiennent à la fois l'adresse et la charge utile.
(c'est-à-dire le contenu)
Lorsque le paquet quitte la machine, il est reçu par le routeur, qui facilite le transfert des paquets.
entre les réseaux
Les routeurs sont connectés à plusieurs réseaux et savent à quel réseau le paquet doit être envoyé.
ordre de l'approcher de sa destination finale
Client et Serveur
Un serveur est un programme fonctionnant sur un port spécifique d'une certaine machine, attendant qu'un autre programme...
envoyer un message
•A user runs aclientprogram when they want to start interacting with a server
–Le client envoie soit un message unique, soit commence une connexion (c'est-à-dire une conversation entre le
deux machines qui impliquent plusieurs messages
Une fois que les programmes ont établi un canal de communication, alors soit la machine peut envoyer des données à l'autre.
autre
Pour établir un canal de communication, nous utiliserons des sockets
Prises
A de nombreux types
Sockets de datagramme
Sockets de flux
–Sockets bruts
Sockets de flux
•Construit sur le protocole TCP
Page 60
•Garantir que le message ne sera pas perdu en transit, et que les messages seront livrés dans l'ordre dans
qu'ils sont envoyés
ThesocketSystem Call
Prototype :
intsocket(intdomaine,inttype,intprotocol)
•Lorsque tout est configuré, nous avons besoin d'un point de terminaison dans le programme client et d'un point de terminaison dans le serveur, donc
•En cas de succès, la valeur retournée sera l'indice d'un élément dans la table des descripteurs de fichiers
– Généralement défini sur PF INET ou AF INET, qui sont définis comme étant les mêmes
•type
Pour les sockets de flux, définissez ce paramètre sur SOCK STREAM
protocole
–TCP est le seul protocole disponible pour les sockets de flux, définissez ce paramètre à 0 (ce qui indique
que nous utilisons le protocole par défaut pour ce type de socket)
•Prototype:
adresse
Le pointeur vers la structure sockaddr bind fonctionne pour toutes les familles d'adresses différentes
–Pour notre famille particulière AF INET, nous définissons ce paramètre en utilisant une struct sockaddr in (où
in signifie internet)
Définition de sockaddr dans :
structsockaddr_in {
shortsin_family;
u_short sin_port;
structin_addr sin_addr;
charsin_zero[8];
};
Page 61
Les numéros de port varient de 0 à 65535
Les numéros de port 0-1023 sont réservés aux services bien connus
*Le numéro de port 1024-49151 est un port enregistré, c'est-à-dire que nous pouvons nous enregistrer auprès de l'IANA (Internet Assigned Numbers Authority).
Définir à INADDR_ANY, ce qui configure le socket pour accepter les connexions de n'importe quelle adresse
de la machine
Une machine peut avoir plusieurs cartes d'interface réseau et peut être branchée sur des prises séparées.
réseaux, résultant en ayant des adresses IP différentes dans chaque réseau
La machine a également une adresse pour elle-même, [Link] (c'est-à-dire localhost)
–sin zéro est un rembourrage supplémentaire, rendant le sockaddr de la même longueur que la structure sockaddr
∗ Lorsque nous allouons de l'espace pour cette structure, ces octets ne sont d'aucune manière réinitialisés
Nous pouvons utiliser memset pour définir ces 8 octets à 0 (pour des raisons de sécurité)
–Puisque bindexpects un pointeur vers sockaddr (pas un pointeur vers sockaddr in), nous devons faire 2 choses
1. Take the address of thesockaddr instruct
2. Cast vers struct sockaddr *
•l'adresse lenis la longueur de l'adresse que nous passons
•Configuration générale
structsockaddr_in addr;
addr.sin_family = AF_INET;
Page 62
addr.sin_port = htons(54321);
addr.sin_addr.s_addr = INADDR_ANY;
memset(&(addr.sin_zero), 0, 8);
•Prototype:
intlisten(intsocket,intbacklog);
•backlog
Il se peut qu'il y ait un cas où plusieurs utilisateurs tentent de se connecter presque en même temps, entraînant
une file d'attente pour les demandes de connexion
–écoute met en place la structure de données nécessaire pour stocker ces connexions partielles
–le backlog est le nombre maximum de connexions partielles qu'il peut contenir
–Pas le nombre maximum de connexions qu'il peut contenir
•address
•En cas de succès, la valeur de retour est un entier représentant un nouveau socket que nous utiliserons pour communiquer
avec le client
Page 63
•addrlen est la longueur de l'adresse
Nous définissons la longueur à la taille de notre adresse et passons l'adresse de cette valeur.
Configuration générale
structsockaddr_in client_addr;
client_addr.sin_family = AF_INET;
unsigned int client_len = sizeof(struct sockaddr_in);
Pour établir une connexion via un socket à un serveur, utilisez l'appel système connect.
•Prototype:
Page 64
Nous castons la structure sockaddr résultante en *tostruct sockaddr *
Configuration générale
structaddrinfo *result;
intgetaddrinfo("[Link]", NULL, NULL, &result);
server.sin_addr = ((structsockaddr_in *) result->ai_addr)->sin_addr;
freeaddrinfo(result);
écouter_soc = socket(...);
lier(listen_soc, ...);
écoute(écoute_soc, ...);
•Callaccept, qui bloque, ne renvoyant que s'il y a une erreur ou lorsqu'une connexion est établie
si(client_socket == -1) {
perror(accept);
sortir(1);
}
•Le socket d'écoute est toujours en écoute - nous pourrions l'appeler à nouveau.
Page 65
•Nous pouvons utiliser le descripteur de socket tout comme un descripteur de fichier
fermer(listen_soc);
Côté client :
•Créer une socket pour se connecter au serveur
soc = socket(...);
si((connect(soc, ...)) == -1) {
perror(connect);
exit(1);
}
Allouer de la mémoire pour contenir les valeurs que nous allons lire
charbuf[10];
fermer(soc);
•Vous devez utiliser la valeur de retour de read pour déterminer combien d'octets ont été lus, et appeler read à nouveau si
nécessaire
La valeur de retour de read peut également déterminer si l'autre extrémité du socket est fermée.
Page 66
30 Programmation Shell
Le shell est une grande boucle qui effectue les actions suivantes dans l'ordre
•Prints a prompt
•Lit une commande
Analyse la commande
•Exécute la commande
Variétés de langages de programmation de shell
•sh
Les versions incluent la version 7 shell, ksh, ash, bash, dash, etc.
Ce sont des implémentations du langage de programmation sh de base, avec des fonctionnalités supplémentaires.
mented by the author
•csh
•Par exemple, les caractères génériques de nom de fichier sont remplacés par la liste correspondante des noms de fichiers par le shell avant l'exécution
le commandement
-Par exemple, cat *.c est remplacé par cat a.c b.c
La commande theechocommand peut être utilisée pour voir les arguments de ligne de commande substitués.
Les substitutions en ligne de commande sont également utilisées pour les variables.
$i=3
$ écho$i
3
•Par exemple
$ expr2 + 2
4
Pour effectuer des calculs arithmétiques sur des variables, utilisez l'accent grave ‘
Ce qui est à l'intérieur des accents graves est interprété comme une commande et est exécuté.
Page 67
Si la sortie est capturée, la sortie est substituée dans la ligne de commande - moins une nouvelle ligne à la fin
caractère à la fin
–Par exemple.
$i=`expr4 + 1`
$ écho$i
5
$i=`expr$i + 1
$ écho$i
6
•lire suivi des noms de variables dans lesquelles l'entrée est stockée
$ lisez y
foo bar baz
$ écho$x
foo
$ écho$y
bar baz
•S'il y a plus de variables que de jetons, alors les variables suivantes sont définies sur des chaînes vides
Lorsque une commande est exécutée, le shell cherche la variable PATH pour la commande
Pour obtenir le statut de sortie de la dernière commande exécutée, utilisez $ ?
Instructions conditionnelles
•Structure
ifcondition
alors
commande
sinon
ordre
fi
•Si la condition réussit (c'est-à-dire a un statut de sortie 0), alors la commande then est exécutée
Page 68
•Pour avoir plusieurs conditions, utilisez elif, par exemple.
si condition 1
alors
commande1
elifcondition2
alors
commande2
sinon
commande3
fi
•Si nous ne voulons rien faire pour l'une des branches, utilisons :
•Par exemple.
$ test2 -lt 3
$ echo $?
0
$ test3 -lt 2
$ écho$?
1
Au lieu de chiffres, nous pourrions utiliser des variables en mettant un signe dollar devant.
–-eq: equal
–-ne: not equal
–-gt: plus grand que
–ge : supérieur ou égal
–lt: moins que
–-le : inférieur ou égal
Ces opérateurs de comparaison numériques sont issus du langage de programmation Fortran.
•Opérateurs de comparaison de chaînes pour le test :
égal
–!=: pas égal
Motivation d'avoir deux ensembles d'opérateurs de comparaison :
Page 69
–Etc.
Boucles While
•Structure
condition de boucle
faire
commande
fait
Utile pour lire un fichier avec read, qui s'arrête à la fin du fichier
–La fin de fichier peut être signalée depuis le terminal en utilisant Ctrl-V
Constantes Booléennes
Citant en sh
•Dans sh, ls et "ls" sont les mêmes puisqu'ils sont tous deux des chaînes.
•Pour supprimer le sens d'un seul caractère, utilisez \ suivi par le caractère
•Les guillemets doubles suppriment tout sauf le signe dollar, le backquote, le backslash ou le guillemet double de fermeture.
citation
•Single quotes suppress everything except for the closing single quote
•L'espace est préservé dans des guillemets doubles et des guillemets simples
•Exemple :
$filename='a b'
$chat"$filename"
Pour le chat, des guillemets sont nécessaires pour éviter l'interprétation de l'espace, et nous avons besoin de guillemets doubles.
pour encore interpréter le signe dollar
Pour boucle
•Structure
Page 70
pourvariabledansliste
faire
commande
fait
pourin*.c
faire
écho$i
fait
$seq 1 4
1
2
3
4
•seq peut être utilisé dans une boucle for, par exemple
sum=0
pourinséquence 1 100
faire
somme=`expr$somme +$i`
fait
•Peut utiliser une variation de la boucle for qui parcourt tous les arguments de la ligne de commande
fori
faire
un argument écho$i
Fait
Déclaration de cas
•Structure
expressiondecas
modèle1)
commande1
;;
motif2)
commande2
;;
motif3)
commande3
;;
*)
commande4
;;
Page 71
esac
•Matchesexpressionto eachpattern
•Chaque cas se termine par;;
Les cas sont testés dans l'ordre
Descripteurs de fichiers
-0 : entrée standard
-1 : sortie standard
-2 : erreur standard
•L'entrée et la sortie standard sont toutes deux le terminal
•Pour rediriger un descripteur de fichier vers un autre, utilisez >&2 où le côté gauche de >& est le descripteur de fichier à
rediriger et le côté droit est le descripteur de fichier vers lequel rediriger
•$@est identique à$*en dehors des guillemets doubles ; à l'intérieur des guillemets doubles, les guillemets doubles sont considérés comme
cesser de fonctionner entre les arguments tout en continuant à fonctionner dans un argument donné
• Lorsque nous voulons passer tous les arguments de ligne de commande à un autre programme, utilisez "$@" entre guillemets.
shiftcommand moves all of the command-line arguments down one place, i.e. $3becomes$2, etc.,$1is
écarté
Pour une variable, nous pourrions mettre des accolades autour du nom de la variable, par exemple ${var}
Cela permet d'avoir un autre argument qui vient immédiatement après la variable (sans espaces).
Commentaires dans sh est#
Page 72