进程通信算法设计

1. 实验目的

  • 通过基础实验,基本掌握共享内存的程序设计。
  • 通过编写程序,使读者掌握消息队列的设计方法。
  • 通过编写程序,掌握通道通信的设计方法

2. 实验内容

  • 共享内存程序设计:创建两个进程,在A进程中创建一个共享内存,并向其写入数据,通过B进程从共享内存中读出数据。
  • 消息队列程序设计:创建一个消息队列,如何使用消息队列进行两个进程(发送端和接受端)之间的通信,包括消息队列的创建、消息发送与读取、消息队列的撤销和删除等多种操作。
  • 管道通信程序设计:编写程序实现进程的管道通信,掌握有名管道的创建和读写方式,并熟悉LINUX支持的有名管道通信方式

3. 实验要求

    理解进程通信的不同方法,要求仿真实现共享内存方式或消息传递方式或管道方式。运行程序并分析实验结果。

    实验报告中给出程序中使用的数据结构及流程图。

4. 实验预习内容

     参阅相关资料,熟悉共享内存、消息队列、管道通信策略及实现技术。

5. 实验指导

(一)共享内存程序设计

  • 函数说明:共享内存的实现分为两个步骤,第一步是创建共享内存,这里用到的函数是shmget(),也就是从内存中获得一段共享内存区域。第二步映射共享内存,也就是把这段创建的共享内存映射到具体的进程空间中,这里使用的函数是shmat()。到这里,就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O读写命令对其进行操作。除此之外,当然还有撤销映射的操作,其函数为shmdt()。 
  • 共享内存的用法:使用共享内存进行进程间通信一般要经历下面几个步骤:
  1. 分配:进程通过调用shmget来分配一个共享内存块。
  2. 映射:要让一个进程获取对一块共享内存的访问,这个进程必须先调用shmat映射共享内存。
  3. 脱离与释放:当进程结束使用共享内存时,使用shmdt使共享内存脱离进程。当不再需要共享内存时,使用shmctl(sid,IPC_RMID,0)删除它。

实验步骤及代码:

1)自己建立文件夹,然后分别编辑shm_com.h、shm1.c、shm2.c.

/*shm_com.h*/
#define TEXT_SZ 2048
struct shared_use_st
{
        int written_by_you;
        char some_text[TEXT_SZ];
};

功能描述:本程序申请和分配共享内存,然后轮询并读取共享内存中的数据,直至读到“end”

/*shm1.c*/
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include "shm_com.h"
                                                                                
int main(void)
{
        int running=1;
        void *shared_memory=(void *)0;
        struct shared_use_st *shared_stuff;
        int shmid;
/*创建共享内存*/  
        shmid=shmget((key_t) 1234,sizeof(struct shared_use_st),
                        0666|IPC_CREAT);
        if(shmid==-1)
        {
                fprintf(stderr,"shmget failed\n");
                exit(EXIT_FAILURE);
        }
/*映射共享内存*/  
shared_memory=shmat(shmid,(void *)0,0);
        if(shared_memory==(void *)-1)
        {
                fprintf(stderr,"shmat failed\n");
                exit(EXIT_FAILURE);
        }
        printf("Memory attached at %X\n",(int)shared_memory);
/*让结构体指针指向这块共享内存*/  
        shared_stuff=(struct shared_use_st *)shared_memory;
/*控制读写顺序*/  
        shared_stuff->written_by_you=0;
/*循环地从共享内存中读数据,直到读到“end”为止*/  
        while(running)
        {
                if(shared_stuff->written_by_you)
                {
                        printf("You wrote:%s",shared_stuff->some_text);
            /*读进程睡眠1秒,同时会导致写进程睡眠1秒,做到先读后写	*/   
sleep(1);
                        shared_stuff->written_by_you=0;
                        if(strncmp(shared_stuff->some_text,"end",3)==0)
                        {
                                running=0;//结束循环
                        }
                }
        }
/*删除共享内存*/  
if(shmdt(shared_memory)==-1)
        {
                fprintf(stderr,"shmdt failed\n");
                exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
}

功能描述:本程序申请了上一段程序相同的共享内存,然后循环地向共享内存中写数据,直至写入“end”

/*shm2.c*/
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include "shm_com.h"
                                                                                
int main(void)
{
        int running=1;
        void *shared_memory=(void *)0;
        struct shared_use_st *shared_stuff;
        char buffer[BUFSIZ];
        int shmid;
/*创建共享内存*/  
        shmid=shmget((key_t) 1234,sizeof(struct shared_use_st),
                        0666|IPC_CREAT);
        if(shmid==-1)
        {
                fprintf(stderr,"shmget failed\n");
                exit(EXIT_FAILURE);
        }
/*映射共享内存*/  
shared_memory=shmat(shmid,(void *)0,0);
        if(shared_memory==(void *)-1)
        {
                fprintf(stderr,"shmat failed\n");
                exit(EXIT_FAILURE);
        }
        printf("Memory attached at %X\n",(int)shared_memory);
/*让结构体指针指向这块共享内存*/  
        shared_stuff=(struct shared_use_st *)shared_memory;
 /*循环地向共享内存中写入数据,直到写入的为“end”为止*/             
        while(running)
        {
                while(shared_stuff->written_by_you==1)
                {
                        sleep(1); //等到读进程读完之后再写
                        printf("waiting for client...\n");
                }
                printf("ENter some text:");
                fgets(buffer,BUFSIZ,stdin);
                strncpy(shared_stuff->some_text,buffer,TEXT_SZ);
                shared_stuff->written_by_you=1;
                if(strncmp(buffer,"end",3)==0)
{
                        running=0; //结束循环
                }
        }
                                                                                
 /*删除共享内存*/  
        if(shmdt(shared_memory)==-1)
        {
                fprintf(stderr,"shmdt failed\n");
                exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
}

2)使用gcc shm1.c o shm1和gcc shm2.c o shm2 分别编译shm1.c和shm2.c

3)在一个终端运行shm1,在另一个终端运行shm2。当shm1运行起来后,由于共享内存中没有数据可读,会处于等待状态。在向shm2运行的终端中输入字符串后,shm1能够逐个从共享内存中把它们读出来,直到双方遇到字符串“end”后,两个程序都退出。

