IO et threads de processus

Processus d'E/S

scanf\printf : terminal

IO : entrée/sortie, fichier

E/S standards

fichier E/S

Acquisition d'attributs de fichier : ls -l type de fichier autorisation de fichier numéro de lien nom d'utilisateur nom de groupe taille heure nom de fichier

Opérations sur les répertoires : ls

bibliothèque

processus

processus : créer un processus

Threads : création de threads, synchronisation et mutex

Communication inter-processus : 7->6 types

1. E/S standard

Fichiers : 7 types de fichiers

b(block device) c(character device) d(répertoire) -(ordinary file) l(linked file) s(socket) p(named pipe)

1. Conception

1.1 Définition

Un ensemble de fonctions définies dans la bibliothèque C dédiée à l'entrée et à la sortie

1.2 Caractéristiques

1) Il existe un mécanisme de tampon, qui réduit le nombre d'appels système et améliore l'efficacité

Appels système : un ensemble d'interfaces fournies par le noyau

2) Opérer autour du flux, le flux est décrit par FILE *, et FILE est une structure qui décrit les informations pertinentes du fichier

typedef struct _IO_FILE FICHIER ;

3) Trois flux sont ouverts par défaut, stdin (entrée standard), stdout (sortie standard), stderr (erreur standard)

structure _IO_FILE *stdin ; --> FICHIER *stdin;

Supplément : l'utilisation de ctags (vous pouvez suivre le code)

vi -t FILE (typedef définit les types de données, les définitions de macros, les structures, etc.)

choisir le bon numéro

Positionnez le curseur à la position cible, ctrl+] : code de poursuite vers le bas

ctrl+t : retour

Q : quitter

1.3 Tampon

1) Cache complet : lié aux fichiers

Conditions pour rafraichir le cache :

1 - Le programme se termine normalement

2- actualisation complète du cache

Rafraîchissement forcé en 3 flux

2) Cache de ligne : lié au terminal

Conditions pour rafraichir le cache :

1 - Le programme se termine normalement

2-\nActualiser le cache

3- actualisation complète du cache

Rafraîchissement forcé en 4 flux

3) Pas de cache : pas de cache, erreur standard

Exercice : Calculer la taille du tampon pour la sortie standard dans le tampon de ligne

#include <stdio.h>

int main(int argc, char const *argv[])
{
    // for(int i = 0; i < 300; i++)
    //     printf("%4d", i);
    // while(1);

    // 结构体,stdout _IO_buf_end
    printf("hello");
    printf("%d\n", stdout->_IO_buf_end - stdout->_IO_buf_base);
    
    return 0;
}

2. Interface de fonction

2.1 Ouvrir un fichier

FILE *fopen(const char *path, const char *mode);
参数:
    path:打开文件
    mode:打开方式
        r:只读,流被定位到文件开头
        r+:可读可写,流被定位到文件开头
        w:只写,文件不存在创建,文件存在清空,流被定位到文件开头
        w+:可读可写,文件不存在创建,文件存在清空,流被定位到文件开头
        a:追加,文件不存在创建,存在追加,流被定位到文件末尾
        a+:可读可写,文件不存在创建,存在追加,开始进行读从头读,进行写流被定位到文件末尾
返回值:成功:文件流
       失败:NULL,并且设置errno(错误码)

2.2 Lecture et écriture de fichiers

2.2.1 Lire et écrire un caractère à la fois

int fgetc(FILE *stream);
功能:从文件中读一个字符
参数:stream:文件流
返回值:成功:读到字符的ASCII
       失败或读到文件末尾:EOF
int fputc(int c, FILE * stream)
功能:向文件中写入一个字符
参数:c:要写的字符
   stream:文件流
返回值:成功:写的字符的ASCII
      失败:EOF
#include <stdio.h>

int main(int argc, const char *argv[])
{
    FILE *fp;
    fp=fopen("./i2.c","w");
    if(fp==NULL)
    {
        perror("fopen err");
        return -1;
    }
    fputc('a',fp);    //存放字符到指定文件中
    fputc(32,fp);
    fputc('a',stdout);  //向终端(标准输出)一个函数
    return 0;                                         
}
                                                      
                                                      
                                                      

 Exercice : Programme pour réaliser la fonction du chat  

Idée : ouvrir le fichier, lire le fichier dans une boucle (fgetc), lorsque la fin du fichier est lue (la valeur de retour de la fonction fgetc est EOF), la boucle se termine et le contenu lu est imprimé

#include <stdio.h>

int main(int argc, const char *argv[])
{
FILE *p;    //定义结构体指针
int ch;
if (argc!=2)   //传入的参数不等于2个的时候进行提示
{
printf("usage:%s <filename>\n",argv[0]);
return -1;
}
p=fopen(argv[1],"r");   //打开文件
if (p==NULL)
{
perror("open err");
//printf("open error\n");
return -1;
}

while((ch=fgetc(p))!=EOF)   //循环打印出文件的全部内容

{
printf("%c",ch);
}
    return 0;                                      
}
                                                   
                                                   

Remplir:

int  feof(FILE * stream);
功能:判断文件有没有到结尾
返回:到达文件末尾,返回非零值
int ferror(FILE * stream);
功能:检测文件有没有出错
返回:文件出错,返回非零值
void perror(const char *s);
功能:根据errno打印错误信息
参数:s:要打印的字符串
#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    fp = fopen("./a.c", "w");
    if(fp == NULL)
    {
        // printf("fopen err\n");
        perror("fopen err"); 
        return -1;
    }
    printf("fopen success\n");

    int ch = fgetc(fp);
    printf("%c\n", ch);
    //fgetc返回值为EOF时,是因为读到末尾还是因为调用失败,可以用这两个函数区分
    if(feof(fp))   //判断是否读到文件末尾
        printf("eof\n");
    if(ferror(fp))  //判断函数是否调用失败
        printf("error\n");
    return 0;
}

Supplément utilisation vscode :

  1. Copiez le dossier .vscode du répertoire de travail dans votre propre répertoire (fichier)
  2. Passer au répertoire supérieur du fichier
  3. fichier de code, ouvrez le répertoire avec vscode et écrivez le code
  4. Un raccourci:

1) ctrl+shift+i : alignement automatique du code

2) ctrl+/ : code commentaire

3) Suivi des codes :

ctrl + bouton gauche de la souris : poursuivre

alt + touche gauche du clavier : retour en arrière

2.2.2 Lire et écrire une ligne à la fois

char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取一串字符
参数:s:存放读取的字符串的首地址
     size:读取的大小
     stream:文件流
返回值:成功:读取的字符串的首地址
      失败或读到文件末尾:NULL
特性:1.实际读取size-1个字符,在末尾添加\0
     2.读到\n结束读取
int  fputs(const char *s,  FILE *stream);
功能:向文件中写字符串
参数:s:要写的内容
    stream:文件流
返回值:成功:非负整数
       失败:EOF

 Exercice : Programme pour réaliser la fonction de calcul du nombre de lignes dans un fichier (wc -l nom du fichier).

Exigences : implémenté à l'aide de fgets

Idée : ouvrez le fichier et lisez-le dans une boucle. Lorsque la fin du fichier est lue (la valeur de retour de fgets est NULL), la boucle se termine. Dans la boucle, jugez s'il y a \n dans la chaîne. Si c'est le cas \n, la variable n++ peut être utilisée

#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{

    FILE *fp;
    int n=0;
    char buf[32]="";

    fp=fopen("./i2.c","r");
    if(fp==NULL)
    {
        perror("fopen err");
        return -1;                                      
    }

    while(fgets(buf,30,fp)!=NULL)
    {
#if 0
            for(int i=0;buf[i]!='\0';i++)
            {
                if(buf[i]=='\n')
                n++;
            }
#endif
      if(buf[strlen(buf)-1]=='\n')
          n++;

    }
    printf("%d\n",n);
    return 0;
}
                                                        
                                                        

2.3 Fermeture du dossier

int fclose(FILE* stream);
功能:关闭文件
参数:stream:文件流

Exercice 1 : programme pour lire et écrire un fichier test.txt, écrire une ligne de données dans le fichier toutes les 1 seconde

Quelque chose comme ça:

1, 2007-7-30 15:16:42  

2, 2007-7-30 15:16:43

Le programme devrait boucler à l'infini jusqu'à ce que Ctrl-C soit pressé pour interrompre le programme.

Lorsque vous redémarrez le programme pour écrire un fichier, il peut être ajouté au fichier d'origine et le numéro de série peut continuer le numéro de série précédent, par exemple :

1, 2007-7-30 15:16:42

2, 2007-7-30 15:16:43

3, 2007-7-30 15:19:02

4, 2007-7-30 15:19:03

5, 2007-7-30 15:19:04

dormir(1);

fprintf/sprintf();

time(); //Temps de calcul, secondes

localtime(); // convertit les secondes en année, mois, jour, heure, minute, seconde

Idée : ouvrez le fichier, comptez le nombre de lignes, écrivez des chaînes dans le fichier en boucle et écrivez une ligne toutes les secondes

Remarque : cache complet

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    char buf[32] = "";
    int n = 0;
    fp = fopen("test.txt", "a+");
    if(fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    //判断行数
    while(fgets(buf, 32, fp) != NULL)
    {
        if(buf[strlen(buf)-1] == '\n')
            n++;
    }
    time_t tm;
    struct tm *t;
    while(1)
    {
        //计算时间
        // time(&tm);
        tm = time(NULL);
        t = localtime(&tm);
        fprintf(fp, "%d,%d-%d-%d %d-%d-%d\n", ++n,t->tm_year+1900,t->tm_mon+1,\
        t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
        fflush(NULL);
        sleep(1);
    }
    return 0;
}

Exercice 2 : Réaliser la fonction de tête

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    char buf[32] = "";
    int n = 0;
    int num = atoi(argv[1]+1); //argv[1]:"-15"
    fp = fopen(argv[2], "r");
    if(fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    while(fgets(buf, 32, fp) != NULL)
    {
        if(buf[strlen(buf)-1] == '\n')
            n++;
        printf("%s", buf);
        if(n == num)
            break;
    }
    return 0;
}

2.4 Lecture et écriture binaires

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素
参数:	ptr :用来存放读取元素
        size :元素大小  sizeof(数据类型)
		nmemb :读取对象的个数
		stream :要读取的文件
返回值:成功:读取对象的个数
      读到文件尾或失败:0
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:按对象写
参数:同上	
返回值:成功:写的元素个数
      失败 :-1
Fread和fwrite函数注意:
1)两个函数的返回值为:读或写的对象数
2)对于二进制数据我们更愿意一次读或写整个结构。
#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    int arr[3] = {10, 20, 30}, num[3] = {0};

    fp = fopen("a.c", "w+");
    if(fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    fwrite(arr, sizeof(int), 3, fp);
    //将文件位置移动到文件开头
    rewind(fp);
    fread(num, sizeof(int), 3, fp);
    for(int i = 0; i < 3; i++)
        printf("%d\n", num[i]);

    return 0;
}

