ホームページ  >  記事  >  運用・保守  >  Linuxのプロセス間通信を実装する方法

Linuxのプロセス間通信を実装する方法

王林
王林転載
2023-05-21 16:28:521651ブラウズ

    共有メモリ

    • 共有メモリは、最も便利なプロセス間通信方法であり、IPC の最速形式であると言えます。異なるプロセス A と B がメモリを共有するということは、物理メモリの同じ部分がプロセス A と B のそれぞれのプロセス アドレス空間にマップされることを意味します。プロセス A は、プロセス B による共有メモリ内のデータの更新を同時に確認できます。 、 およびその逆。

    • 複数のプロセスが同じメモリ領域を共有するため、何らかの同期メカニズム、ミューテックス ロック、セマフォが必要です。

    利点: 効率が高く、プロセスはデータをコピーせずにメモリを直接読み書きできますが、パイプやメッセージ キューなどの通信方法ではカーネルとユーザー空間で 4 回のデータ レプリケーションが必要です。 。

    そして、マッピングを解除する場合にのみ、共有メモリの内容が記念として書き込まれます。

    共有メモリはカーネル オブジェクトを使用して、さまざまなプロセスが独自の仮想アドレス空間に空間マッピングを割り当てることができるようにします。同じ物理メモリ空間の場合、この物理メモリ空間には、マップされているすべてのプロセスがアクセスできます。 (重要なリソース)

    Linuxのプロセス間通信を実装する方法

    共有メモリにより、無関係な 2 つのプロセスが同じ論理メモリにアクセスできます

    共有メモリは、実行中の 2 つのプロセス間でデータを共有および転送するための非常に効率的な方法です。

    異なるプロセス間で共有されるメモリは、通常、物理メモリの同じセグメントとして配置されます。

    プロセスは、共有メモリの同じセグメントを独自のアドレス空間に接続でき、すべてのプロセスは、C 言語関数 malloc() を使用して割り当てられたメモリであるかのように、共有メモリ内のアドレスにアクセスできます。

    そして、プロセスが共有メモリにデータを書き込むと、その変更は同じ共有メモリにアクセスできる他のプロセスに直ちに影響します。

    mmap() とその関連システム コール

    mmap は、Linux オペレーティング システムによってユーザー空間に提供されるメモリ マッピング関数です。多くの人は、プロセス間のメモリ共有が完了できることしか知りません。 mmap。また、ユーザー モードからカーネル モードへのデータ コピーの数を減らしますが、mmap がオペレーティング システム内でどのように実装され、その原理が何であるかについては深く理解していません。

    Linuxのプロセス間通信を実装する方法

    ##mmap() システムを使用する 呼び出したプロセスは、同じ通常のファイルをマッピングすることでメモリを共有できます。通常のファイルがプロセスのアドレス空間にマップされると、プロセスは読み取りおよび書き込み操作を呼び出すことなく、通常のメモリと同様にファイルにアクセスできます。

    注: mmap は完全に IPC 用に設計されているわけではありません。これは単なる IPC のアプリケーション メソッドであり、それ自体が通常のメモリにアクセスするのと同じように、通常のファイルへのアクセスを提供します。

    特別な権限セットを持つ仮想メモリ セグメントを使用することによって実現されます。この種の仮想メモリ セグメントを読み書きするとき、オペレーティング システムは対応するディスク ファイル部分を読み書きします。

    Themmap 関数は、オープン ファイル記述子を通じてアクセスできるファイルの内容に関連付けられたメモリ領域へのポインタを作成します。

    以下で説明します。

    mmap()

    #include <sys/mman.h>
    
    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

    offset パラメータを渡すことで、共有メモリ セグメントを通じてアクセスされるファイル内のデータの開始オフセット値を変更できます。

    オープン ファイル記述子は、fd パラメーターによって指定されます。

    アクセスできるデータの量 (つまり、メモリ セグメントの長さ) は、length パラメータによって設定されます。

    addr パラメータを使用して、特定のメモリ アドレスの使用を要求できます。その値がゼロの場合、結果ポインタは自動的に割り当てられます。この慣行に従わないと、使用可能なアドレス範囲がシステムごとに異なるため、プログラムの移植性が低下します。

    prot パラメータは、メモリ セグメントのアクセス許可を設定するために使用されます。これは、次の定数値のビット単位の OR の結果です。

    • PROT_READ メモリ セグメントは読み取り可能です。

    • PROT_WRITE メモリ セグメントは書き込み可能です。

    • PROT_EXEC メモリ セグメントは実行可能です。

    • PROT_NONE メモリ セグメントにアクセスできません。

    #flags パラメータは、メモリ セグメントに対するプログラムの変更の影響を制御します。

    Linuxのプロセス間通信を実装する方法

    mmap() は共有メモリに使用されます。

    通常のファイルによって提供されるメモリ マッピングを使用します。これは、あらゆるプロセスに適しています。この方法を使用するには、最初にファイルを開くか作成してから、ngmmap を呼び出す必要があります。一般的な呼び出しコードは次のとおりです:

    fd = open(name.falg.mode);
    if(fd < 0)
    ptr = mmap(NULL,len.PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    使用特殊文件提供的内存映射,适用于具有亲缘关系的进程之间,由于父子进程特殊的亲缘关系,在父进程中先调用mmap,调用fork,那么在代用fork之后,子进程可以继承父进程匿名映射后的地址空间,同样也继承mmap返回的地址,这样父子进程就可以通过映射区域进行通信了。(注意:一般来说,子进程单独维护从父进程继承而来的一些变量,而mmap()返回的地址由父子进程共同维护)【具体使用实现敬请期待博主整理】

    munmap()

    用于解除内存映射,取消参数start所指的映射内存的起始地址,参数length则是欲取消的内存大小,当进程结束或者利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述符时不会解除映射。

    #include <sys/mman.h>
    
    int munmap(void *addr, size_t length);

    共享内存的使用

    与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h 中。 

    1.获取或创建内核对象,并且制定共享内存的大小(系统分配物理空间是,按照页进行分配)

    int shmget(key_t key, int size, int flag);

    只是创建内核对象,并申请物理空间

    • key_t key:与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,不同的进程通过相同的key值来访问同一块共享内存

    • int size:size以字节为单位指定需要共享的内存容量

    • int flag:falg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

    返回值

    • 调用成功后,shmget()函数会返回一个非负整数,用作后续共享内存函数的共享内存标识符,该标识符与key相关。

    • 调用失败返回-1.

    2.分配自己虚拟地址空间映射到共享内存的物理空间上

    void *shmat(int shmid,const void *addr, int flag);
    • shmid:shmid是由shmget()函数返回的共享内存标识。

    • void *addr:addr指定共享内存连接到当前进程中的地址位置,通常为NULL,表示让系统来选择共享内存的地址。

    • int flag:flag是一组标志位,通常为0。

    调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

    3.断开当前进程与共享内存的映射

    不使用删除而使用断开的原因是因为:也许还存在其他的进程会继续使用这块共享内存

    int shmdt(const void *addr);

    4.操作共享内存的方法

    int shmctl(int shmid, int cmd, struct shmid_t *buf);
    • int shmid:shmid是shmget()函数返回的共享内存标识符。

    • int cmd:command是要采取的操作,它可以取下面的三个值 :

    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

    IPC_RMID:删除共享内存段

    • struct shmid_t *buf:buf是一个结构指针,它指向共享内存模式和访问权限的结构

    因为有连接计数器,除非最后一个进程与该共享段断开连接,则删除该共享段。否则,并不会真正删除该共享段,但是共享内存的内核对象会被立即删除,不能使用shmat方法与该段连接。 

    一个进程调用该方法删除后,不会影响之前已经和该共享存储段连接的进程

    下面我们利用共享内存来进行一个简单的测试:

    Linuxのプロセス間通信を実装する方法

    Linuxのプロセス間通信を実装する方法

    完成下面的过程,

    Linuxのプロセス間通信を実装する方法

    成功在共享内存中读到了数据

    Linuxのプロセス間通信を実装する方法

    #include<stdio.h>
    #include<stdlib.h>
    #include<assert.h>
    #include<string.h>
    #include<unistd.h>
    
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/shm.h>
    
    #include"sem.h"
    
    #define READSEM 1
    #define WRITESEM 0
    
    int main()
    {
    	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
    	assert(shmid != -1);
    
    	char *ptr = (char*)shmat(shmid,NULL,0);
    	assert(ptr != (char*)-1);
    	
    	int initVal[] = {1,0};
    	int semid = SemGet(1234,intVal,2);
    	assert(semid != -1);
    	
    	//A进程写
    	while(1)
    	{
    		SemP(semid,WRITESEM);
    		printf("Input:");
    		
    		fgets(ptr,127,stdin);
    		
    		SemV(semid,READSEM);
    		
    		if(strncmp(ptr,"end",3) == 0)
    		{
    			break;
    		}
    	}
    	
    	shmdt(ptr);
    	exit(0);
    }
    #include<stdio.h>
    #include<stdlib.h>
    #include<assert.h>
    #include<string.h>
    #include<unistd.h>
    
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/shm.h>
    
    #include"sem.h"
    
    #define READSEM 1
    #define WRITESEM 0
    
    int main()
    {
    	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
    	assert(shmid != -1);
    
    	char *ptr = (char*)shmat(shmid,NULL,0);
    	assert(ptr != (char*)-1);
    	
    	int initVal[] = {1,0};
    	int semid = SemGet(1234,intVal,2);
    	assert(semid != -1);
    	
    	//B进程读
    	while(1)
    	{
    		SemP(semid,READSEM);
    		
    		if(strncmp(ptr,"end",3) == 0)
    		{
    			break;
    		}
    		
    		int i = 0;
    		for(;i < strlen(ptr) - 1;i++)
    		{
    			printf("%c",toupper(ptr[i]));
    			fflush(stdout);
    			sleep(1);
    		}
    		printf("\n");
    		SemV(semid,WRITESEM);
    	}
    	
    	shmdt(ptr);
    	exit(0);
    }

    从上面的代码中我们可以看出: 

    共享内存是最快的IPC,在通信过程中少了两次数据的拷贝。(相较于管道)

    命令管理共享内存

    • 查看 ipcs -m

    • 删除 ipcrm -m shmid

    以上がLinuxのプロセス間通信を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。