ホームページ >システムチュートリアル >Linux >Linux プロセス間通信の効率的な方法: 共有メモリの使用
次に、共有メモリを使用したプロセス間通信の別の方法について説明します。
名前が示すように、共有メモリを使用すると、無関係な 2 つのプロセスが同じ論理メモリにアクセスできます。共有メモリは、実行中の 2 つのプロセス間でデータを共有および転送するための非常に効率的な方法です。異なるプロセス間で共有されるメモリは、通常、物理メモリの同じセグメントとして配置されます。プロセスは、共有メモリの同じセグメントを独自のアドレス空間に接続でき、すべてのプロセスは、C 言語関数 malloc を使用して割り当てられたメモリであるかのように、共有メモリ内のアドレスにアクセスできます。また、プロセスが共有メモリにデータを書き込むと、その変更は共有メモリの同じセグメントにアクセスできる他のプロセスに直ちに影響します。
特別な注意: 共有メモリには同期メカニズムがありません。つまり、最初のプロセスが共有メモリへの書き込みを完了する前に、2 番目のプロセスが共有メモリの読み取りを開始するのを防ぐ自動メカニズムはありません。したがって、通常は、前述のセマフォなどの他のメカニズムを使用して、共有メモリへのアクセスを同期する必要があります。セマフォの詳細については、私の他の記事「Linux プロセス間通信 - セマフォの使用」を参照してください。
1.shmget 関数
この関数は共有メモリを作成するために使用されます。そのプロトタイプは次のとおりです:
が返されます。
無関係なプロセスは、プログラムが使用する可能性のあるリソースを表すこの関数の戻り値を通じて同じ共有メモリにアクセスできます。プログラムによるすべての共有メモリへのアクセスは間接的です。プログラムは最初に shmget 関数を呼び出し、A キーを提供します, その後、システムは対応する共有メモリ識別子 (shmget 関数の戻り値) を生成します。shmget 関数のみがセマフォ キーを直接使用し、他のすべてのセマフォ関数は semget 関数によって返されたセマフォ識別子を使用します。2 番目のパラメータの size は、共有されるメモリ容量をバイト単位で指定します。
3 番目のパラメータ shmflg は許可フラグです。その機能は open 関数の mode パラメータと同じです。キーで指定された共有メモリが存在しない場合に作成したい場合は、次の操作を行うか操作できます。 IPC_CREATを使用して。共有メモリの許可フラグは、ファイルの読み取りおよび書き込み許可と同じです。たとえば、0644 は、プロセスによって作成された共有メモリは、所有するプロセスによる共有メモリへの読み取りおよび書き込みが許可されていることを意味します。同時に、他のユーザーが作成したプロセスは共有メモリの読み取りのみ可能です。
2. shmat 関数
共有メモリが初めて作成されるときは、どのプロセスからもアクセスできません。shmat 関数の機能は、共有メモリへのアクセスを開始し、共有メモリを現在のプロセスのアドレス空間に接続することです。そのプロトタイプは次のとおりです:
リーリー2 番目のパラメータ shm_addr は、共有メモリが現在のプロセスに接続されているアドレスの場所を指定します。通常は空であり、システムが共有メモリのアドレスを選択できることを示します。
3 番目のパラメータ shm_flg はフラグ ビットのセットで、通常は 0 です。
呼び出しが成功すると、共有メモリの最初のバイトへのポインタが返され、呼び出しが失敗した場合は、-1 が返されます。
3. shmdt 関数この関数は、現在のプロセスから共有メモリを切り離すために使用されます。共有メモリを切り離しても、共有メモリは削除されず、共有メモリが現在のプロセスで使用できなくなるだけであることに注意してください。そのプロトタイプは次のとおりです:
リーリー
パラメータ shmaddr は、shmat 関数によって返されるアドレス ポインタです。呼び出しが成功した場合は 0 を返し、失敗した場合は -1 を返します。4. shmctl 関数
セマフォの semctl 関数と同様、共有メモリの制御に使用されます。プロトタイプは次のとおりです。
**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、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。
以上がLinux プロセス間通信の効率的な方法: 共有メモリの使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。