运行shm1

运行shm2并输入字符串

shm1读出共享内存中的数据

(二)消息队列程序设计

       编写一个应用程序,创建一个消息队列,如何使用消息队列进行两个进程(发送端和接受端)之间的通信,包括消息队列的创建、消息发送与读取、消息队列的撤销和删除等多种操作。

(1)消息队列发送端的代码

/* msgsnd.c */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define  BUFFER_SIZE	512

struct message
{
	long msg_type;
	char msg_text[BUFFER_SIZE];
};

int main()
{
	int qid;
	key_t key;
	struct message msg;
	
	/*根据不同的路径和关键表示产生标准的key*/
	if ((key = ftok(".", 'a')) == -1)
	{
		perror("ftok");
		exit(1);
	}
	
	/*创建消息队列*/
	if ((qid = msgget(key, IPC_CREAT|0666)) == -1)
	{
		perror("msgget");
		exit(1);
	}
	printf("Open queue %d\n",qid);
	
	while(1)
	{
		printf("Enter some message to the queue(enter 'quit' to exit):");
		if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL)
		{
			puts("no message");
			exit(1);
		}
		
		msg.msg_type = getpid();
		
		/*添加消息到消息队列*/
		if ((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0)
		{
			perror("message posted");
			exit(1);
		}
		
		if (strncmp(msg.msg_text, "quit", 4) == 0)
		{
			break;
		}
	}
	
	exit(0);
}

(2)消息队列接收端的代码

/* msgrcv.c */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define  BUFFER_SIZE	512

struct message
{
	long msg_type;
	char msg_text[BUFFER_SIZE];
};

int main()
{
	int qid;
	key_t key;
	struct message msg;
	
	/*根据不同的路径和关键表示产生标准的key*/
	if ((key = ftok(".", 'a')) == -1)
	{
		perror("ftok");
		exit(1);
	}
	
	/*创建消息队列*/
	if ((qid = msgget(key, IPC_CREAT|0666)) == -1)
	{
		perror("msgget");
		exit(1);
	}
	printf("Open queue %d\n", qid);
	
	do
	{
		/*读取消息队列*/
		memset(msg.msg_text, 0, BUFFER_SIZE);
		if (msgrcv(qid, (void*)&msg, BUFFER_SIZE, 0, 0) < 0)
		{
			perror("msgrcv");
			exit(1);
		}
		printf("The message from process %d : %s", msg.msg_type, msg.msg_text);		
		
	} while(strncmp(msg.msg_text, "quit", 4));
	
	
	/*从系统内核中移走消息队列 */
	if ((msgctl(qid, IPC_RMID, NULL)) < 0)
	{
		perror("msgctl");
		exit(1);
	}
	
	exit(0);
}  

运行程序,输入quit两个程序都结束。

#./msgsnd

(三)管道通信程序设计

1、了解什么是管道,并熟悉LINUX支持的管道通信方式

    编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句话:

    Child 1 is sending a message!

    Child 2 is sending a message!

    父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。   

