Lab Week 07

lab Week 07.

实验内容:

实验内容:进程间通信 — Linux System Call 共享内存

建立一个足够大的共享内存空间结构 (lock, M),逻辑值 lock 用来保证同一时间只有一个进程进入 M;测试在你的系统上 M 的容量上限。

设计一个程序,在 M 上建立一个结点信息结构为 (flag, 学号, 姓名) 的列表 L,逻辑值 flag 用作结点的删除标识;在 L 上建立一个以学号为关键字的二元小顶堆,自行设计结点的控制结构 (如静态指针数据域)。

设计一个程序对上述堆结构的结点实现插入、删除、修改、查找、排序等操作。该程序的进程可以在同一主机的多个终端并发执行。

思考:使用逻辑值 lock 实现的并发机制不能彻底解决访问冲突问题。

Part1:

建立一个足够大的共享内存空间 (lock, M),逻辑值lock用来保证同一时间只有一个进程进入M;测试你的系统上 M 的上限。

代码如下

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <fcntl.h>
#define TEST_SIZE 2147483640


struct shared_struct {
    char test[TEST_SIZE];/* buffer for message reading and writing */
    int lock;/* lock = 0: buffer writable; others: readable */
};//共享结构体

#define PERM S_IRUSR|S_IWUSR|IPC_CREAT

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    struct stat fileattr;
    key_t key; /* of type int*/
    int shmid; /* shared memory ID */
    void *shmptr;
    struct shared_struct *shared; /* structured shm*/
    pid_t childpid1, childpid2;
    char pathname[80], key_str[10], cmd_str[80];
    int shmsize, ret;

    shmsize = sizeof(struct shared_struct); //共享内存的大小
    printf("shm size = %d\n", shmsize);
    if(argc <2) {
        printf("Usage: ./a.out pathname\n");
        return EXIT_FAILURE;
    }

    strcpy(pathname, argv[1]);

    if(stat(pathname, &fileattr) == -1) {
        ret = creat(pathname, O_RDWR);
        if (ret == -1) {
            ERR_EXIT("creat()");
        }
        printf("shared file object created\n");
    }
 
    key = ftok(pathname, 0x27); /* 0x27 a project ID 0x0001 - 0xffff, 8 least bits used */
	//把路径名和整数标识符转换成IPC键值

    if(key == -1) {
        ERR_EXIT("shmcon: ftok()");
    }

	printf("key generated: IPC key = %x\n", key); /* can set any nonzero key without ftok()*/

    shmid = shmget((key_t)key, shmsize, 0666|PERM);

	if (shmid == -1) {
        printf("The shared memory size is %d, which exceeds the maximum shared memory limit\n", shmsize);//申请超过上限
        ERR_EXIT("hread: shmget()");
    }
    printf("shmcon: shmid = %d\n", shmid);
	
    //成功返回共享存储段的指针,出错返回-1
    shmptr = shmat(shmid, 0, 0);

    if(shmptr == (void *)-1) {
        ERR_EXIT("shread: shmat()");
    }
    printf("shmcon: shared Memory attached at %p\n", shmptr);
    
    shared = (struct shared_struct *)shmptr;
    shared->lock = 0;
	//设置lock使共享内存可写
    sprintf(cmd_str, "ipcs -m | grep '%d'\n", shmid);
    //ipcs -m,查看共享内存

    printf("\n------ Shared Memory Segments ------\n");
    system(cmd_str);
    if (shmdt(shmptr) == -1) 
    {
   //把共享内存从当前进程中分离,使该共享内存对当前进程不再可用
        ERR_EXIT("shmread: shmdt()");
    }
 
    printf("The shared memory size is %d, which is within the maximum shared memory range\n", shmsize);
   
      if (shmctl(shmid, IPC_RMID, 0) == -1) { //删除共享内存
         ERR_EXIT("shmcon: shmctl(IPC_RMID)");
       }
	exit(EXIT_SUCCESS);
}

在这里插入图片描述
经过手动二分,确定该系统能创建共享内存的最大值。
当TEST_SIZE设置为2147483640时,创建的共享内存数据段大小为2147483644,创建成功,为该系统能创建共享内存的最大值,接近int的上限。

当TEST_SIZE设置为2147483641时,创建的共享内存数据段大小为2147483645超过限制,因此创建失败。

Part2:

设计一个程序,在 M 上建立一个结点信息结构为 (flag, 学号, 姓名) 的列表 L,逻辑值 flag 用作结点的删除标识;在 L 上建立一个以学号为关键字的二元小顶堆,自行设计结点的控制结构 (如静态指针数据域)。
设计一个程序对上述堆结构的结点实现插入、删除、修改、查找、排序等操作。该程序的进程可以在同一主机的多个终端并发执行。

