Linux进程间通信(中)

目录

一、共享内存shm

 示例

shmwr.c

shmrd.c

二、内存映射和共享内存的区别

三、消息队列msg queque


一、共享内存shm

        古老的System V IPC 包含:【共享内存】、【消息队列】、【信号灯集】。这类IPC对象有唯一的ID,用key值来关联,并且IPC创建后一直存在,直到被删除。此类IPC可通过 ipcs 命令来显示查看

        共享内存在内核空间创建全双工通信,最快的IPC方式允许多个进程访问同一块物理内存区域(需要注意同步和互斥机制来配合),共享内存的数据可多次读。

使用步骤:

        1.生成key值(不是必须的步骤,但通过节点号产生的key值可以确保是唯一的)

key_t ftok(const char *path, int proj_id)
功能:生成key来关联IPC的ID
    
@param: path 文件的节点号 习惯给 “.”
    	id  任取1-255
@return: 成功返回key值,失败返回-1

        2.创建/打开共享内存

int shmget(key_t key, int size, int shmflg)

@param: key  	ftok生成的key值,没生成可以任取 
    	size   	内存大小
    	shmflg 	创建时给IPC_CREAT|mode值,打开时给0
@return: 成功返回shmid,失败返回-1

        3.连接共享内存

void *shmat(int shmid, const void *shmaddr, int shmflg)

@param: shmaddr 一般给NULL系统自动分配
		shmflg 一般写0,表示可读可写
@return: 成功返回映射后的地址,失败返回(void *)-1

         4.数据读写通信可通过strcpy映射后地址来写,printf来读

        5.断开连接共享内存(表示当前进程不占用这块内存了)

int shmdt(shmaddr) 成功返回0失败返回-1

ipcs -m表中状态一栏 会显示当前占用共享内存的数量

此外,进程结束会自动断开连接

        6.控制共享内存(常用于删除共享内存,删除前应断开所有进程的连接)

int shmctl(shmid,cmd,struct shmid_ds *buf)
@param: cmd 一般给IPC_RMID
    	buf 保存或设置共享内存属性的地址 一般给NULL
@return:成功返回0失败返回-1    

 示例

实现两个进程 全双工通信

实际上就是利用父子进程,各自执行读、写,再加上利用信号去回收子进程(虽然是不阻塞且不关心~)

shmwr.c

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <wait.h>
#include <sys/types.h>
#include <unistd.h>


void *sig_child_handler(int signo);

int main()
{
	key_t key;
	int shmid;	
	void *addr;
	pid_t pid;
	char buf[128];

	//create key
	key = ftok(".",2);
	if(key == -1)
	{
		perror("ftok");
		return 0;
	}
	printf("key=%x\n",key);
	
	//create and open shared memory
	shmid = shmget(key,128,IPC_CREAT|0666);
	if(shmid == -1)
	{
		perror("shmget");
		return 0;
	}
	printf("shmid=%d\n",shmid);
	
	//connect shared memory
	addr = shmat(shmid,NULL,0);
	if(addr == (void*)-1)
	{
		perror("shmget");
		return 0;
	}
	printf("connect shared memory successfully!\n");

	if( (pid = fork()) < 0)
	{
		perror("fork");
		return 0;
	}
	else if(pid == 0) //child process read shm
	{
		while(1)
		{
			printf("read shm from shmrd:%s\n",(char *)addr);
			if(strncasecmp(addr,"quit",strlen("quit")) == 0) //输入了quit
			{
				break;
			}	
			sleep(2);
		}

	}
	else if(pid > 0) //father process write shm
	{
		signal(SIGCHLD,sig_child_handler);

		while(1)
		{
			bzero(buf,128);

			fgets(buf,128-1,stdin);		//leave 1 char space for '\n' that added automaticcally
			buf[strlen(buf) - 1] = '\0';//add end char manually
			memset(addr,0,128);			//clear before copy
			memcpy(addr,buf,strlen(buf));
			if( strncasecmp(buf,"quit",strlen("quit")) == 0) //if quit input
			{
				break;
			}
		}
	}


	return 0;
}

void* sig_child_handler(int signo)
{
    if(SIGCHLD == signo)
    {
        waitpid(-1,NULL,WNOHANG);//不阻塞等待、不关心---任一子进程
    }

}

shmrd.c

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <wait.h>
#include <sys/types.h>
#include <unistd.h>

void* sig_child_handler(int signo);