参考代码:

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int pid1,pid2;
 
main( )
{ 
int fd[2];
char outpipe[100],inpipe[100];
pipe(fd);                       /*创建一个管道*/
while ((pid1=fork( ))= =-1);
if(pid1= =0)
 {
lockf(fd[1],1,0);
 sprintf(outpipe,"child 1 process is sending message!"); 
/*把串放入数组outpipe中*/
write(fd[1],outpipe,50);     /*向管道写长为50字节的串*/
sleep(5);                 /*自我阻塞5秒*/
lockf(fd[1],0,0);
exit(0);
  }
else {
while((pid2=fork( ))= =-1);
if(pid2= =0)
{    lockf(fd[1],1,0);           /*互斥*/
sprintf(outpipe,"child 2 process is sending message!");
 write(fd[1],outpipe,50);
sleep(5);
 lockf(fd[1],0,0);
 exit(0);
 }
 else
 {  wait(0);              /*同步*/
read(fd[0],inpipe,50);   /*从管道中读长为50字节的串*/
printf("%s/n",inpipe);
wait(0);
read(fd[0],inpipe,50);
printf("%s/n",inpipe);
exit(0);
 }
 }
}

2、掌握有名管道的创建和读写方式,并熟悉LINUX支持的有名管道通信方式

  • 创建有名管道
  • 本进程执行循环等待数据被写入到管道中并读有名管道
  • 打开有名管道并写数据到名管道

参考代码://read_fifo.c

//read_fifo.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define BUFFER_SIZE	1024

int main(int argc, char **argv)
{
  int fd;

  if (argc < 2)
  {
    fprintf(stdout, "Usage: %s <filename>\n", argv[0]);
    exit(1);
  }

  //int open(const char *path, int oflag, ...);
  if ((fd = open(argv[1], O_RDONLY)) < 0)
  {
    fprintf(stderr, "open fifo %s for reading failed: %s\n", argv[1], strerror(errno));
    exit(1);
  }

  fprintf(stdout, "open fifo %s for reading successed.\n", argv[0]);
  char buffer[BUFFER_SIZE];
  ssize_t n;

  while (1)
  {
  again:
    //ssize_t read(int fd, void *buf, size_t count);
    if ((n = read(fd, buffer, BUFFER_SIZE)) < 0)
    {
      if (errno == EINTR)
      {
	goto again;
      }
      else
      {
	fprintf(stderr, "read failed on %s: %s\n", argv[1], strerror(errno));
	exit(1);
      }
    }
    else if (n == 0)
    {
      fprintf(stderr, "peer closed fifo.\n");
      break;
    }
    else
    {
      buffer[n] = '\0';
      fprintf(stdout, "read %d bytes from fifo: %s\n", n, buffer);
    }
  }
  return 0;
}

write_fifo.c

// write_fifo.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

#define BUFFER_SIZE	1024

void signal_handler(int s);

int main(int argc, char **argv)
{
  int fd;

  if (argc < 2)
  {
    fprintf(stdout, "Usage: %s <filename>\n", argv[0]);
    exit(1);
  }

  signal(SIGPIPE, signal_handler);

  //int open(const char *path, int oflag, ...);
  if ((fd = open(argv[1], O_WRONLY)) < 0)
  {
    fprintf(stderr, "open fifo %s for writting failed: %s\n", argv[1], strerror(errno));
    exit(1);
  }

  fprintf(stdout, "open fifo %s for writting successed.\n", argv[0]);

  char buffer[BUFFER_SIZE];
  ssize_t n;

  //char *fgets(char *s, int size, FILE * stream);
  while (fgets(buffer, BUFFER_SIZE, stdin))
  {
  again:
    //ssize_t write(int fd, const void *buf, size_t count);
    if ((n = write(fd, buffer, strlen(buffer))) < 0)
    {
      if (errno == EINTR)
      {
	goto again;
      }
      else
      {
	fprintf(stderr, "write() failed on fifo: %s\n", strerror(errno));
	// FIXME:
	break;
      }
    }
  }

  return 0;
}

void signal_handler(int s)
{
  fprintf(stdout, "Caught signal %d\n", s);
}

// create_fifo.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
  if (argc < 2)
  {
    fprintf(stdout, "Usage: %s <filename>\n", argv[0]);
    exit(1);
  }

  //int mkfifo(const char *path, mode_t mode);
  if (mkfifo(argv[1], 0644) < 0)
  {
    fprintf(stderr, "mkfifo() failed: %s\n", strerror(errno));
    exit(1);
  }

  return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值