学生struct包含 id ,name ,flag。
flag为1 表示该学生已经删除。

代码文件1 heapdata.h
#include<string.h>

#define TEXT_SIZE 4*1024  /* size of each message */
#define STUDENT_NUM 10007 /* maximal number of students */
#define QUE_NUM 1      /* maximal number of students queue */
    /* total size can not exceed current shmmax,
       or an 'invalid argument' error occurs when shmget */

/* a demo structure, modified as needed */

struct student{
    	int flag;
        int id;
        char name[TEXT_SIZE];
	};
	
struct shared_struct {
    int written; /* flag = 0: buffer writable; others: readable */
    int quit; /* flag=0: running; others:quit process */
    int exit;
    
    int l; /* head of queue */
    int r; /* tail of queue */
    int n; /* size of queue set by user */
    int num,totnum;
    int lock;
    
    struct student heap[STUDENT_NUM]; /* buffer for students queue reading and writing */	
};

void swap(struct student *a,struct student *b)
{
	int tmp=a->flag;
	a->flag=b->flag;
	b->flag=tmp;
	
	tmp=a->id;
	a->id=b->id;
	b->id=tmp;
	
	char tt[TEXT_SIZE];
	strcpy(tt,a->name);
	strcpy(a->name,b->name);
	strcpy(b->name,tt);
	
}

void push(struct shared_struct* x,  char *s,int idd)
{
	x->totnum++;
	x->num++;
	x->heap[x->totnum].flag=0;// 1 means deleted
	x->heap[x->totnum].id=idd;
	strcpy( x->heap[x->totnum].name , s);
	
	int now=x->totnum;
	while( now!=1 && x->heap[now].id < x->heap[now/2].id )	
	{
		swap( &x->heap[now] , &x->heap[now/2]);
		now/=2;
	}
}


void pop(struct shared_struct* x)
{
	swap(&x->heap[x->totnum],&x->heap[1]);
	if( x->heap[x->totnum].flag==0)
	x->num--;
	x->totnum--;
	
	int now=1;
	while(1)
	{
		if(now*2 > x->totnum)
		break;
		
		if(  ( now*2+1> x->totnum || x->heap[now*2].id < x->heap[now*2+1].id ) && x->heap[now].id > x->heap[now*2].id)
		swap( &x->heap[now], &x->heap[now*2]);
		else
		if( now*2+1<= x->totnum && x->heap[now].id > x->heap[now*2+1].id)
		swap( &x->heap[now], &x->heap[now*2+1]);
		else
		break;
	}
}

void work(struct shared_struct* x)
{
	while(x->heap[1].flag==1 && x->totnum>=1)
	pop(x);
}

struct student top(struct shared_struct* x)
{
	work(x);
	return x->heap[1];
}

int small(struct student x, struct student y)
{
	if( x.flag==y.flag)
	return x.id<y.id;
	return x.flag<y.flag;
}

void ssort(struct shared_struct* x)
{
	for(int i=1;i<=x->totnum;i++)
	for(int j=i+1;j<=x->totnum;j++)
	if( small(x->heap[j] , x->heap[i]))
	swap(&x->heap[i] , &x->heap[j]);
	x->totnum=x->num;
}

void del(struct shared_struct* x,char *s)
{
	for(int i=1;i<=x->totnum;i++)
	if(strcmp(x->heap[i].name,s)== 0)
	{
		if( x->heap[i].flag==0)
		x->num--;
		
		x->heap[i].flag=1;
	}
}

void init(struct shared_struct* x)
{
	x->exit=0;
	x->totnum=0;
	x->num=0;
	x->lock=0;
}

#define PERM S_IRUSR|S_IWUSR|IPC_CREAT

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

其中num表示有效的学生数量,totnum表示当前堆的大小,lock为0表示当前共享内存空间可操作。
该程序没有使用静态指针域,而是直接创建了一个堆,heap数组的元素满足小根堆的性质,父亲的id小于它的两个儿子(若存在)。
在共享数据空间创建了一个堆,以id为键值的小根堆,以传统的方法实现了堆的各种操作,有pushpop,删除,排序。
根据堆的性质,push操作和pop操作保证了log (n) 复杂度,但删除操作O(n) 实现,排序以 O(n^2) 实现。
删除时直接操作学生节点的 flag,不破坏堆的结构,不把它弹出堆中,即堆中可能包括已被删除的学生。

代码文件2 heapcon.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <fcntl.h>

#include "heapdata.h"