int main()
{
	key_t key;
	int shmid;	
	void *addr;
	pid_t pid;
	char buf[128];

	key = ftok(".",2);
	if(key == -1)
	{
		perror("ftok");
		return 0;
	}
	printf("key=%x\n",key);
	
	shmid = shmget(key,128,0);
	if(shmid == -1)
	{
		perror("shmget");
		return 0;
	}
	printf("shmid=%d\n",shmid);
	
	addr = shmat(shmid,NULL,0);
	if(addr == (void*)-1)
	{
		perror("shmget");
		return 0;
	}
	if( (pid = fork()) < 0)
	{
		perror("fork");
		return 0;
	}
	else if(pid == 0) //child process read shm
	{
		while(1)
		{
			printf("read shm from shmwr:%s\n",(char *)addr);
			if(strncasecmp(addr,"quit",strlen("quit")) == 0) //输入了quit
			{
				break;
			}	
			sleep(1);
		}
	}
	else if(pid > 0) //father process write shm
	{
		signal(SIGCHLD,sig_child_handler);

		while(1)
		{
			bzero(buf,128);

			fgets(buf,128-1,stdin);		//leave 1 char space for '\n' that added automaticcally
			buf[strlen(buf) - 1] = '\0';//add end char manually
			memset(addr,0,128);			//clear before copy
			memcpy(addr,buf,strlen(buf));
			if( strncasecmp(buf,"quit",strlen("quit")) == 0) //if quit input
			{
				shmdt(addr);
				shmctl(shmid,IPC_RMID,NULL);
				break;
			}
		}
	}

	return 0;
}

void* sig_child_handler(int signo)
{
    if(SIGCHLD == signo)
    {
        waitpid(-1,NULL,WNOHANG);//不阻塞等待、不关心---任一子进程
    }

}

二、内存映射和共享内存的区别

  • 内存映射需要磁盘文件(匿名映射除外);共享内存可以直接创建

  • 内存映射注意事项多;共享内存步骤稍繁琐

  • 内存映射实际上是进程把同个文件映射到各自地址空间,操作的是独立虚拟内存;

  • 共享内存所有进程操作都是对同一块共享内存直接操作,避免了拷贝开销

  • 使用场景上:内存映射特别适合于需要频繁读写大文件的场景,因为它可以减少磁盘 I/O 操作的次数。它也允许文件的一部分被映射到内存中,这对于处理大型文件尤为有用;共享内存通常用于进程间通信(IPC),允许多个进程访问相同的内存区域,这样可以非常高效地在进程之间交换数据


三、消息队列msg queque

        消息链表,消息可随机查询,全双工通信,存放在内核中,每个队列也都有key来关联,具有特定格式(成员包含消息类型、消息内容、其他节点的结构体)和优先级,独立于发送和接收进程,即不会因进程终止而自动删除,消息的读取遵循先进先出 

使用步骤:

        1.生成key值(不是必须的步骤,但通过节点号产生的key值可以确保是唯一的)

key_t ftok(const char *path, int proj_id)
功能:生成key来关联IPC的ID
    
@param: path 文件的节点号 习惯给 “.”
    	id  任取1-255
@return: 成功返回key值,失败返回-1

        2.创建或打开队列

int msgget(key_t key, int msgflg)

@param: key    ftok成功返回的键值
		msgflg IPC_CREAT|mode 没有则创建,有则打开
    
@return: 成功返回队列ID失败返回-1 

        3.发送/添加消息到队列

int msgsnd(int msgid, const void *msgp, size_t size, int msgflg)

@param: msgp 消息指针
    	size 消息长度 不包含消息类型的大小
    	msgflg  一般给0表示队列满了则阻塞等待,直到能写进去
                如果给IPC_NOWAIT表示消息队列满了则不等待立即返回

@return:成功返回0失败返回-1
    
发送消息时要遵循一定的格式,消息格式参考如下结构体:
typedef struct{
    long msg_type; //表示消息类型 这个必须包含 且为大于0的数
    char buf[128]; //消息长度
}msg1;

        4.从队列中读取/接收消息

int msgrcv(int msgid, const void *msgp, size_t size, long msgtype, int msgflg)


@param: msgtype =0 任意类型(遵循先进先出)的第一条消息 
    			>0 指定类型的第一条消息
    			<0 小于等于|msgtype|类型(也遵循先进先出)中的第一条消息
		msgflg  0 阻塞式等待接收消息
                IPC_NOWAIT 没有符合的类型消息立即返回
                IPC_EXCEPT 接收除msgtype指定的类型外的其他类型的第一条消息 
            
@return:成功时返回收到的消息长度,失败返回-1

注意:消息内容读到变量的buf里,类型读到msg_type里,消息读取一条,该类型的一条消息则少一条,读完了则阻塞等待消息添加进来

         5.控制队列(多用于删除)

int msgctl(int msgid, int cmd,struct msqid_ds *buf)

@param:  cmd IPC_RMID删除队列
    	 buf 一般给NULL即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值