2.5 Opération de localisation de fichier

void rewind(FILE *stream);
功能:将文件位置指针定位到起始位置
int fseek(FILE *stream, long offset, int whence);
功能:文件的定位操作
参数:stream:文件流
     offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
     whence:相对位置:
           SEEK_SET:相对于文件开头
           SEEK_CUR:相对于文件当前位置
           SEEK_END:相对于文件末尾
返回值:成功:0
        失败:-1   
注:当打开文件的方式为a或a+时,fseek不起作用              
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置,出错:-1
#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp;
    char buf[32] = "";
    int n = 0;
    fp = fopen("test.txt", "r+");
    if(fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    //将文件位置进行定位操作
    fseek(fp, 10, SEEK_SET); //相对文件开头向后偏移
    fputc('a', fp);

    fseek(fp, -5, SEEK_CUR); //相对文件当前向前偏移
    fputc('b', fp);

    long l = ftell(fp); //获取当前文件位置
    printf("%ld\n", l);

    //计算文件长度
    // fseek(fp, 0, SEEK_END);
    // l = ftell(fp);
    
    //rewind和fseek等价
    // rewind(fp); //fseek(fp, 0, SEEK_SET);

    return 0;
}

2.6 Redirection vers les fichiers ouverts

FILE * freopen(const char *pathname,  const char *mode,  FILE* fp)
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径
mode:打开文件的方式(同fopen)
      fp:文件流指针
返回值:成功:返回文件流指针
      失败:NULL
#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("hello\n");
    //将标准输出重定向到打开的文件
    freopen("test.txt", "r+", stdout);
    printf("world\n");
    //将标准输出重定向到终端
    freopen("/dev/tty", "r+", stdout);
    printf("nihao\n");

    return 0;
}

Exercice : Réaliser la fonction cp via des IO standard

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
    FILE *fd;
    FILE *fd1;
    char ch;
    fd=fopen(argv[1],"r");
    fd1=fopen(argv[2],"w+");
    if(fd==NULL)
    {
        perror("open eror\n");
        return -1;

    }

    if(fd1==NULL)
    {
        perror("open eror\n");
        return -1;

    }

    while((ch=fgetc(fd))!=EOF)
        fputc(ch,fd1);

    fclose(fd);
    fclose(fd1);
    return 0;
}                                               
                                                

Manuel Baidu+man

temps(temps_t *tm)

appel de fonction :

Paramètres : le nombre, le type et la signification du prototype de la fonction correspondent ; lorsque le paramètre dans le prototype de la fonction est un pointeur de premier niveau, il est nécessaire de définir une variable pour passer l'adresse

Valeur de retour : Toutes les fonctions n'ont pas besoin de recevoir une valeur de retour ; si vous avez besoin de recevoir une valeur de retour, quel est le type de valeur de retour du prototype de la fonction et quel type de variable ou de pointeur est défini dans le code à recevoir

2. Fichier IO

1. Conception

 1.1 Définition

Un ensemble de fonctions d'entrée et de sortie définies en posix (Portable Operating System Interface)

Appels système : un ensemble d'interfaces fournies par le noyau

 1.2 Caractéristiques

1) Il n'y a pas de mécanisme de tampon, chaque opération IO provoquera un appel système

2) Opérez autour des descripteurs de fichiers , des entiers non négatifs (int), et allouez-les tour à tour

3) Trois descripteurs de fichiers sont ouverts par défaut : 0 (entrée standard), 1 (sortie standard), 2 (erreur standard)

4) Peut utiliser n'importe quel type de fichier sauf d  

2. Interface de fonction

2.1 Ouvrir un fichier

int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
      flags:打开文件的方式
            O_RDONLY:只读
            O_WRONLY:只写
            O_RDWR:可读可写
            O_CREAT:创建
            O_TRUNC:清空
            O_APPEND:追加   
返回值:成功:文件描述符
        失败:-1
当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限 
int open(const char *pathname, int flags, mode_t mode);
创建出来的文件权限为指定权限值&(~umask)  //umask为文件权限掩码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int fd;
    char buf[32] = "";

    // fd = open("./a.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
    fd = open("test.txt", O_RDWR);
    if (fd < 0)
    {
        perror("open err");
        return -1;
    }
    printf("fd:%d\n", fd);
    //read返回值s表示实际读到的字符个数
    ssize_t s = read(fd, buf, 32);
    printf("%s\n", buf);
    printf("%d\n", s);
    
    write(fd, "nihao", 5);

    close(fd);

    return 0;
}

Comparaison : la relation correspondante entre les E/S standard et les E/S de fichiers dans la manière d'ouvrir des fichiers

E/S standards

fichier E/S

r

O_RDONLY

r+

O_RDWR

w

O_WRONLY|O_CREAT|O_TRUNC,0666

w+

O_RDWR|O_CREAT|O_TRUNC,0666

un

O_WRONLY|O_CREAT|O_APPEND,0666

un+

O_RDWR|O_CREAT|O_APPEND,0666

2.2 Lecture et écriture de fichiers

ssize_t read(int fd, void *buf, size_t count);
功能:从一个已打开的可读文件中读取数据
参数:fd  文件描述符
     buf  存放位置
    count  期望的个数
返回值:成功:实际读到的个数
      返回-1:表示出错,并设置errno号
      返回0:表示读到文件结尾
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:fd   文件描述符
          buf   要写的内容
          count  期望值
返回值:成功:实际写入数据的个数
              失败  : -1

Exercice : Réaliser la fonction cp.

cp srcfile nouveaufichier -> ./a.out srcfile nouveaufichier

Idée : ouvrez deux fichiers, lisez le fichier source et écrivez un nouveau fichier dans une boucle, et terminez la boucle lorsque la fin du fichier source est lue

diff nom_fichier1 nom_fichier2 : comparer deux fichiers pour l'égalité

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int fd_src, fd_new;
    char buf[32] = "";
    ssize_t s;
    fd_src = open(argv[1], O_RDONLY);
    fd_new = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0666);
    if(fd_src < 0 || fd_new < 0)
    {
        perror("open err");
        return -1;
    }
    while(1)
    {
        s = read(fd_src, buf, 32);
        if(s == 0)
            break;
        write(fd_new, buf, s);
    }
    close(fd_src);
    close(fd_new);
    return 0;
}

2.3 Fermeture du dossier

int close(int fd);
参数:fd:文件描述符

2.4 Emplacement du fichier

off_t lseek(int fd, off_t offset, int whence);
功能:设定文件的偏移位置
参数:fd:文件描述符
    offset偏移量  
        正数:向文件结尾位置移动
        负数:向文件开始位置
    whence  相对位置
        SEEK_SET   开始位置
        SEEK_CUR   当前位置
        SEEK_END   结尾位置
返回值:成功:文件的当前位置
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int fd;
    char buf[32] = "";

    fd = open("test.txt", O_RDWR);
    if (fd < 0)
    {
        perror("open err");
        return -1;
    }
    printf("fd:%d\n", fd);
    
    lseek(fd, 10, SEEK_SET);
    write(fd, "a", 1);

    off_t off = lseek(fd, 0, SEEK_CUR);
    printf("%ld\n", off);

    close(fd);

    return 0;
}

Exercice 2 : Réaliser les fonctions suivantes

   1-- Ouvrir un fichier, le créer s'il n'existe pas, l'effacer s'il existe 

    2-- écrire un caractère en position 10 dans le fichier,

    3-- Dans la position du fichier à ce moment, aux 20 dernières positions, écrivez-y une ligne de chaîne hello

    4-- Trouvez la longueur du fichier.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd;
char buf[32]="";
fd=open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd<0)
{
perror("open eror\n");
return -1;

}

lseek(fd,9,SEEK_SET);
write(fd,"i",1);

lseek(fd,19,SEEK_CUR);
write(fd,"hello",5);

off_t off=lseek(fd,0,SEEK_END);
printf("%ld\n",off);

close(fd);

    return 0;
}                                                 
                                                  
                                                  

2.5 Comparaison des E/S standard et des E/S fichier

E/S standards

fichier E/S

définition

Fonctions qui définissent l'entrée et la sortie dans la bibliothèque C

Fonctions d'entrée et de sortie définies en posix

caractéristiques

Il existe un mécanisme tampon

Opérations autour des flux, FILE*

Trois flux sont ouverts par défaut : stdin/stdout/stderr

Ne peut utiliser que des fichiers ordinaires

pas de mécanisme tampon

Fonctionne autour des descripteurs de fichiers, int entier non négatif

Trois descripteurs de fichiers sont ouverts par défaut : 0/1/2

Tout type de fichier sauf d

interface fonctionnelle

Ouvrir un fichier : fopen/freopen

Lire et écrire des fichiers : fgetc/fputc, fgets/fputs, fread/fwrite

Fermer un fichier : fclose

Positionnement des fichiers : rembobiner, fseek, ftell

Ouvrir un fichier : ouvrir

Lire et écrire des fichiers : lire, écrire

Fermer le fichier : fermer

Positionnement du fichier : lseek

2.6 Acquisition des attributs du fichier

int stat(const char *path, struct stat *buf);
功能:获取文件属性
参数:path:文件路径名
       buf:保存文件属性信息的结构体
返回值:成功:0
      失败:-1
