linux进程间内存共享和信号量协作[基于哈工大操作系统实验]

进程间的协作一直是做产品的永恒需求和必需技术,但是却是一片技术灰色地带,这里我们基于哈工大的操作系统实验,用实际代码讲解如何完成内存共享,和信号量协作


先给出一套完整可用的样例程序:

生产者producer.c:


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#define PRODUCT 1000
#define KEYS 0x345379
#define SIZE 4096
#define SHM_EXIST_ERROR -1
int main()
{
	int shmid,*myArray,tmp_counter,id_counter =0;
	key_t mem_key;
	sem_t *shop_sig,*consumer_sig;
	int * tests;
	mem_key=KEYS;
	shmid = shmget(mem_key,SIZE,0666);
	if(shmid != SHM_EXIST_ERROR)
	{
		tests = (int*)shmat(shmid,NULL,0);
		if ( tests!= (void *)-1)
		{
			shmdt(tests);
			shmctl(shmid,IPC_RMID,0) ;
		}
	}
	shmid=shmget(mem_key,SIZE,IPC_CREAT|0666|IPC_EXCL);
	if(shmid==SHM_EXIST_ERROR)
	{printf("ERROR: shmget");return 0;}
	myArray=(int *)shmat(shmid,NULL,0);
	shop_sig = sem_open("shop_signal",O_CREAT,0644,1);
	consumer_sig = sem_open("consumer_signal",O_CREAT,0644,0);

	for(tmp_counter=1;tmp_counter<11;tmp_counter++)
	{
		myArray[tmp_counter]=-1;
	}
	myArray[0]=0;
	while(id_counter++<PRODUCT)
	{
		sem_wait(shop_sig);
		if(myArray[0]<10)
		{
			myArray[myArray[0]+1]=id_counter;
			myArray[0]++;
		}
		sem_post(consumer_sig);
	}
	myArray[0]=-1;
	sem_post(consumer_sig);
	printf("shop is quitting\n");
	sem_close(shop_sig);sem_close(consumer_sig);
	return 0;
}


消费者进程 consumer.c


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#define PRODUCT 1000
#define KEYS 0x345379
#define SIZE 4096
#define SHM_EXIST_ERROR -1
int main()
{
	int shmid,*myArray,tmp_counter;
	key_t mem_key;
	sem_t *shop_sig,*consumer_sig;
	mem_key=KEYS;
	shmid=shmget(mem_key,SIZE,0666);
	if(shmid == SHM_EXIST_ERROR)
	{
		printf("ERROR: shmget failure\n");return 0;
	}
	myArray=(int *)shmat(shmid,NULL,0);
	shop_sig = sem_open("shop_signal",O_CREAT,0644,1);
	consumer_sig = sem_open("consumer_signal",O_CREAT,0644,0);
	tmp_counter=0;
	while(myArray[0]!=-1)
	{
		sem_wait(consumer_sig);
		if(myArray[0]>0)
		{
			printf("con: %d\n",myArray[myArray[0]]);
			myArray[0]--;
		}
		sem_post(shop_sig);
	}
	printf("consumer is quitting\n");
	sem_close(shop_sig);sem_close(consumer_sig);
	sem_unlink("shop_signal");sem_unlink("consumer_signal");
	shmdt(myArray);
	shmctl(shmid,IPC_RMID,NULL);
	return 0;
}


控制台编译语法:

>gcc -o producer producer.c -lpthread
>gcc -o consumer consumer.c -lpthread


控制台运行语法:

>sudo ./producer &
>sudo ./consumer

因为涉及高权限的信号和内存操作,所以需要superUser权限来do  (这就是sudo)

控制台&符号会将程序置于后台运行,这样就可以运行两个程序了,而且似乎可以递归地运行多个(不同机器表现略不同)。

注:样例代码请同学们严重修改后再使用。

想学技术的小伙伴可以继续看:


我们现在开始讲技术,上述涉及两种技术:1,信号量  2,共享内存 我们一一道来


一,信号量

信号量是:请见百度百科

信号量的抽象用法:请见百度百科

linux信号量具体实现:

struct _sem {
	__uint32_t	_magic;
	struct _usem	_kern;
};

typedef	struct _sem	sem_t;

semaphore 单词是打信号和打旗语的意思

1,sem_t * sem_ open(const char * name , int flag, mode_t authority_mode, int init_value );

2,int sem_wait(sem_t * sem);

3,int sem_post(sem_t * sem);

4,int sem_close(sem_t* sem);

5,int sem_unlink(const char * sem_name);


sem_open返回一个有名信号量的系统分配指针,参数字符串name 是名字,

       flag是创建方式【O_CREAT:没有则创建,有则返回;  O_EXCL:没有就报错;仅给权限值:0666、 有就返回,没有就报错】,

       mode是权限模式(四个用户态的权限,一般用0666最高或0644中等),value是信号的初始值,仅在创建新信号量时用于赋值。

sem_wait 等待指定信号量大于0,这时信号量减一。

sem_opst 指定信号量值加一。

sem_close 保存指定信号量状态到系统文件,断开信号量连接,不删除信号量,可以再次open不创建新的,并直接读取原有信号值。

sem_unlink 从系统删除指定名字信号量的存储文件,清空值和连接,下次使用同名信号量时open函数将重建文件并初始化值。


二,共享内存

key_t 是一个宏定义,原型是signed_Int32

size_t 是一个宏定义 ,原型是 自由机器整形Integer 就是...整形

0, key_t ftok( const char * fname, int id );

1, int shmget(key_t key,size_t size,int authority_mode);                                     shared memory get

2, void * shmat(int shmid,const void *shmaddr , int location);                          shared memory allocate

3, shmdt(void * memory);                                                                                 shared memory delete

4, shmctl(int shmid , int cmd , struct shmid_ds *buf);                               shared memory change table link


ftok 根据你给出的存在的文件路径找到文件的INODE,取出节点编号加上参数2,返回给你。一般较少使用这个函数

       注意:你可以自己定义key ,没有任何问题,定义的key 可以任意赋值,建议大于10000,不要碰受保护的共享内存。

shmget 根据你给出的key 和大小新建或返回内存索引号码。其中参数3 mode 【IPC_CREAT:没有则创建,有则返回 ; IPC_EXCL:没有就报错】。

       注意:UBUNTU-linux是带有严格权限的用户系统,所以ubuntu下用get时,参数三要加上权限才能允许执行,例如shmget(mykey,4096,IPC_CREAT|0666);

       注意:用shmget(mykey,4096,06xx)形式的语句打开一块确定存在的内存时,权限标志要和申请时一致,或者权限更小,否则报错。

shmat 根据内存索引号返回可用内存地址指针(强转即可) ,shmaddr是你设置的物理地址,设置为null让系统自动分配,location是返回指针相对于内存0的偏移值,一般给0

shmdt 保存指定内存数据到系统文件,断开内存连接,不释放内存,可以再次open不创建新的,并直接读取上次的内存数据。

shmctl 改变内存状态,常用于释放共享内存,参数2是操作类型

【IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
IPC_RMID 从系统释放指定索引ID的内存,清空备份文件和连接,下次使用同KEY内存时将重建共享文件和内存并初始化内存。】

参数3给NULL;


从shmat得到指针后,可以像数组一样使用,例如:

int * mymem =(int *) shamt(shmid,NULL,0);

mymem[i]=i;

struct Node *  mynode = (struct Node * ) shmat(shmid,NULL,0);

mynode->data = 1;







讲技术,说人话

Aurora极光城

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值