int main(int argc, char *argv[])
{
    struct stat fileattr;
    key_t key; /* of type int */
    int shmid; /* shared memory ID */
    void *shmptr;
    struct shared_struct *shared; /* structured shm */
    pid_t childpid1, childpid2;
    char pathname[80], key_str[10], cmd_str[80];
    int shmsize, ret;

    shmsize = QUE_NUM*sizeof(struct shared_struct);
    printf("max record number = %d, shm size = %d\n", QUE_NUM, shmsize);

    if(argc <2) {
        printf("Usage: ./a.out pathname\n");
        return EXIT_FAILURE;
    }
    strcpy(pathname, argv[1]);

    if(stat(pathname, &fileattr) == -1) {
        ret = creat(pathname, O_RDWR);
        if (ret == -1) {
            ERR_EXIT("creat()");
        }
        printf("shared file object created\n");
    }
 
    key = ftok(pathname, 0x27); /* 0x27 a project ID 0x0001 - 0xffff, 8 least bits used */
    if(key == -1) {
        ERR_EXIT("shmcon: ftok()");
    }
    printf("key generated: IPC key = 0x%x\n", key); /* or you can set any nonzero key without ftok()*/

    shmid = shmget((key_t)key, shmsize, 0666|PERM);
    if(shmid == -1) {
        ERR_EXIT("shmcon: shmget()");
    }
    printf("shmcon: shmid = %d\n", shmid);

    shmptr = shmat(shmid, 0, 0); /* returns the virtual base address mapping to the shared memory, *shmaddr=0 decided by kernel */

    if(shmptr == (void *)-1) {
        ERR_EXIT("shmcon: shmat()");
    }
    printf("\nshmcon: shared Memory attached at %p", shmptr);
    
    shared = (struct shared_struct *)shmptr;//
    
    sprintf(cmd_str, "ipcs -m | grep '%d'\n", shmid); 
    printf("\n------ Shared Memory Segments ------\n");
    system(cmd_str);
    
    init(shared);
   
    sleep(2);
    
    while(shared->exit==0)
	{
	
	printf("input 0 to exit");
	int x;
	scanf("%d",&x);
	if(x==0)
	break;
	
	sleep(5);
}

	printf("there are %d students in the heap\n",shared->num);

	for(int i=1;i<=shared->totnum;i++)
	if(shared->heap[i].flag==0)
	{
		printf("id %d name: %s \n",shared->heap[i].id,shared->heap[i].name);
	}


    
    if(shmdt(shmptr) == -1) {
        ERR_EXIT("shmcon: shmdt()");
    }

    printf("\nshmcon: shared Memory detached at %p", shmptr);
    printf("\n------ Shared Memory Segments ------\n");
    
    system(cmd_str);
    
    sprintf(key_str, "%x", key);
    char *argv1[] = {" ", key_str, 0};
    
            if (shmctl(shmid, IPC_RMID, 0) == -1) {
                ERR_EXIT("shmcon: shmctl(IPC_RMID)");
            }
            else {
                printf("shmcon: shmid = %d removed \n", shmid);
                printf("\n------ Shared Memory Segments ------\n");
                system(cmd_str);
                printf("\n\n"); 
            }
  
    exit(EXIT_SUCCESS);
}

主要代码部分,创建了一个shared_struct结构的共享内存空间,得到了对应的KEY值和shmid,再得到指针shmptr 和强制类型转换shared_struct类型的shared指针。
随后对shared_struct初始化。
再以I/O挂起该程序,直到输入0之后退出,遍历一遍共享空间并输出。
再断开共享空间和该进程的连接,并注销共享空间。