struct stat {
        ino_t     st_ino;     /* inode号 */
        mode_t    st_mode;    /* 文件类型和权限 */
        nlink_t   st_nlink;   /* 硬链接数 */
        uid_t     st_uid;     /* 用户ID */
        gid_t     st_gid;     /* 组ID */
        off_t     st_size;    /* 大小 */
        time_t    st_atime;   /* 最后访问时间 */
        time_t    st_mtime;   /* 最后修改时间 */
        time_t    st_ctime;  /* 最后状态改变时间 */
    };
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    struct stat st;
    if (stat("./test.c", &st) < 0)
    {
        perror("stat err");
        return -1;
    }
    printf("%lu\n", st.st_ino);
    printf("%d\n", st.st_nlink);
    //判断文件类型
    printf("%#o\n", st.st_mode);
    if((st.st_mode & S_IFMT) == S_IFREG)
        putchar('-');
    else if((st.st_mode & S_IFMT) == S_IFDIR)
        putchar('d');
    //判断文件权限
    if(st.st_mode & S_IRUSR)
        putchar('r');
    else 
        putchar('-');
     if(st.st_mode & S_IWUSR)
        putchar('w');
    else 
        putchar('-');
    //获取用户名和组名
    //getpwuid();//将用户ID转换成用户名
    //getgrgid();//将组ID转换成组名

    //时间
    //st_mtime:最后一次修改时间,s
    //localtime();

    return 0;
}

2.7 Opérations sur les répertoires

Fonctionne autour des flux de répertoire, DIR *

DIR *opendir(const char *name);
功能:获得目录流
参数:要打开的目录
返回值:成功:目录流
       失败:NULL
struct dirent *readdir(DIR *dirp);
功能:读目录
参数:要读的目录流
返回值:成功:读到的信息    
      失败或读到目录结尾:NULL
返回值为结构体,该结构体成员为描述该目录下的文件信息
struct dirent {
        ino_t   d_ino;                   /* 索引节点号*/
        off_t   d_off;               /*在目录文件中的偏移*/
        unsigned short d_reclen;    /* 文件名长度*/
        unsigned char  d_type;      /* 文件类型 */
        char    d_name[256];      /* 文件名 */
};
int closedir(DIR *dirp);
功能:关闭目录
参数:dirp:目录流
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char const *argv[])
{
    DIR *dir;
    struct dirent *d;

    dir = opendir(".");
    if (dir == NULL)
    {
        perror("opendir err");
        return -1;
    }
    while((d = readdir(dir)) != NULL)
    {
        if(d->d_name[0] != '.') 
            printf("%s\n", d->d_name);
    }
    // d = readdir(dir);
    // printf("%s\n", d->d_name);
    // d = readdir(dir);
    // printf("%s\n", d->d_name);

    closedir(dir);

    return 0;
}

Exercice : Programme pour réaliser la fonction ls

Trois, bibliothèque

1. Définition de bibliothèque

Lorsque vous utilisez les fonctions d'autres personnes, en plus d'inclure le fichier d'en-tête, il doit y avoir une bibliothèque

  Bibliothèque : Il s'agit de regrouper les fichiers objets de certaines fonctions couramment utilisées et de fournir l'interface de la fonction correspondante, ce qui est pratique pour les programmeurs ; en substance, la bibliothèque est une forme binaire de code exécutable

En raison de la nature différente de Windows et de Linux, les binaires des deux bibliothèques ne sont pas compatibles

2. Classement des bibliothèques

La différence essentielle entre les bibliothèques statiques et les bibliothèques dynamiques est que le code est chargé à des moments différents.

1) La bibliothèque statique sera liée au code objet lors de la compilation du programme.

Avantages : la bibliothèque statique ne sera plus nécessaire lorsque le programme est en cours d'exécution ; il n'est pas nécessaire de charger la bibliothèque lors de l'exécution et la vitesse d'exécution est plus rapide

Inconvénient : le code de la bibliothèque statique est copié dans le programme, donc le volume est important ;

   Une fois la bibliothèque statique mise à niveau, le programme doit être recompilé et lié

2) La bibliothèque dynamique est chargée dans le code lorsque le programme est en cours d'exécution.

            Avantages : Le programme charge la bibliothèque dynamique pendant l'exécution et la taille du code est petite ;

 La mise à jour du programme est plus simple ;

Si différentes applications appellent la même bibliothèque, il suffit qu'il y ait une instance de la bibliothèque partagée en mémoire.

Inconvénients : l'existence de bibliothèques dynamiques est également requise à l'exécution et la portabilité est médiocre

3. Production de la bibliothèque

3.1 Production de bibliothèque statique

1- Compiler le fichier source pour générer le fichier cible

gcc -c add.c -o add.o

2- Créez une bibliothèque statique avec la commande ar, elle convertit de nombreux .o en .a

ar crs libmyadd.a add.o 

La convention de dénomination du nom de fichier de la bibliothèque statique est préfixée par lib, suivi du nom de la bibliothèque statique et l'extension est .a

3- Test en utilisant la bibliothèque statique :

gcc main.c -L. -lmyadd // -L spécifie le chemin de la bibliothèque -l spécifie le nom de la bibliothèque

exécuter ./a.out

3.2 Production de bibliothèque dynamique

1- Nous utilisons gcc pour créer la bibliothèque partagée

gcc -fPIC -c bonjour.c -o bonjour.o

-fPIC Créer un compilateur indépendant de l'adresse

gcc -shared -o libmyhello.so hello.o

2- Tester l'utilisation de la bibliothèque dynamique

gcc main.c -L. -lmyhello

Il peut être compilé normalement, mais une erreur est signalée lors de l'exécution./a.out : erreur lors du chargement des bibliothèques partagées : libmyadd.so : impossible d'ouvrir le fichier objet partagé : aucun fichier ou répertoire de ce type

Raison : Lors du chargement d'une bibliothèque dynamique, le système recherchera les fichiers de bibliothèque à partir de /lib ou /usr/lib par défaut

Solutions de contournement (il y en a trois):

