进程间通讯(3)信号量 & 共享内存

3 篇文章 0 订阅
3 篇文章 0 订阅

 

信号量:用于进程间同步控制

       信号量就是具有原子性的计数器,相当于一把锁,进程要访问临资源时,必须向信号量获取锁,才能访问这个临界资源,同时其他进程因为无法“获得锁”而不能访问该临界资源,只有以获得锁的进程访问完了临界资源,将锁还给信号量后,其他进程才能“获得锁”去执行临界区代码。从而实现了进程间的同步控制。

       对信号量只有两种操作:等待(P)、发送信号(V

假设有一个信号量变量sv:

       P(sv):如果sv的值大于0,就给它减一;如果它的值为0,就挂起该进程的执行

       V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而被挂起,就给它加1

       即:当临界区资源可用时,信号量变量sv的值时true,然后P(sv)操作将它减1使他变为false,表示临界区域正在被使用;当进程离开临界区时,使用V(sv)操作将它加1,使临界区变为可用。

 

这里有一些概念:

       临界资源:不同进程可以看到的那份共同资源

       临界区:多个进程访问临界资源的代码

       原子性:一件事情只能有做了和没做两种状态

 

对成组的信号量值操作函数:#include<sys/sem.h>

       ①创建一个新信号量或取得一个已有信号量的键:

       int semget(key_t key, int num_sems, int sem_flags); //返回值信号标识符sem_id。

            key是整数值,不相关进程可以通过它访问同一个信号量

            num_sems指定要创建/获取的信号量集中信号的个数,如果是获取已经存在的信号量,则将它设为0。

       ②改变信号量的值:P  V 操作由此函数完成

       int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops); //sem_ops是一个指向结构数组的指针,结构类型 如下:

            struct sembuf{

                     short sem_num;   //信号量编号,除非是一组信号量,否则一般为0表示这是第一个也是唯一的一个信号量

                     short sem_op;    //信号量在一次操作中需要改变的数值:p(-1) /v(+1)

                     short sem_flg;    //使操作系统跟踪当前进程对这个信号量的修改情况,通常被设为SEM_UNDO

            }

       ③控制信号量的信息:

       int semctl(int sem_id,int sem_num,int command,…);  //

           command是将要采取的动作。如果有第四个参数,它就是一个union semun结构:

           union semun{

                    int val;

                    struct semid_ds *buf;

                    unsigned short *array;

           }

           command常取的值为:

                    SETVAL:把信号量初始化为一个已知的值,由第四个参数中的val设置。

                    IPC_RMID:删除一个已经无需继续使用的信号量标识符。

共享内存:最快的IPC

       共享内存允许两个或多个进程共享给定的存储区,这段存储区可以被两个或两个以上的进程映射至自身的地址空间中。一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过简单的内存读取读出,从而实现进程间的通讯。

       共享内存是最快的IPC,因为进程是直接对内存进行读写的,不需要任何数据的拷贝(像管道实际上一次消息的传递需要两次拷贝:写入管道和从管道中读出)。

A、B两个进程通过内核对象将各自的虚拟指针ptr都指向物理空间中的共享内存进行操作。

       共享内存并未提供同步机制,即在第一个进程介乎对共享内存的写操作之前,其他进程也可能会对共享内存进行操作。所以通常需要使用进程的同步机制来同步对共享内存的访问。如:信号量

      

       共享内存有多种实现方式:mmap系统调用,POSIX共享内存,以及System V共享内存。这里只介绍System V共享内存API。

 

共享内存的使用:#include<sys/shm.h>

①创建共享内存:

int shmget(key_t key, size_t size, int shmflg);  //成功返回共享内存的标识符;失败:返回-1

    key:为共享内存段命名。

   size:指定共享内存需要的内存容量,如果是获取已存在的共享内存,设为0.

   shmflg:对共享内存的访问权限

②把共享内存连接到当前进程的地址空间:

void *shmat(int shm_id, const void *shm_addr, int shmflg);

 //成功返回指向共享内存第一个字节的指针,失败返回-1.

  shm_id:共享内存标识符

  shm_addr:共享内存连接到当前进程中的地址位置,通常为空,由系统选择

  shmflg:标志位。SHM_RND(与shm_addr一起用,控制共享内存连接的地址)、SHM_RDONLY(使得连续的内存只读)

③把共享内存从当前进程分离:进程不能再使用这块共享内存

int shmdt(const void *ahmaddr); //成功返回0,失败返回-1

④控制共享内存:

int shmctl(int shm_id, int command, shruct shmid_ds *buf);

  shm_id:共享内存标识符

  command:要采取的动作:

          IPC_STAT —》把shmid_ds中的数据设为共享内存的当前关联值

          IPC_SET —》如果有权限,把共享内存的当前关联值设为shmid_ds中给出的值

          IPC_RMID —》删除共享内存

  buf:指向包含共享内存模式和访问权限的结构

        struct shmid_ds

        {

               uid_t shm_perm.uid;  //

               uid_t shm_perm.gid;  //

               mode_t shm_perm.mode;
        }

 

下面的程序实例时由信号进行同步控制共享内存进行进程间通讯

shm_com.h

/*
 * shm_com.h
 */
#include<sys/sem.h>

#define TEXT_SZ 2048
struct memory
{
	char text[TEXT_SZ];
};

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};

sever.c

/*
 *向共享内存中写入数据
 *使用信号量做同步控制
 */
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include"shm_com.h"

