Maison > Article > Tutoriel système > Une méthode efficace pour la communication inter-processus Linux : utiliser la mémoire partagée
Ce qui suit expliquera une autre façon de communication inter-processus, en utilisant la mémoire partagée.
Comme son nom l'indique, la mémoire partagée permet à deux processus non liés d'accéder à la même mémoire logique. La mémoire partagée est un moyen très efficace de partager et de transférer des données entre deux processus en cours d'exécution. La mémoire partagée entre différents processus est généralement organisée comme le même segment de mémoire physique. Les processus peuvent connecter le même segment de mémoire partagée dans leur propre espace d'adressage, et tous les processus peuvent accéder aux adresses de la mémoire partagée comme s'il s'agissait de mémoire allouée à l'aide de la fonction malloc du langage C. Et si un processus écrit des données dans la mémoire partagée, les modifications affecteront immédiatement tout autre processus pouvant accéder au même segment de mémoire partagée.
Rappel spécial : la mémoire partagée ne fournit pas de mécanisme de synchronisation. C'est-à-dire qu'avant que le premier processus ait fini d'écrire dans la mémoire partagée, il n'y a pas de mécanisme automatique pour empêcher le deuxième processus de commencer à la lire. Par conséquent, nous devons généralement utiliser d’autres mécanismes pour synchroniser l’accès à la mémoire partagée, comme le sémaphore mentionné précédemment. Pour plus d'informations sur les sémaphores, vous pouvez consulter mon autre article : Communication inter-processus Linux - utilisation des sémaphores
Comme les sémaphores, Linux fournit également un ensemble d'interfaces fonctionnelles pour utiliser la mémoire partagée, et les interfaces qui utilisent la coexistence partagée sont très similaires à celles des sémaphores et sont plus simples que celles qui utilisent des sémaphores. Ils sont déclarés dans le fichier d'entête sys/shm.h.
1. fonction shmget
Cette fonction permet de créer de la mémoire partagée, son prototype est :
Le premier paramètre est le même que la fonction semget du sémaphore. Le programme doit fournir une clé de paramètre (entier non nul), qui nomme effectivement le segment de mémoire partagée. Lorsque la fonction shmget réussit, elle renvoie un segment partagé. identifiant de mémoire lié à la clé (entier non négatif), utilisé dans les fonctions de mémoire partagée ultérieures. L'appel échoue et renvoie -1.
Les processus non liés peuvent accéder à la même mémoire partagée via la valeur de retour de cette fonction, qui représente une ressource que le programme peut utiliser. L'accès du programme à toute la mémoire partagée est indirect. Le programme appelle d'abord la fonction shmget et fournit une clé, et Ensuite, le système génère un identifiant de mémoire partagée correspondant (la valeur de retour de la fonction shmget). Seule la fonction shmget utilise directement la clé du sémaphore, et toutes les autres fonctions du sémaphore utilisent l'identifiant du sémaphore renvoyé par la fonction semget.
Le deuxième paramètre, size spécifie la capacité mémoire à partager en octets
Le troisième paramètre, shmflg est l'indicateur d'autorisation. Sa fonction est la même que le paramètre mode de la fonction open. Si vous souhaitez créer la mémoire partagée identifiée par la clé alors qu'elle n'existe pas, vous pouvez faire ou opérer avec IPC_CREAT. . L'indicateur d'autorisation de la mémoire partagée est le même que l'autorisation de lecture et d'écriture du fichier, par exemple 0644, ce qui signifie que la mémoire partagée créée par un processus est autorisée à être lue et écrite dans la mémoire partagée par le processus détenu. par le créateur de la mémoire. En même temps, d'autres utilisateurs créent des processus qui ne peuvent lire que la mémoire partagée.
2. fonction shmat
Lorsque la mémoire partagée est créée pour la première fois, aucun processus ne peut y accéder. La fonction shmat est d'initier l'accès à la mémoire partagée et de connecter la mémoire partagée à l'espace d'adressage du processus en cours. Son prototype est le suivant :
**void** *shmat(**int** shm_id, **const** **void** *shm_addr, **int** shmflg);
Le premier paramètre, shm_id est l'identification de la mémoire partagée renvoyée par la fonction shmget.
Le deuxième paramètre, shm_addr, spécifie l'emplacement de l'adresse où la mémoire partagée est connectée au processus en cours. Il est généralement vide, indiquant que le système est autorisé à sélectionner l'adresse de la mémoire partagée.
Le troisième paramètre, shm_flg est un ensemble de bits d'indicateur, généralement 0.
Si l'appel réussit, il renverra un pointeur vers le premier octet de la mémoire partagée. Si l'appel échoue, il renverra -1.
.3. fonction shmdt
Cette fonction est utilisée pour détacher la mémoire partagée du processus en cours. Notez que le détachement de la mémoire partagée ne la supprime pas, cela rend simplement la mémoire partagée indisponible pour le processus en cours. Son prototype est le suivant :
1. **int** shmdt(**const** **void** *shmaddr);
Le paramètre shmaddr est le pointeur d'adresse renvoyé par la fonction shmat. Il renvoie 0 lorsque l'appel réussit et -1 lorsqu'il échoue.
4. fonction shmctl
Identique à la fonction semctl du sémaphore, elle permet de contrôler la mémoire partagée. Son prototype est le suivant :
**int** shmctl(**int** shm_id, **int** command, **struct** shmid_ds *buf);
第一个参数,shm_id是shmget函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:
1. struct** shmid_ds 2. { 3. uid_t shm_perm.uid; 4. uid_t shm_perm.gid; 5. mode_t shm_perm.mode; 6. };
说了这么多,又到了实战的时候了。下面就以两个不相关的进程来说明进程间如何通过共享内存来进行通信。其中一个文件shmread.c创建共享内存,并读取其中的信息,另一个文件shmwrite.c向共享内存中写入数据。为了方便操作和数据结构的统一,为这两个文件定义了相同的数据结构,定义在文件shmdata.c中。结构shared_use_st中的written作为一个可读或可写的标志,非0:表示可读,0表示可写,text则是内存中的文件。
shmdata.h的源代码如下:
1. \#ifndef _SHMDATA_H_HEADER 2. \#define _SHMDATA_H_HEADER 3. 4. \#define TEXT_SZ 2048 5. 6. **struct** shared_use_st 7. { 8. **int** written;//作为一个标志,非0:表示可读,0表示可写 9. **char** text[TEXT_SZ];//记录写入和读取的文本 10. }; 11. 12. \#endif
源文件shmread.c的源代码如下:
1. \#include 2. \#include 3. \#include 4. \#include 5. \#include "shmdata.h" 6. 7. **int** main() 8. { 9. **int** running = 1;//程序是否继续运行的标志 10. **void** *shm = NULL;//分配的共享内存的原始首地址 11. **struct** shared_use_st *shared;//指向shm 12. **int** shmid;//共享内存标识符 13. //创建共享内存 14. shmid = shmget((key_t)1234, **sizeof**(**struct** shared_use_st), 0666|IPC_CREAT); 15. **if**(shmid == -1) 16. { 17. fprintf(stderr, "shmget failed\n"); 18. exit(EXIT_FAILURE); 19. } 20. //将共享内存连接到当前进程的地址空间 21. shm = shmat(shmid, 0, 0); 22. **if**(shm == (**void***)-1) 23. { 24. fprintf(stderr, "shmat failed\n"); 25. exit(EXIT_FAILURE); 26. } 27. printf("\nMemory attached at %X\n", (**int**)shm); 28. //设置共享内存 29. shared = (**struct** shared_use_st*)shm; 30. shared->written = 0; 31. **while**(running)//读取共享内存中的数据 32. { 33. //没有进程向共享内存定数据有数据可读取 34. **if**(shared->written != 0) 35. { 36. printf("You wrote: %s", shared->text); 37. sleep(rand() % 3); 38. //读取完数据,设置written使共享内存段可写 39. shared->written = 0; 40. //输入了end,退出循环(程序) 41. **if**(strncmp(shared->text, "end", 3) == 0) 42. running = 0; 43. } 44. **else**//有其他进程在写数据,不能读取数据 45. sleep(1); 46. } 47. //把共享内存从当前进程中分离 48. **if**(shmdt(shm) == -1) 49. { 50. fprintf(stderr, "shmdt failed\n"); 51. exit(EXIT_FAILURE); 52. } 53. //删除共享内存 54. **if**(shmctl(shmid, IPC_RMID, 0) == -1) 55. { 56. fprintf(stderr, "shmctl(IPC_RMID) failed\n"); 57. exit(EXIT_FAILURE); 58. } 59. exit(EXIT_SUCCESS); 60. }
源文件shmwrite.c的源代码如下:
1. \#include 2. \#include 3. \#include 4. \#include 5. \#include 6. \#include "shmdata.h" 7. 8. **int** main() 9. { 10. **int** running = 1; 11. **void** *shm = NULL; 12. **struct** shared_use_st *shared = NULL; 13. **char** buffer[BUFSIZ + 1];//用于保存输入的文本 14. **int** shmid; 15. //创建共享内存 16. shmid = shmget((key_t)1234, **sizeof**(**struct** shared_use_st), 0666|IPC_CREAT); 17. **if**(shmid == -1) 18. { 19. fprintf(stderr, "shmget failed\n"); 20. exit(EXIT_FAILURE); 21. } 22. //将共享内存连接到当前进程的地址空间 23. shm = shmat(shmid, (**void***)0, 0); 24. **if**(shm == (**void***)-1) 25. { 26. fprintf(stderr, "shmat failed\n"); 27. exit(EXIT_FAILURE); 28. } 29. printf("Memory attached at %X\n", (**int**)shm); 30. //设置共享内存 31. shared = (**struct** shared_use_st*)shm; 32. **while**(running)//向共享内存中写数据 33. { 34. //数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本 35. **while**(shared->written == 1) 36. { 37. sleep(1); 38. printf("Waiting...\n"); 39. } 40. //向共享内存中写入数据 41. printf("Enter some text: "); 42. fgets(buffer, BUFSIZ, stdin); 43. strncpy(shared->text, buffer, TEXT_SZ); 44. //写完数据,设置written使共享内存段可读 45. shared->written = 1; 46. //输入了end,退出循环(程序) 47. **if**(strncmp(buffer, "end", 3) == 0) 48. running = 0; 49. } 50. //把共享内存从当前进程中分离 51. **if**(shmdt(shm) == -1) 52. { 53. fprintf(stderr, "shmdt failed\n"); 54. exit(EXIT_FAILURE); 55. } 56. sleep(2); 57. exit(EXIT_SUCCESS); 58. }
再来看看运行的结果:
分析:
1、程序shmread创建共享内存,然后将它连接到自己的地址空间。在共享内存的开始处使用了一个结构struct_use_st。该结构中有个标志written,当共享内存中有其他进程向它写入数据时,共享内存中的written被设置为0,程序等待。当它不为0时,表示没有进程对共享内存写入数据,程序就从共享内存中读取数据并输出,然后重置设置共享内存中的written为0,即让其可被shmwrite进程写入数据。
2、程序shmwrite取得共享内存并连接到自己的地址空间中。检查共享内存中的written,是否为0,若不是,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。若共享内存的written为0,表示没有其他进程对共享内存进行读取,则提示用户输入文本,并再次设置共享内存中的written为1,表示写完成,其他进程可对共享内存进行读操作。
四、关于前面的例子的安全性讨论
这个程序是不安全的,当有多个程序同时向共享内存中读写数据时,问题就会出现。可能你会认为,可以改变一下written的使用方式,例如,只有当written为0时进程才可以向共享内存写入数据,而当一个进程只有在written不为0时才能对其进行读取,同时把written进行加1操作,读取完后进行减1操作。这就有点像文件锁中的读写锁的功能。咋看之下,它似乎能行得通。但是这都不是原子操作,所以这种做法是行不能的。试想当written为0时,如果有两个进程同时访问共享内存,它们就会发现written为0,于是两个进程都对其进行写操作,显然不行。当written为1时,有两个进程同时对共享内存进行读操作时也是如些,当这两个进程都读取完是,written就变成了-1.
要想让程序安全地执行,就要有一种进程同步的进制,保证在进入临界区的操作是原子操作。例如,可以使用前面所讲的信号量来进行进程的同步。因为信号量的操作都是原子性的。
五、使用共享内存的优缺点
1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!