(1) Copiez la bibliothèque dans les répertoires /usr/lib et /lib . (Cette méthode n'a pas besoin de spécifier le chemin de la bibliothèque lors de la compilation) 

(2) Ajoutez le chemin de la bibliothèque à la variable d'environnement LD_LIBRARY_PATH. 

exporter LD_LIBRARY_PATH=$LD_LIBRARY_PATH :. 

(Lorsque le terminal est fermé, la variable d'environnement a disparu)

(3) Ajoutez les fichiers /etc/ld.so.conf.d/*.conf. Ajoutez le chemin où se trouve la bibliothèque à la fin du fichier et exécutez ldconfig refresh

sudo vi xx.conf

Ajoutez le chemin où se trouve la bibliothèque dynamique, par exemple :

/home/hq/teach/22092/day3/dynamique

Remplir:

fichier de tête :

Placez-le dans le répertoire courant : #include "xx.h", recherchez le fichier dans le chemin actuel, sinon, recherchez dans le répertoire système

Mettez-le dans le répertoire système : #include, recherchez à partir du chemin système par défaut, chemin système : /usr/include

Mettez-le dans un autre répertoire : #include "xx.h", ajoutez l'option -I (i majuscule) pour spécifier le chemin du fichier d'en-tête lors de la compilation du code avec gcc

gcc main.c -I chemin du fichier d'en-tête

Fichiers de bibliothèque : les bibliothèques dynamiques sont placées dans le répertoire système

Chemins système : /usr/lib et /lib

gcc doit ajouter des options lors de la compilation

-L chemin : spécifiez le chemin de la bibliothèque

-l nom de la bibliothèque : (L minuscule) spécifie le nom de la bibliothèque

-I path : (i majuscule) spécifie le chemin d'accès au fichier d'en-tête

4. Processus

1. Conception :

1.1 Différence entre programme et processus :

Programme : exécutable compilé

Une collection ordonnée d'instructions et de données stockées sur disque (fichier)

Les programmes sont statiques, sans aucune notion d'exécution

Processus : une tâche planifiable indépendante

Terme général désignant les ressources allouées à l'exécution d'un programme

Un processus est une exécution d'un programme

Les processus sont dynamiques, y compris la création, la planification, l'exécution et la mort

1.2 Caractéristiques

  1. Le système allouera 0-4g d'espace virtuel pour chaque processus, dont 0-3g est l'espace utilisateur, unique à chaque processus ; 3g-4g est l'espace noyau, partagé par tous les processus
  2. Ordonnancement circulaire : tranche de temps, le système alloue une tranche de temps (quelques millisecondes à dizaines de millisecondes) pour chaque processus, lorsqu'une tranche de temps de processus est épuisée, le CPU programme un autre processus, réalisant ainsi la commutation de la planification de processus

1.3. Segment de processus :

Un processus sous Linux se compose de trois segments :

Le "segment de données" stocke les variables globales, les constantes et l'espace de données alloué par les données dynamiques (comme l'espace obtenu par la fonction malloc), etc.

Le "segment de texte" stocke le code dans le programme

      Le "segment de pile" stocke l'adresse de retour de la fonction, les paramètres de la fonction et les variables locales dans le programme

1.4. Classement des processus :

Processus interactif : ce type de processus est contrôlé et exécuté par le shell. Les processus interactifs peuvent s'exécuter au premier plan ou en arrière-plan. Ce type de processus interagit souvent avec les utilisateurs et doit attendre l'entrée de l'utilisateur. Lors de la réception d'une entrée utilisateur, ce type de processus répondra immédiatement. Les processus interactifs typiques incluent : processus de commande shell, éditeur de texte, etc.

Traitement par lots : ce type de processus n'appartient pas à un certain terminal, il est soumis à une file d'attente pour une exécution séquentielle.

Processus démon : ce type de processus s'exécute en arrière-plan. Il commence généralement à s'exécuter lorsque Linux démarre et se termine lorsque le système est arrêté

1.5. État du processus :

1) État d'exécution (TASK_RUNNING) : R

Fait référence à l'état d'exécution ou de préparation par la CPU. Un tel processus est appelé un processus en cours d'exécution.

2) État de veille (état d'attente) :

État de veille interruptible (TASK_INTERRUPTIBLE) S : Un processus dans l'état d'attente, une fois que les ressources que le processus attend sont libérées, le processus entrera dans l'état d'exécution.

État de veille sans interruption (TASK_UNINTERRUPTIBLE) D : Le processus dans cet état ne peut être réveillé qu'avec la fonction wake_up().

3) Etat de pause (TASK_STOPPED) : T

Lorsque le processus reçoit le signal SIGSTOP, SIGTSTP, SIGTTIN ou SIGTTOU, il entrera dans l'état suspendu. Un signal SIGCONT peut lui être envoyé pour faire passer le processus à un état exécutable.

4) État de mort : le processus se termine X

5) État zombie : Z Lorsque le processus est terminé, mais occupe encore des ressources système, il faut éviter la génération d'état zombie

 < haute priorité

 N faible priorité

 s chef de groupe de session

 l Multithreading

 + processus de premier plan

1.6. Diagramme de commutation d'état de processus

Une fois le processus créé, le processus passe à l'état prêt. Lorsque la CPU planifie ce processus, il passe à l'état d'exécution. Lorsque la tranche de temps est épuisée, le processus passe à l'état prêt. Si le processus effectue des opérations d'E/S ( bloquant les opérations), il entre dans l'état de blocage, après avoir terminé l'opération d'E/S (fin de blocage), il peut entrer à nouveau dans l'état prêt, en attendant la planification de la CPU, et entrer dans l'état final lorsque le processus se termine.

2. Fonction :

2.1 Créer un processus

pid_t fork(void);
功能:创建子进程
返回值:
    成功:在父进程中:返回子进程的进程号 >0
         在子进程中:返回值为0
    失败:-1并设置errno
#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int num = 10;
    pid_t id;
    id = fork(); //创建子进程
    if(id < 0)
    {
        perror("fork err");
        return -1;
    }
    else if(id == 0)
    {
        //in the child
        printf("in the child\n");
    }
    else
    {
        // int s;
        // wait(&s); //回收子进程资源,阻塞函数
        // printf("%d\n", s);
        wait(NULL);
        //in the parent
        printf("in the parent\n");
        while(1);
    }
    // while(1);
    
    return 2;
}

Caractéristiques :

1) Le processus enfant copie presque tout le contenu du processus parent. Y compris le code, les données, la valeur pc dans le segment de données système, les données dans la pile, les fichiers ouverts dans le processus parent, etc. ; mais leur PID et PPID sont différents.

2) Les processus parent et enfant ont des espaces d'adressage indépendants et ne s'affectent pas ; lorsque des variables globales et des variables statiques sont modifiées dans le processus correspondant, elles ne s'affectent pas mutuellement.

3) Si le processus parent se termine en premier, le processus enfant devient un processus orphelin , adopté par le processus init, et le processus enfant devient un processus d'arrière-plan.

4) Si le processus enfant se termine en premier, si le processus parent n'est pas recyclé à temps, le processus enfant deviendra un processus zombie (pour éviter la génération de processus zombie)

2.2 Récupérer les ressources du processus

pid_t wait(int *status);
功能:回收子进程资源,阻塞函数,等待子进程退出后结束阻塞
参数:status:子进程退出状态,不接受子进程状态设为NULL
返回值:成功:回收的子进程的进程号
        失败:-1
pid_t waitpid(pid_t pid, int *status, int options);
功能:回收子进程资源
参数:
    pid:>0     指定子进程进程号
         =-1   任意子进程
         =0    等待其组ID等于调用进程的组ID的任一子进程
         <-1   等待其组ID等于pid的绝对值的任一子进程
    status:子进程退出状态
    options:0:阻塞
        WNOHANG:非阻塞
返回值:正常:结束的子进程的进程号
      当使用选项WNOHANG且没有子进程结束时:0
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char const *argv[])
{
    pid_t id;
    id = fork(); //创建子进程
    if (id < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (id == 0)
    {
        sleep(1);
        //in the child
        printf("in the child\n");
    }
    else
    {
        //IO模型:阻塞IO\非阻塞IO
        // waitpid(-1, NULL, 0); //wait(NULL);
        //轮询(循环)
        while(1)
        {
            if(waitpid(id, NULL, WNOHANG)!=0) //WNOHANG:表示非阻塞
                break;
        }
        //in the parent
        printf("in the parent\n");
        while (1)
            ;
    }

    return 0;
}

2.3 Terminer le processus

void exit(int status);
功能:结束进程,刷新缓存
void _exit(int status);
功能:结束进程,不刷新缓存
参数:status是一个整型的参数,可以利用这个参数传递进程结束时的状态。
    通常0表示正常结束;
其他的数值表示出现了错误,进程非正常结束

La différence entre sortie et retour :

exit : que ce soit dans la sous-fonction ou dans la fonction principale, le processus peut être terminé

return : lorsqu'il y a un retour dans la sous-fonction, retour à l'emplacement de l'appel de la fonction sans terminer le processus

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int fun()
{
    printf("in fun\n");
    exit(0);
    // return 0;
}
int main(int argc, char const *argv[])
{
    printf("hello\n");
    // fun();
    // printf("world\n");
    // return 0;
    // exit(0); //结束进程,刷新缓存
    // _exit(0); //结束进程,不刷新缓存区

    // execl("/bin/ls", "ls", "-l", NULL);

    // char *arg[] = {"ls", "-l", NULL};
    // execv("/bin/ls", arg);

    while(1);

    return 0;
}

2.4 Obtenir le numéro de processus

pid_t getpid(void);
功能:获取当前进程的进程号
pid_t getppid(void);
功能:获取当前进程的父进程号
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    pid_t id;
    if ((id = fork()) < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (id == 0)
    {
        printf("in child, pid:%d ppid:%d\n", getpid(), getppid());
    }
    else
    {
        printf("in parent, pid:%d ppid:%d\n", id, getpid());
    }

    return 0;
}

fonction exec - compréhension

system("ls -l");

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int fun()
{
    printf("in fun\n");
    exit(0);
    // return 0;
}
int main(int argc, char const *argv[])
{
    printf("hello\n");
    // fun();
    // printf("world\n");
    // return 0;
    // exit(0); //结束进程,刷新缓存
    // _exit(0); //结束进程,不刷新缓存区

    // execl("/bin/ls", "ls", "-l", NULL);

    // char *arg[] = {"ls", "-l", NULL};
    // execv("/bin/ls", arg);

    while(1);

    return 0;
}

2.5 Démons

1. Caractéristiques :

Le processus démon est un processus d'arrière-plan et ne dépend pas du terminal de contrôle ;

Le cycle de vie est relativement long, commençant à l'exécution et se terminant lorsque le système est arrêté ;

C'est un processus qui se détache du terminal de contrôle et s'exécute périodiquement.

2. Étapes :

1) Créer un processus enfant, le processus parent se termine

Laisser le processus enfant devenir un processus orphelin et devenir un processus d'arrière-plan ; fork()

2) Créer une nouvelle session dans le processus enfant

Laissez le processus enfant devenir le leader du groupe de session, afin de séparer complètement le processus enfant du terminal ; setsid()

3) Modifiez le chemin d'exécution du processus vers le répertoire racine

Raison Le chemin où le processus est en cours d'exécution ne peut pas être déchargé ; chdir("/")

4) Réinitialiser le masque d'autorisation de fichier

Objectif : augmenter les autorisations du processus lors de la création de fichiers et améliorer la flexibilité ; umask(0)

5) Fermez le descripteur de fichier

Fermez les fichiers inutiles ; close()

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    pid_t id;
    if ((id = fork()) < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (id == 0)
    {
        //在子进程中创建新会话
        setsid();
        //修改进程运行路径为根目录
        chdir("/");
        //修改文件权限掩码
        umask(0);
        //关闭文件描述符
        for (int i = 0; i < 2; i++)
            close(i);
        int fd;
        fd = open("/tmp/info.log", O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (fd < 0)
        {
            perror("open err");
            return -1;
        }
        while (1)
        {
            write(fd, "hello", 5);
            sleep(1);
        }
    }
    else
    {
        exit(0);
    }
    return 0;
}

Cinq, fil

1. Conception

          C'est un processus léger qui introduit des threads afin d'améliorer les performances du système

           Linux utilise également task_struct pour décrire un thread.

           Les threads et les processus participent à la planification unifiée.

           Les threads créés dans le même processus partagent l'espace d'adressage de ce processus.

2. La différence entre thread et processus

Points communs : les deux fournissent des capacités d'exécution simultanées pour le système d'exploitation

différence:

Planification et ressources : le thread est la plus petite unité de planification du système et le processus est la plus petite unité d'allocation des ressources.

En termes d'espace d'adressage : plusieurs threads créés par le même processus partagent les ressources du processus ; les espaces d'adressage des processus sont indépendants les uns des autres

En termes de communication : la communication par thread est relativement simple et ne peut être réalisée qu'au travers de variables globales , mais le problème de la protection des ressources critiques doit être considéré ; la communication par processus est plus compliquée et nécessite l'utilisation de mécanismes de communication inter-processus (avec l'aide d'espace noyau 3g-4g)

En termes de sécurité : la sécurité des threads est médiocre, lorsque le processus se termine, tous les threads se terminent ; le processus est relativement sûr

3. Fonction de fil

3.1 Créer un fil

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
                    void *(*start_routine) (void *), void *arg);
功能:创建线程
参数:thread:线程标识
     attr:线程属性, NULL:代表设置默认属性
     start_routine:函数名:代表线程函数
     arg:用来给前面函数传参
返回值:成功:0
       失败:错误码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

char buf[32] = "";
//线程函数
void *handler(void *arg)
{
    sleep(1);
    printf("in the thread\n");
    printf("num:%d\n", *((int *)arg));
    printf("buf:%s\n", buf);
    pthread_exit(NULL); //结束线程
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    int num = 100;
    if(pthread_create(&tid, NULL, handler, &num) != 0)
    {
        perror("create thread err");
        return -1;
    }
    printf("in the main\n");
    printf("main tid:%lu\n", pthread_self());
    strcpy(buf, "hello");
    //线程回收,阻塞函数,等待子线程结束,回收线程资源
    pthread_join(tid, NULL);

    return 0;
}

3.2 Fin du fil

int  pthread_exit(void *value_ptr) 
功能:用于退出线程的执行
参数:value_ptr:线程退出时返回的值
返回值:成功 : 0
        失败:errno

3.3 Recyclage des fils

int  pthread_join(pthread_t thread,  void **value_ptr) 
功能:用于等待一个指定的线程结束,阻塞函数
参数:thread:创建的线程对象
        value_ptr:指针*value_ptr指向线程返回的参数
返回值:成功 : 0
      失败:errno

3.4 Obtenir le numéro de fil

pthread_t pthread_self(void);
功能:获取线程号
返回值:线程ID

3.5 Séparation des fils

int pthread_detach(pthread_t thread);
功能:让线程分离,线程退出让系统自动回收线程资源

Exercice : Implémenter la communication via les threads.

Le thread principal boucle pour entrer des données à partir du terminal, et le sous-thread boucle pour imprimer les données et termine le programme lorsque quit est entré.

Exigences : entrée d'abord, puis sortie

Astuce : flag bit int flag = 0 ;

#include <stdio.h>
#include <pthread.h>
#include <string.h>

char buf[32] = "";
int flag = 0;
void *print(void *arg)
{
    while (1)
    {
        if (flag == 1)
        {
            if (strcmp(buf, "quit") == 0)
                break;
            printf("buf:%s\n", buf);
            flag = 0;
        }
    }
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, print, NULL) != 0)
    {
        perror("create thread err");
        return -1;
    }
    while (1)
    {
        scanf("%s", buf);
        flag = 1;
        if (strcmp(buf, "quit") == 0)
            break;
    }
    pthread_join(tid, NULL);

    return 0;
}

4. Synchronisation des fils

4.1 Conception

La synchronisation fait référence à plusieurs tâches (threads) coopérant les unes avec les autres dans un ordre convenu pour accomplir une chose

4.2 Mécanisme de synchronisation

La synchronisation entre les threads est réalisée via des sémaphores.

Sémaphore : le sémaphore détermine si le thread continue de s'exécuter ou bloque l'attente. Le sémaphore représente un certain type de ressource et sa valeur représente le nombre de ressources dans le système.

Un sémaphore est une variable protégée accessible uniquement via trois opérations : initialisation, opération P (demander des ressources), opération V (libérer des ressources)

La valeur du sémaphore est un entier non négatif

4.3 Caractéristiques

Opération P :

Lorsque la valeur du sémaphore est supérieure à 0, la ressource peut être demandée et la valeur du sémaphore est réduite de 1 après l'application de la ressource.

Lorsque la valeur du sémaphore est égale à 0, la ressource ne peut pas être appliquée, et les blocs fonction

Opération V :

Non bloquant, tant que l'opération de libération n'est pas exécutée, la valeur du sémaphore est augmentée de 1

4.4 Fonctions

int  sem_init(sem_t *sem,  int pshared,  unsigned int value)  
功能:初始化信号量   
参数:sem:初始化的信号量对象
    pshared:信号量共享的范围(0: 线程间使用   非0:1进程间使用)
    value:信号量初值
返回值:成功 0
      失败 -1
int  sem_wait(sem_t *sem)  
功能:申请资源  P操作 
参数:sem:信号量对象
返回值:成功 0
      失败 -1
注:此函数执行过程,当信号量的值大于0时,表示有资源可以用,则继续执行,同时对信号量减1;当信号量的值等于0时,表示没有资源可以使用,函数阻塞
int  sem_post(sem_t *sem)   
功能:释放资源  V操作      
参数:sem:信号量对象
返回值:成功 0
      失败 -1
注:释放一次信号量的值加1,函数不阻塞

#include <stdio.h>
#include <pthread.h>
#include <string.h>

char buf[32] = "";
int flag = 0;
void *print(void *arg)
{
    while (1)
    {
        if (flag == 1)
        {
            if (strcmp(buf, "quit") == 0)
                break;
            printf("buf:%s\n", buf);
            flag = 0;
        }
    }
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, print, NULL) != 0)
    {
        perror("create thread err");
        return -1;
    }
    while (1)
    {
        scanf("%s", buf);
        flag = 1;
        if (strcmp(buf, "quit") == 0)
            break;
    }
    pthread_join(tid, NULL);

    return 0;
}

5. Fil d'exclusion mutuelle

5.1 Conception

Ressources critiques : ressources qui ne peuvent être utilisées que par un processus à la fois

Section critique : fait référence à un fragment de programme qui accède à des ressources partagées

Exclusion mutuelle : lorsque plusieurs threads accèdent à des ressources critiques, un seul thread peut y accéder en même temps

     Verrou d'exclusion mutuelle : le mécanisme d'exclusion mutuelle peut être réalisé via le verrou d'exclusion mutuelle, qui est principalement utilisé pour protéger les ressources critiques. Chaque ressource critique est protégée par un verrou mutex. Le thread doit d'abord obtenir le verrou mutex pour accéder à la ressource critique. Après avoir accédé à la ressource Libérez le verrou. Si le verrou ne peut pas être acquis, le thread se bloque jusqu'à ce que le verrou soit acquis.

5.2 Interface de fonction

int  pthread_mutex_init(pthread_mutex_t  *mutex, pthread_mutexattr_t *attr)  
功能:初始化互斥锁  
参数:mutex:互斥锁
    attr:  互斥锁属性  //  NULL表示缺省属性
返回值:成功 0
      失败 -1
int  pthread_mutex_lock(pthread_mutex_t *mutex)   
功能:申请互斥锁     
参数:mutex:互斥锁
返回值:成功 0
      失败 -1
注:和pthread_mutex_trylock区别:pthread_mutex_lock是阻塞的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回
int  pthread_mutex_unlock(pthread_mutex_t *mutex)   
功能:释放互斥锁     
参数:mutex:互斥锁
返回值:成功 0
      失败 -1
int  pthread_mutex_destroy(pthread_mutex_t  *mutex)  
功能:销毁互斥锁     
参数:mutex:互斥锁

Cas : tableau global int a[10] = {} ;

t1 : boucle sur les éléments du tableau inversé

t2 : imprime cycliquement les éléments du tableau

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int a[10] = {0,1,2,3,4,5,6,7,8,9};
pthread_mutex_t lock;
void *print_handler(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        for(int i = 0; i < 10; i++)
            printf("%d", a[i]);
        printf("\n");
        pthread_mutex_unlock(&lock);
        sleep(1);
    }
}
void *swap_handler(void *arg)
{
    int t;
    while(1)
    {
        pthread_mutex_lock(&lock);
        for(int i = 0; i < 5; i++)
        {
            t = a[i];
            a[i] = a[9-i];
            a[9-i] = t;
        }
        pthread_mutex_unlock(&lock);
    }
}
int main(int argc, char const *argv[])
{
    pthread_t t1, t2;
    pthread_create(&t1, NULL, print_handler, NULL);
    pthread_create(&t2, NULL, swap_handler, NULL);

    if(pthread_mutex_init(&lock, NULL) != 0)
    {
        perror("mutex init err");
        return -1;
    }

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return 0;
}

5.3 Blocage

Il fait référence à un phénomène de blocage causé par deux ou plusieurs processus/threads pendant le processus d'exécution en raison de la concurrence pour les ressources ou de la communication entre eux. S'il n'y a pas de force externe, ils ne pourront pas avancer.

Quatre conditions nécessaires pour qu'une impasse se produise :

1. Utilisation mutuellement exclusive, c'est-à-dire que lorsqu'une ressource est utilisée (occupée) par un thread, les autres threads ne peuvent pas l'utiliser

2. Il ne peut pas être anticipé.Le demandeur de ressources ne peut pas saisir de force les ressources de l'occupant de la ressource, et la ressource ne peut être libérée que par l'occupant de la ressource.

3. Request and hold, c'est-à-dire lorsque le demandeur de ressources demande d'autres ressources tout en conservant la possession de la ressource d'origine.

4. Attente circulaire, c'est-à-dire qu'il y a une file d'attente : P1 occupe les ressources de P2, P2 occupe les ressources de P3 et P3 occupe les ressources de P1. Cela forme une boucle d'attente.

Remarque : lorsque les quatre conditions ci-dessus sont toutes vraies, un blocage est formé. Bien sûr, dans le cas d'un blocage, si l'une des conditions ci-dessus est brisée, le blocage peut disparaître.

6. Variables conditionnelles

Utiliser avec mutex pour réaliser le mécanisme de synchronisation

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
功能:初始化条件变量
参数:cond:是一个指向结构pthread_cond_t的指针
    restrict attr:是一个指向结构pthread_condattr_t的指针,一般设为NULL
返回值:成功:0 失败:非0
int pthread_cond_wait(pthread_cond_t *restrict cond,    pthread_mutex_t *restrict mutex);
功能:等待条件的产生
参数:restrict cond:要等待的条件
     restrict mutex:对应的锁
返回值:成功:0,失败:不为0
注:当没有条件产生时函数会阻塞,同时会将锁解开;如果等待到条件产生,函数会结束阻塞同时进行上锁。
int pthread_cond_signal(pthread_cond_t *cond);
功能:产生条件变量
参数:cond:条件变量值
返回值:成功:0,失败:非0
注:必须等待pthread_cond_wait函数先执行,再产生条件才可以
int pthread_cond_destroy(pthread_cond_t *cond);
功能:将条件变量销毁
参数:cond:条件变量值
返回值:成功:0, 失败:非0

pthread_mtex_init(&lock, NULL);

Cas : Exemples de dépôt et de retrait d'argent

Le thread principal dépose de l'argent de manière cyclique et le sous-thread retire de l'argent de manière cyclique, en retirant 100 à chaque fois jusqu'à ce que le solde soit à 0, puis en déposant de l'argent;

#include <stdio.h>
#include <pthread.h>
int money = 0;
pthread_mutex_t lock;
pthread_cond_t cond, cond1;
//取钱
void *getMoney(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        if(money < 100)
            pthread_cond_wait(&cond, &lock);
        money -= 100;
        printf("money:%d\n", money);
        if(money < 100)
            pthread_cond_signal(&cond1);
        pthread_mutex_unlock(&lock);
    }
}
int main(int argc, char const *argv[])
{
    pthread_t t;
    if(pthread_create(&t, NULL, getMoney, NULL) != 0)
    {
        perror("create thread err");
        return -1;
    }
    //互斥锁
    pthread_mutex_init(&lock, NULL);
    //条件变量
    if(pthread_cond_init(&cond, NULL) != 0)
    {
        perror("cond init err");
        return -1;
    }
    if(pthread_cond_init(&cond1, NULL) != 0)
    {
        perror("cond init err");
        return -1;
    }
    //存钱
    while(1)
    {
        pthread_mutex_lock(&lock);
        if(money >= 100)
            pthread_cond_wait(&cond1, &lock);
        scanf("%d", &money); //50
        if(money >= 100)
            pthread_cond_signal(&cond); //产生条件
        pthread_mutex_unlock(&lock);  
    }
    pthread_join(t, NULL);
    return 0;
}

6. Communication inter-processus

7 sortes

6 sortes

Méthodes traditionnelles de communication interprocessus :

canal sans nom, canal nommé, signal

objets IPC système V :

Mémoire partagée, files d'attente de messages, ensembles de sémaphores

BDS :

prise

1. Pipe anonyme

1.1 Caractéristiques

a. Ne peut être utilisé que pour la communication entre processus avec affinité

B. Mode de communication semi-duplex, avec des extrémités de lecture et d'écriture fixes

c. Le pipeline peut être considéré comme un fichier spécial et les E/S de fichier telles que les fonctions de lecture et d'écriture peuvent être utilisées pour sa lecture et son écriture.

d. Pipeline est une méthode de communication basée sur des descripteurs de fichiers. Lorsqu'un tube est établi, il crée deux descripteurs de fichier

fd[0] et fd[1]. Parmi eux, fd[0] est fixé pour la lecture du pipeline et fd[1] est fixé pour l'écriture du pipeline.

1.2 Interface de fonction

int pipe(int fd[2])
功能:创建无名管道
参数:文件描述符 fd[0]:读端  fd[1]:写端
返回值:成功 0
      失败 -1
#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int fd[2] = {0};
    char buf[65536] = "";
    //创建无名管道
    if(pipe(fd) < 0)
    {
        perror("pipe err");
        return -1;
    }
    printf("%d %d\n", fd[0], fd[1]);

    //对管道进行读写
    // write(fd[1], "hello", 5);
    // read(fd[0], buf, 32);
    // printf("%s\n", buf);
    //1.当管道中没有数据时,读阻塞
    // close(fd[1]); //关闭写端
    // ssize_t s = read(fd[0], buf, 32);
    // printf("%s %d\n", buf, s);
    //2.当写满管道时,写阻塞,当至少读出4k空间时,才可以继续写
    //管道大小:64k
    // write(fd[1], buf, 65536);
    // read(fd[0], buf, 4096);
    // printf("befor\n");
    // write(fd[1], "a", 1);
    // printf("after\n");
    //3.当读端关闭,写管道时,会导致管道破裂
    close(fd[0]);
    write(fd[1], "hello", 5); //SIGPIPE
    printf("after\n");

    return 0;
}

1.3 Précautions

A. Lorsqu'il n'y a pas de données dans le pipeline, l'opération de lecture sera bloquée ;

    Lorsqu'il n'y a pas de données dans le pipeline, fermez la fin de l'écriture et l'opération de lecture reviendra immédiatement

b. Le pipeline est plein (la taille du pipeline est de 64 Ko) et l'écriture de données est bloquée. Une fois qu'il y a 4 Ko d'espace, l'écriture se poursuit

c. Il n'est logique d'écrire des données dans le tube que si l'extrémité de lecture du tube existe. Sinon, le tube se cassera et le processus écrivant des données dans le tube recevra un signal SIGPIPE du noyau (généralement une erreur de tube cassé).

Exercice : Communication de processus parent-enfant.

Le processus parent entre cycliquement des chaînes à partir du terminal et le processus enfant sort cycliquement les chaînes.Lorsque quit est entré, le programme se termine.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>


int main(int argc, char const *argv[])
{
    char buf[32] = "";
    pid_t id;
    int fd[2] = {0};
    if(pipe(fd) < 0)
    {
        perror("pipe err");
        return -1;
    }
    if ((id = fork()) < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (id == 0)
    {
        while(1)
        {
            char buf[32] = "";
            read(fd[0], buf, 32);
            if(strcmp(buf, "quit") == 0)
                break;
            printf("buf:%s\n", buf);
        }
        exit(0);
    }
    else
    {
        while(1)
        {
            char buf[32] = "";
            // scanf("%s", buf);
            fgets(buf, 32, stdin);
            write(fd[1], buf, strlen(buf)+1);
            if(strcmp(buf, "quit") == 0)
                break;
        }
        wait(NULL);
    }

    return 0;
}

2. Pipeline célèbre

2.1 Caractéristiques

a. Un canal nommé permet à deux processus non liés de communiquer entre eux.

b. Un canal nommé peut être identifié par un nom de chemin et est visible dans le système de fichiers, mais son contenu est stocké en mémoire.

c. Les processus exploitent des canaux bien connus via le fichier IO

d. Les canaux nommés suivent la règle du premier entré, premier sorti

e. Ne prend pas en charge les opérations telles que lseek()

2.2 Interface de fonction

int mkfifo(const char *filename,mode_t mode);
功能:创健有名管道
参数:filename:有名管道文件名
       mode:权限
返回值:成功:0
       失败:-1,并设置errno号
注意对错误的处理方式:
如果错误是file exist时,注意加判断,如:if(errno == EEXIST)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    int fd;
    //创建管道
    if (mkfifo("./fifo", 0666) < 0)
    {
        if (errno == EEXIST)
            printf("file exists\n");
        else
        {
            perror("mkfifo err");
            return -1;
        }
    }
    printf("mkfifo ok\n");
    //打开管道
    fd = open("./fifo", O_RDWR);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    char buf[32] = "hello";
    char data[32] = "";
    write(fd, buf, strlen(buf));

    read(fd, data, 32);
    printf("%s\n", data);

    return 0;
}

2.3 Précautions

a. Mode écriture seule, blocage d'écriture jusqu'à ce qu'un autre processus ouvre la lecture

B. Mode lecture seule, blocage de la lecture jusqu'à ce qu'un autre processus ouvre l'écriture

C. Lisible et inscriptible, s'il n'y a pas de données dans le pipeline, lire le blocage

Exercice : Implémenter la communication entre deux processus non liés.

read.c : lit les données du terminal

write.c : sortie des données vers le terminal

Se termine lorsque quit est tapé.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    int fd;
    char buf[32] = "";
    //创建管道
    if (mkfifo("./fifo", 0666) < 0)
    {
        if (errno == EEXIST)
            printf("file exists\n");
        else
        {
            perror("mkfifo err");
            return -1;
        }
    }
    printf("mkfifo ok\n");
    //打开管道
    fd = open("./fifo", O_WRONLY);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    while(1)
    {
        scanf("%s", buf);
        write(fd, buf, strlen(buf)+1);
        if(!strcmp(buf, "quit"))
            break;
    }

    return 0;
}


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    int fd;
    char buf[32] = "";
    //创建管道
    if (mkfifo("./fifo", 0666) < 0)
    {
        if (errno == EEXIST)
            printf("file exists\n");
        else
        {
            perror("mkfifo err");
            return -1;
        }
    }
    printf("mkfifo ok\n");
    //打开管道
    fd = open("./fifo", O_RDONLY);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    while(1)
    {
        read(fd, buf, 32);
        if(!strcmp(buf, "quit"))
            break;
        printf("buf:%s\n", buf);
    }

    return 0;
}

2.4 Différences entre les canaux nommés et les canaux sans nom

tuyau sans nom

pipeline célèbre

caractéristiques

Ne peut être utilisé qu'entre les processus d'affinité

communication semi-duplex

Il y a des fins de lecture et d'écriture fixes, fd[0] : lecture, fd[1] : fin d'écriture

Fonctionne via le fichier IO

Étapes : création d'un pipeline, opérations de lecture et d'écriture

utilisation inter-processus arbitraire non liée

Il y a des fichiers de pipeline dans le chemin et les données réelles existent dans l'espace du noyau

Fonctionne via le fichier IO

Étapes : création d'un canal, ouverture d'un canal, opérations de lecture et d'écriture

fonction

tuyau

mkfifo

Caractéristiques de lecture et d'écriture

Lorsqu'il n'y a pas de données dans le tube, lisez le blocage

Écrire des blocs lorsque le tuyau est plein

3. Signaler

3.1 Conception

1) Le signal est une simulation du mécanisme d'interruption au niveau logiciel, et c'est une  méthode de communication asynchrone

2) Le signal peut interagir directement entre le processus de l'espace utilisateur et le processus du noyau, et le processus du noyau peut également l'utiliser pour informer le processus de l'espace utilisateur des événements système qui se sont produits.

3) Si le processus n'est pas actuellement dans l'état d'exécution, le signal est enregistré par le noyau jusqu'à ce que le processus reprenne son exécution, puis lui est délivré ; si un signal est configuré pour être bloqué par le processus, la livraison du signal est retardée jusqu'à ce qu'il soit transmis au processus lorsque le bloc est annulé.

 3.2. Réponses aux signaux

1) Ignorer le signal : n'effectuez aucun traitement sur le signal, mais il y a deux signaux qui ne peuvent pas être ignorés : SIGKILL et SIGSTOP

2) Signal de capture : définissez la fonction de traitement du signal, lorsque le signal se produit, exécutez la fonction de traitement correspondante.

3) Exécuter l'opération par défaut : Linux spécifie l'opération par défaut pour chaque signal 

3. 3. Types de signaux 

SIGKILL : termine le processus, ne peut être ignoré et ne peut pas être attrapé

SIGSTOP : processus de fin, ne peut être ignoré et ne peut pas être intercepté

SIGCHLD : le signal envoyé au processus parent lorsque l'état du processus enfant change, ne mettra pas fin au processus

SIGINT : termine le processus, correspondant au raccourci ctrl+c

SIGTSTP : Signal de pause, correspondant au raccourci ctrl+z

SIGQUIT : Signal de sortie, correspondant au raccourci ctrl+\

SIGALRM : Signal d'alarme, la fonction d'alarme définit le minutage, lorsque l'heure définie est atteinte, le noyau enverra ce signal au processus pour mettre fin au processus.

SIGTERM : termine le processus du terminal, lorsque kill est utilisé sans numéro, la valeur par défaut est ce signal

3.4 Interface de fonction

int kill(pid_t pid, int sig);
功能:信号发送
参数:pid:指定进程
   sig:要发送的信号
返回值:成功 0     
       失败 -1

int raise(int sig);
功能:进程向自己发送信号
参数:sig:信号
返回值:成功 0   
       失败 -1

int pause(void);
功能:用于将调用进程挂起,直到收到信号为止。
#include <signal.h>
 typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);
功能:信号处理函数
参数:signum:要处理的信号
      handler:信号处理方式
            SIG_IGN:忽略信号
            SIG_DFL:执行默认操作
           handler:捕捉信号  void handler(int sig){} //函数名可以自定义
返回值:成功:设置之前的信号处理方式
      失败:-1
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    // kill(getpid(), SIGKILL);

    raise(SIGKILL); //给自己发信号

    // while (1)
    //     ;
    while (1)
        pause(); //将当前进程挂起(阻塞),直到收到信号结束

    return 0;
}

typedef void (*sighandler_t)(int); //typedef entier non signé INT ;

typedef void (*)(int) sighandler_t ;

sighandler_t signal(int signum, void (*handler)(int) );

gestionnaire de vide (int sig)

{

si(sig == SIGINT)

printf("xxx\n");

sinon si(sig == SIGQUI T)

}

signal(SIGINT, gestionnaire);

signal(SIGQUIT, gestionnaire)

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int sig)
{
    printf("ctrl+c\n");
}
int main(int argc, char const *argv[])
{ 
    // signal(SIGINT, SIG_IGN);//忽略信号
    // signal(SIGINT, SIG_DFL); //执行默认操作
    signal(SIGINT, handler); //捕捉信号

    while(1)
        pause();

    return 0;
}

Opération:

  1. Deux processus implémentent la fonction cp

./r fichier_src

./w nouveaufichier

  1. Utiliser la connaissance des signaux pour mettre en œuvre le problème du conducteur et du conducteur.

1) Le conducteur capture le signal SIGINT (représentant la conduite), envoie un signal SIGUSR1 au conducteur et le conducteur imprime (allons-y)

  2) Le conducteur capte le signal SIGQUIT (représentant l'arrêt), envoie un signal SIGUSR2 au conducteur et le conducteur imprime (arrêtez le bus)

3) Le conducteur capte le signal SIGTSTP (au nom de l'arrivée au terminal), envoie un signal SIGUSR1 au conducteur et le conducteur imprime (veuillez descendre du bus)

4) Le conducteur attend que le conducteur descende, puis le conducteur redescend.

pilote : processus parent

Signal de capture :

Ignorer le signal :

chef d'orchestre : processus enfant

Signal de capture :

Ignorer le signal :

./a.out

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

pid_t pid;
void driver(int sig)
{
    if (sig == SIGUSR1)
        printf("let's gogogo\n");
    else if (sig == SIGUSR2)
        printf("stop the bus\n");
    else if (sig == SIGTSTP)
    {
        kill(pid, SIGUSR1);
        wait(NULL);
        exit(0);
    }
}
void saler(int sig)
{
    if (sig == SIGINT)
        kill(getppid(), SIGUSR1);
    else if (sig == SIGQUIT)
        kill(getppid(), SIGUSR2);
    else if (sig == SIGUSR1)
    {
        printf("please get off the bus\n");
        exit(0);
    }
}
int main(int argc, char const *argv[])
{
    if ((pid = fork()) < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        signal(SIGINT, saler);
        signal(SIGQUIT, saler);
        signal(SIGUSR1, saler);
        signal(SIGTSTP, SIG_IGN);
    }
    else
    {
        signal(SIGUSR1, driver);
        signal(SIGUSR2, driver);
        signal(SIGTSTP, driver);
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);
    }
    while (1)
        pause();

    return 0;
}

4. Mémoire partagée

4.1 Caractéristiques

   1) La mémoire partagée est la méthode de communication inter-processus la plus efficace . Les processus peuvent lire et écrire directement dans la mémoire sans copier aucune donnée.

   2) Afin d'échanger des informations entre plusieurs processus, le noyau réserve spécialement une zone mémoire, qui peut être mappée sur son propre espace d'adressage privé par le processus qui doit y accéder

   3) Le processus peut directement lire et écrire cette zone de mémoire sans copier les données, améliorant ainsi considérablement l'efficacité.

   4) Étant donné que plusieurs processus partagent un morceau de mémoire, ils doivent également s'appuyer sur une sorte de mécanisme de synchronisation, comme les mutex et les sémaphores, etc.

4.2 étapes

0) Créer une valeur clé

1) Créer ou ouvrir une mémoire partagée

2) Cartographie

3) Démapper

4) Supprimer la mémoire partagée

4.3 Interface de fonction

key_t ftok(const char *pathname, int proj_id);
功能:创建key值
参数:pathname:文件名
     proj_id:取整型数的低8位数值
返回值:成功:key值
       失败:-1
int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:
    key  键值
    size   共享内存的大小
    shmflg   IPC_CREAT|IPC_EXCL|0777
返回值:成功   shmid
      出错    -1
void  *shmat(int  shmid,const  void  *shmaddr,int  shmflg);
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:
    shmid   共享内存的id号
    shmaddr   一般为NULL,表示由系统自动完成映射
              如果不为NULL,那么有用户指定
    shmflg:SHM_RDONLY就是对该共享内存只进行读操作
                0     可读可写
返回值:成功:完成映射后的地址,
      出错:-1的地址
用法:if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)
int shmdt(const void *shmaddr);
功能:取消映射
参数:要取消的地址
返回值:成功0  
      失败的-1
int  shmctl(int  shmid,int  cmd,struct  shmid_ds   *buf);
功能:(删除共享内存),对共享内存进行各种操作
参数:
    shmid   共享内存的id号
    cmd     IPC_STAT 获得shmid属性信息,存放在第三参数
            IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
            IPC_RMID:删除共享内存,此时第三个参数为NULL即可
返回:成功0 
     失败-1
用法:shmctl(shmid,IPC_RMID,NULL);

Afficher les commandes de mémoire partagée :

ipcs -m 

Commande pour supprimer la mémoire partagée :

ipcrm -m shmid

Exemple de code :

int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建或打印共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0666);
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);
    //映射
    char *p = NULL;
    p = shmat(shmid, NULL, 0); //NULL:系统自动进行映射 0:可读可写
    if(p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }
    strcpy(p, "hello");
    printf("%s\n", p);
    //取消映射
    shmdt(p);
    //删除共享内存
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

Exercice : Un processus entre le terminal et un autre processus produit des données, en utilisant la communication en mémoire partagée.

Prérequis : le programme se termine lorsque l'on entre en quit

Synchronisation : bit de drapeau

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>

struct msg
{
    int flg;
    char buf[32];
};
int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建或打印共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0666);
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);
    //映射
    struct msg *p = NULL;
    p = shmat(shmid, NULL, 0); //NULL:系统自动进行映射 0:可读可写
    if(p == (struct msg *)-1)
    {
        perror("shmat err");
        return -1;
    }
    p->flg = 0;
    while(1)
    {
        scanf("%s", p->buf);
        p->flg = 1;
        if(strcmp(p->buf, "quit") == 0)
            break;
    }

    return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建或打印共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0666);
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);
    //映射
    char *p = NULL;
    p = shmat(shmid, NULL, 0); //NULL:系统自动进行映射 0:可读可写
    if(p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }
    strcpy(p, "hello");
    printf("%s\n", p);
    //取消映射
    shmdt(p);
    //删除共享内存
    shmctl(shmid, IPC_RMID, NULL);



    return 0;
}

5. Collection de feux de signalisation

 5.1. Fonctionnalités :

Un sémaphore est aussi appelé sémaphore. C'est un mécanisme de synchronisation entre différents processus ou différents threads au sein d'un processus donné ; un sémaphore System V est une collection d'un ou plusieurs sémaphores. Chacun d'eux est un sémaphore de comptage séparé. Et les sémaphores Posix font référence à un seul sémaphore de comptage.

Synchronisation de la mémoire partagée via des ensembles de sémaphores

5.2 Étapes :

0) Créer une valeur clé

1) Créer ou ouvrir un ensemble de sémaphores semget 

2) Initialiser le jeu de sémaphores semctl

3) opération pv

4) Supprimer le jeu de sémaphores semctl

5.3 Interface de fonction

int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值
    nsems:信号灯集中包含的信号灯数目
    semflg:信号灯集的访问权限,通常为IPC_CREAT |0666
返回值:成功:信号灯集ID
       失败:-1
int semop ( int semid, struct sembuf  *opsptr,  size_t  nops);
功能:对信号灯集合中的信号量进行PV操作
参数:semid:信号灯集ID
     opsptr:操作方式
     nops:  要操作的信号灯的个数 1个
返回值:成功 :0
      失败:-1
struct sembuf {
   short  sem_num; // 要操作的信号灯的编号
   short  sem_op;  //    0 :  等待,直到信号灯的值变成0
                   //   1  :  释放资源,V操作
                   //   -1 :  分配资源,P操作                    
    short  sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};
用法:
申请资源 P操作:
    mysembuf.sem_num = 0;
    mysembuf.sem_op = -1;
    mysembuf.sem_flg = 0;
    semop(semid, &mysembuf, 1);
释放资源 V操作:
    mysembuf.sem_num = 0;
    mysembuf.sem_op = 1;
    mysembuf.sem_flg = 0;
    semop(semid, &mysembuf, 1);
int semctl ( int semid, int semnum,  int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
    semnum: 要操作的集合中的信号灯编号
     cmd:
        GETVAL:获取信号灯的值,返回值是获得值
        SETVAL:设置信号灯的值,需要用到第四个参数:共用体
        IPC_RMID:从系统中删除信号灯集合
返回值:成功 0
      失败 -1
用法:初始化:
union semun{
    int val; //信号灯的初值
}mysemun;
mysemun.val = 10;
semctl(semid, 0, SETVAL, mysemun);
获取信号灯值:函数semctl(semid, 0, GETVAL)的返回值
删除信号灯集:semctl(semid, 0, IPC_RMID);

5.4 Commandes

ipcs -s : afficher le jeu de lampes de signalisation

ipcrm -s semid : supprimer l'ensemble de sémaphores

exemple:

union semun {
    int val; //信号灯的初值
};
int main(int argc, char const *argv[])
{
    key_t key;
    int semid;
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建或打开信号灯集
    semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
    if (semid < 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 2, 0666);
        else
        {
            perror("semget err");
            return -1;
        }
    }
    else
    {
        //初始化
        union semun sem;
        sem.val = 10;
        semctl(semid, 0, SETVAL, sem); //对编号为0的信号灯初值设置为10
        sem.val = 0;
        semctl(semid, 1, SETVAL, sem); //对编号为1的信号灯初值设置为0
    }
    printf("semid:%d\n", semid);
    printf("%d\n",semctl(semid, 0, GETVAL));//获取编号为0的信号灯的值
    printf("%d\n",semctl(semid, 1, GETVAL));//获取编号为1的信号灯的值

    //pv操作
    //p操作:申请资源
    struct sembuf buf;
    buf.sem_num = 0; //信号灯的编号
    buf.sem_op = -1; //p操作
    buf.sem_flg = 0; //阻塞
    semop(semid, &buf, 1);
    //v操作:释放资源
    buf.sem_num = 1;
    buf.sem_op = 1; //v操作
    buf.sem_flg = 0;
    semop(semid, &buf, 1);
    printf("%d\n",semctl(semid, 0, GETVAL));//获取编号为0的信号灯的值
    printf("%d\n",semctl(semid, 1, GETVAL));//获取编号为1的信号灯的值
    //删除信号灯集
    semctl(semid, 0, IPC_RMID); //指定任意一个编号即可删除信号灯集
    return 0;

//input.c代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/sem.h>
#include <string.h>

union semun {
    int val; //信号灯的初值
};
//初始化
void seminit(int semid, int snum, int val)
{
    union semun sem;
    sem.val = val;
    semctl(semid, snum, SETVAL, sem);
}
//pv操作
void sem_op(int semid, int num, int op)
{
    struct sembuf buf;
    buf.sem_num = num;
    buf.sem_op = op;
    buf.sem_flg = 0;
    semop(semid, &buf, 1);
}
int main(int argc, char const *argv[])
{
    key_t key;
    int shmid, semid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建或打印共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0666);
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);
    //创建或打开信号灯集
    semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
    if (semid < 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 1, 0666);
        else
        {
            perror("semget err");
            return -1;
        }
    }
    else
    {
        //初始化
        seminit(semid, 0, 0);
    }
    //映射
    char *p = NULL;
    p = shmat(shmid, NULL, 0); //NULL:系统自动进行映射 0:可读可写
    if(p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }
    while(1)
    {
        scanf("%s", p);
        //释放资源
        sem_op(semid, 0, 1);
        if(strcmp(p, "quit") == 0)
            break;
    }

    return 0;
}



//output.c代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/sem.h>
#include <string.h>

union semun {
    int val; //信号灯的初值
};
//初始化
void seminit(int semid, int snum, int val)
{
    union semun sem;
    sem.val = val;
    semctl(semid, snum, SETVAL, sem);
}
//pv操作
void sem_op(int semid, int num, int op)
{
    struct sembuf buf;
    buf.sem_num = num;
    buf.sem_op = op;
    buf.sem_flg = 0;
    semop(semid, &buf, 1);
}
int main(int argc, char const *argv[])
{
    key_t key;
    int shmid, semid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建或打印共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0666);
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);
    //创建或打开信号灯集
    semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
    if (semid < 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 1, 0666);
        else
        {
            perror("semget err");
            return -1;
        }
    }
    else
    {
        //初始化
        seminit(semid, 0, 0);
    }
    //映射
    char *p = NULL;
    p = shmat(shmid, NULL, 0); //NULL:系统自动进行映射 0:可读可写
    if(p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }
    while(1)
    {
        //申请资源
        sem_op(semid, 0, -1);
        if(strcmp(p, "quit") == 0)
            break;
        printf("data:%s\n", p);
    }

    //取消映射
    shmdt(p);
    //删除共享内存
    shmctl(shmid, IPC_RMID, NULL);
    //删除信号灯集
    semctl(semid, 0, IPC_RMID);

    return 0;
}

6. File d'attente des messages

 6.1 Fonctionnalités :

La file d'attente de messages est une sorte d'objet IPC

La file d'attente de messages est identifiée de manière unique par l'ID de file d'attente de messages

Une file d'attente de messages est une liste de messages. Les utilisateurs peuvent ajouter des messages, lire des messages, etc. dans la file d'attente des messages.

Les files d'attente de messages peuvent envoyer/recevoir des messages par type

6.2 Étapes :

1) Créer la valeur clé ftok

2) Créer ou ouvrir une file d'attente de messages msgget  

3) Ajouter un message/lire le message msgsnd/msgrcv

4) Supprimer la file d'attente de messages msgctl

6.3 Commandes de fonctionnement

ipcs -q : affiche la file d'attente des messages

ipcrm -q msgid : supprimer la file d'attente des messages

6.4. Interface fonctionnelle

int msgget(key_t key, int flag);
功能:创建或打开一个消息队列
参数:  key值
       flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
返回值:成功:msgid
       失败:-1
int msgsnd(int msqid, const void *msgp, size_t size, int flag); 
功能:添加消息
参数:msqid:消息队列的ID
      msgp:指向消息的指针。常用消息结构msgbuf如下:
          struct msgbuf{
            long mtype;        //消息类型
            char mtext[N]};   //消息正文
   size:发送的消息正文的字节数
   flag:IPC_NOWAIT消息没有发送完成函数也会立即返回    
         0:直到发送完成函数才返回
返回值:成功:0
      失败:-1
使用:msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), 0)
注意:消息结构除了第一个成员必须为long类型外,其他成员可以根据应用的需求自行定义。
int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtype,  int  flag);
功能:读取消息
参数:msgid:消息队列的ID
     msgp:存放读取消息的空间
     size:接受的消息正文的字节数
    msgtype:0:接收消息队列中第一个消息。
            大于0:接收消息队列中第一个类型为msgtyp的消息.
            小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
     flag:0:若无消息函数会一直阻塞
        IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
返回值:成功:接收到的消息的长度
      失败:-1
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列的操作,删除消息队列
参数:msqid:消息队列的队列ID
     cmd:
        IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
        IPC_SET:设置消息队列的属性。这个值取自buf参数。
        IPC_RMID:从系统中删除消息队列。
     buf:消息队列缓冲区
返回值:成功:0
      失败:-1
用法:msgctl(msgid, IPC_RMID, NULL)

exemple:

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <errno.h>
#include <string.h>
#include <sys/msg.h>

struct msgbuf{
    long type;
    int num;
    char buf[32];
};
int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建消息队列
    msgid = msgget(key, IPC_CREAT|IPC_EXCL|0666);
    if(msgid < 0)
    {
        if(errno == EEXIST)
            msgid = msgget(key, 0666);
        else
        {
            perror("msgget err");
            return -1;
        }     
    }
    //添加消息
    int size = sizeof(struct msgbuf)-sizeof(long);
    struct msgbuf msg = {1, 100, "hello"};
    struct msgbuf msg1 = {2, 200, "world"};
    msgsnd(msgid, &msg, size, 0);
    msgsnd(msgid, &msg1, size, 0);
    //读取消息
    struct msgbuf m;
    msgrcv(msgid, &m, size, 2, 0);
    printf("%d %s\n", m.num, m.buf);
    //删除消息队列
    msgctl(msgid, IPC_RMID, NULL);
    return 0;
}

Exercice : Deux processus communiquent via la file d'attente de messages. Un processus entre la commande envoyée par le terminal, et l'autre processus reçoit la commande et imprime l'instruction d'opération correspondante.

Si la LED d'entrée est allumée, un autre processus émet "lumière allumée"

Si la LED d'entrée est éteinte, un autre processus produit "s'éteint"

#include <stdio.h>                     //send.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <errno.h>
#include <string.h>
#include <sys/msg.h>

struct msgbuf
{
    long type;
    char buf[32];
};
int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建消息队列
    msgid = msgget(key, IPC_CREAT|IPC_EXCL|0666);
    if(msgid < 0)
    {
        if(errno == EEXIST)
            msgid = msgget(key, 0666);
        else
        {
            perror("msgget err");
            return -1;
        }     
    }
    struct msgbuf msg;
    msg.type = 1;
    int s = sizeof(struct msgbuf)-sizeof(long);
    while(1)
    {
        scanf("%s", msg.buf);
        msgsnd(msgid, &msg, s, 0);
    }
    return 0;
}





#include <stdio.h>                               //recieve.c
#include <stdio.h>
#include <sys/types.h>                          
#include <sys/ipc.h>
#include <errno.h>
#include <string.h>
#include <sys/msg.h>

struct msgbuf
{
    long type;
    char buf[32];
};
int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;
    //创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("%#x\n", key);
    //创建消息队列
    msgid = msgget(key, IPC_CREAT|IPC_EXCL|0666);
    if(msgid < 0)
    {
        if(errno == EEXIST)
            msgid = msgget(key, 0666);
        else
        {
            perror("msgget err");
            return -1;
        }     
    }
    struct msgbuf msg;
    int s = sizeof(struct msgbuf)-sizeof(long);
    while(1)
    {
        msgrcv(msgid, &msg, s, 1, 0);
        if(strcmp(msg.buf, "LEDON") == 0)
            printf("开灯\n");
        else if(strcmp(msg.buf, "LEDOFF") == 0)
            printf("关灯\n");
    }
    
    return 0;
}

Je suppose que tu aimes

Origine blog.csdn.net/qq_52049228/article/details/130565655
conseillé
Classement