#define BUFFSIZE 1024


static int semid;   //信号量标识符

//信号量的p操作
int p()
{
	struct sembuf sem_p;
	sem_p.sem_num = 0;   //信号量在信号集中的编号
	sem_p.sem_op = -1;  //定义操作:-1
	sem_p.sem_flg = SEM_UNDO;
	//调用信号操作函数semop对信号值进行操作
	if(semop(semid,&sem_p,1) == -1)
	{
		printf("P operation failed\n");
		return 0;
	}
	return 1;
}
//信号量的v操作
int v()
{
	struct sembuf sem_p;
	sem_p.sem_num = 0;  //信号在信号集中的编号
	sem_p.sem_op = 1;  //定义操作:1
	//调用信号操作函数semop对信号值进行操作
	if(semop(semid,&sem_p,1) == -1)
	{
		printf("V operation failed\n");
		return 0;
	}
	return 1;
}

int main()
{
	//创建信号量
	semid = semget((key_t)1234,1,0666|IPC_CREAT);
	if(semid == -1)
	{
		printf("creat shm faill\n");
		exit(1);
	}
	
	//初始化信号量的值
	union semun sem_u;
	sem_u.val = 1;  //设置变量值
	semctl(semid,0,SETVAL,sem_u); //设置第0个信号

	struct memory *shared_memory; //共享内存的地址,初始化为0
	char buff[BUFFSIZE];
	int shmid;  //共享内存标识符

	
	//创建共享内存
	shmid = shmget((key_t)6789,sizeof(struct memory),0666|IPC_CREAT);
	if(shmid == -1)
	{
		fprintf(stderr,"shmget failed\n");
		exit(EXIT_FAILURE);
	}

	//把共享内存链接到当前进程的地址空间
	shared_memory = (struct memory*)shmat(shmid,0,0);
	if(shared_memory == (struct memory *)-1)
	{
		fprintf(stderr,"shmat failed\n");
		exit(EXIT_FAILURE);
	}

	//对共享内存进行写操作	
	//先进行P操作,获取信号量

	while(1)
	{
		if(!p())
		{
			exit(EXIT_FAILURE);
		}	

		//向共享内存中写入数据
		printf("Enter some text:");
		fgets(buff,BUFFSIZE,stdin);		
		strncpy(shared_memory->text,buff,TEXT_SZ);

		//写入完成,进行V操作,释放信号量
		if(!v())
		{
			exit(EXIT_FAILURE);
		}
		sleep(3);

		//输入了end,结束循环
		if(strncmp(buff,"end",3) == 0)
		{
			break;
		}
	}
	
	//把共享内存从进程中分离
	if(shmdt(shared_memory) == -1)
	{
		fprintf(stderr,"shmdt failed\n");
		exit(EXIT_FAILURE);
	}
	sleep(2);
	exit(EXIT_SUCCESS);
}

client.c

/*
 * 从共享内存中读取数据
 * 使用信号量进行同步控制
 */

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include"shm_com.h"

static int semid;//信号量标识符

int p()
{
	struct sembuf sem_p;
	sem_p.sem_num = 0;
	sem_p.sem_op = -1;
	sem_p.sem_flg = SEM_UNDO;
	if(semop(semid,&sem_p,1) == -1)
	{
		fprintf(stderr,"p operation failed\n");
		return 0;
	}
	return 1;
}

int v()
{
	struct sembuf sem_p;
	sem_p.sem_num = 0;
	sem_p.sem_op = 1;
	if(semop(semid,&sem_p,1) == -1)
	{
		printf("v operation failed\n");
		return 0;
	}
	return 1;
}

int main()
{

	//创建信号量
	semid = semget((key_t)1234,0,0666);
	if(semid == -1)
	{
		printf("creat shm failed\n");
		exit(1);
	}


	struct memory *shared_memory; //共享内存的地址,初始化为0
	int shmid;  //共享内存标识符

	//创建共享内存
	shmid = shmget((key_t)6789,0,0666);
	if(shmid == -1)
	{
		fprintf(stderr,"shmget failed\n");
		exit(EXIT_FAILURE);
	}

	//把共享内存链接到当前进程的地址空
	shared_memory = (struct memory*)shmat(shmid,0,0);
	if(shared_memory == (struct memory *)-1)
	{
		fprintf(stderr,"shmat failed\n");
		exit(EXIT_FAILURE);
	}

	
	//读取共享内存中的数据
	while(1)
	{
		if(!p())
		{
			exit(EXIT_FAILURE);
		}
	
		//从共享内存中读取数据
		printf("you wrote:%s",shared_memory->text);

		//读取完数据,v操作使得共享内存端可写
		//这里是写一句,读一句
		if(!v())
		{
			exit(EXIT_FAILURE);
		}
	
		sleep(6);

		//输入了end,退出循环
		if(strncmp(shared_memory->text,"end",3) == 0)
		{
			break;
		}
	}
	
	//把共享内存从进程中分离
	if(shmdt(shared_memory) == -1)
	{
		printf("shmdt is fail\n");
	}
	//删除共享内存  否则户一直留存在系统中
	if(shmctl(shmid,IPC_RMID,NULL) == -1)
	{
		fprintf(stderr,"shmctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}
	//删除信号量
	if(semctl(semid,0,IPC_RMID,0) == -1)
	{
		fprintf(stderr,"semctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}

	exit(EXIT_SUCCESS);
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值