代码文件3 heapwork.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/shm.h>
#include "heapdata.h"
int main(int argc, char *argv[])
{
    void *shmptr = NULL;
    struct shared_struct *shared;
    int shmid;
    key_t key;
 
    sscanf(argv[1], "%x", &key);
    //printf("%*sheapwork: IPC key = 0x%x\n", 30, " ", key);
    shmid = shmget((key_t)key, QUE_NUM*sizeof(struct shared_struct), 0666|PERM);
    
    //sscanf(argv[1], "%x", &shmid);
    
    if (shmid == -1) {
        ERR_EXIT("shread: shmget()");
    }
    shmptr = shmat(shmid, 0, 0);
    if(shmptr == (void *)-1) {
        ERR_EXIT("shread: shmat()");
    }
    printf("%*sheapwork: shmid = %d\n", 30, " ", shmid);    
    printf("%*sheapwork: shared memory attached at %p\n", 30, " ", shmptr);
    printf("%*sheapwork process ready ...\n", 30, " ");
    
    shared = (struct shared_struct *)shmptr;
    
    while (shared->exit ==0) 
    {
        while (shared->lock == 1) {
            sleep(1); /* message not ready, waiting ... */
        }
        
        shared->lock=1;
        
        int op;
        printf("1: push 2:pop 3:delete 4:sort 5:top 6: display 7:quit\n");
        printf("please input option number\n");
        scanf("%d",&op);
        
        char s[TEXT_SIZE];
        int id;
        
        if(op==1)
	{
		printf("please input the student name and id split by space\n");
		scanf("%s%d",s,&id);
		push(shared,s,id);
	}
	
	if(op==2)
	{
		work(shared);
		if(shared->num==0)
		printf("the heap is already empty!!\n");
		else
		{
			printf("the student %s id %d has been poped\n",shared->heap[1].name,shared->heap[1].id);
			pop(shared);
		}		
	}
	
	if(op==3)
	{
		printf("please input the name of the student that you want to delete\n");
		scanf("%s",s);
		printf("the students called %s are all deleted\n",s);
		del(shared ,s);
		
	}
	
	if( op==4)
	{
		ssort(shared);
		printf("the heap has been sorted\n");
	}
	
	if( op ==5)
	{
		work(shared);
		if(shared->num==0)
		printf("the heap is already empty!!\n");
		else
		{
			printf("the student at the top is %s with id %d\n",shared->heap[1].name,shared->heap[1].id);
		}		
	}
	
	if( op == 6)
	{
		printf("there are %d students in the heap\n",shared->num);
		for(int i=1;i<=shared->totnum;i++)
		if(shared->heap[i].flag==0)
		{
			printf("id: %d name: %s \n",shared->heap[i].id,shared->heap[i].name);
		}
		
	}
	
        
        if( op==7)
	{
		//shared->exit=1;
		shared->lock=0;
		break;
	}
        
        
        shared->lock = 0;
        sleep(2);
    } /* it is not reliable to use shared->written for process synchronization */
     
   if (shmdt(shmptr) == -1) {
        ERR_EXIT("heapwork: shmdt()");
   }
 
//    sleep(1);
    exit(EXIT_SUCCESS);
}

输入时需要接收主程序生成的key值,并通过shmget函数,以key值与整数生成相同的shmid。虽然shmid相同,但shmptr的值不同,即共享内存在两个程序内的虚拟地址不相同。再attach上共享内存,在程序结束时断开连接。
进入该程序后,会一直休眠到lock为0,并判断是否有其他相同进程改动了exit标志,即结束了该进程。
lock为0后,程序设置lock为1,并弹出用户操作界面。
提供了7种操作,分别为pushpoptop、删除、排序、展示,结束。分别调用heapdata.h中定义的对应函数,完成对共享内存的修改。
每一种操作完成后,设置lock为0,即内存空间可操作,并睡眠两秒。若有其他相同进程操作该空间,则可以接入,由系统随机选择。
在这里插入图片描述
如图所示,同时开启了3个终端访问创建的共享内存。
在没有删除操作时,display操作直接展示堆中全部元素,它们的顺序满足堆的性质。有删除操作后删除的节点在display中不显示。在sort后,相当于删去了堆中所有无效的学生,并按id大小严格排序。
在这里插入图片描述
可见该程序实现了堆的操作,在插入一个最小的学生 A 2,及 gg 0 时,能安按照堆的交换规则,以log(n) 的复杂度交换到top
在这里插入图片描述
在删除学生后,并非将其移除堆,而是找到对应的节点,打上标记。若查询到top为已经删除的节点,则pop即可,相当于懒惰标记。

在这里插入图片描述
在输入退出指令后,不再弹出用户界面,设置exit标志为1,新的进程不能再访问该共享空间。主程序在遍历一遍共享空间,确定程序正确性后,断开与共享内存的连接,并注销共享内存,输出反馈。

本次实验主要以上周实验为框架编写,运用了共享内存的创建,并把key值传递给需要利用该空间的进程,不同之处在于两个相同的进程同时访问共享内存,要设置lock保证同一时间只有一个进程访问,其他进程在休眠。

Part3:

思考:使用逻辑值 lock 实现的并发机制不能解决条件冲突问题。

使用lock逻辑值来防止多进程对共享空间的同时访问,以防止共享空间的混乱和异常,在单个cpu的状态下极小概率出现错误,仅当两个进程在同一时刻sleep完毕,同时苏醒,同时设置lock值,但没有转到寄存器,则有可能两个程序同时访问共享内存。出现概率极小,但可以人为精确调整系统时钟,或使用不合法的I/O使其出错。

但是对于多cpu来说,效果不理想,lock值的赋值要经过内存,寄存器,ALU,再到内存。
当进程有多个cpu占用的时候,lock的值会在寄存器操作的过程中被多次访问或者同时访问。如内存,寄存器的同时读写,从而导致条件冲突问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值