linux实验8

实验八 Linux进程通信实验

学号:6130116217

专业班级:计算机科学与技术165班

课程名称:Linux程序设计实验

一、实验项目名称

Linux进程通信实验

二、实验目的

熟练掌握利用管道、消息队列以及共享内存方式实现进程之间的通信。

三、实验基本原理

Linux进程通信

四、主要仪器设备及耗材

硬件: PC机;
软件:Windows OS,VMware,Fedora10.0或其他Linux发行版。

五、实验步骤

1. 编写一个聊天程序,要求分别采用管道、消息队列以及共享内存三种不同方式实现。
①管道方式

进程一:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

int main(int argc,char* argv[])
{//c1->cl2 cl2->cl1
    int fd_send=open(argv[1],O_WRONLY);
    int fd_recv=open(argv[2],O_RDONLY);
    if(-1==fd_send||-1==fd_recv)
    {
        perror("open");
        exit(1);
    }
    printf("ok!\n");
    char buf[1024];
    while(memset(buf,0,1024),read(0,buf,1024)!=0)
    {
        write(fd_send,buf,strlen(buf));
        printf("begin read from pipe...\n");
        memset(buf,0,1024);
        read(fd_recv,buf,1024);
        printf("from cl2:%s",buf);
    }
    close(fd_send);
    close(fd_recv); 
    printf("pipe exit!\n");
    return 0;

进程二:

int main(int argc,char* argv[])
{//cl2->cl1 cl1->cl2
    int fd_recv=open(argv[2],O_RDONLY);
    int fd_send=open(argv[1],O_WRONLY);
    if(-1==fd_send||-1==fd_recv)
    {
        perror("open");
        exit(1);
    }
    printf("ok!\n");
    char buf[1024];
    while(printf("begin read from pipe...!\n"), memset(buf,0,1024),read(fd_recv,buf,1024)!=0)
    {
        printf("from cl1:%s",buf);
        memset(buf,0,1024);
        read(0,buf,1024);
        write(fd_send,buf,strlen(buf));
    }
    close(fd_send);
    close(fd_recv);
    printf("pipe exit!\n");
    return 0;
}

运行结果
在这里插入图片描述
在这里插入图片描述

②消息队列

进程1:

//这是一个以system V消息队列实现的聊天程序客户端
  1.创建消息队列
  2.从消息队列中获取一个数据,打印出来
  3.从标准输入中获取一个数据,组织成消息队列节点发送
  4.删除消息队列
      接口:
      创建        msgget
      接收数据    msgrcv
      发送数据    msgsnd
      控制        msgctl
      int msgget(key_t key,int msgflg);
      msgflg:
          key:内核中消息队列的标识
          IPC_CREAT 不存在则创建,存在则打开
          IPC_EXCL  与IPC_CREAT 同用,若存在则报错   防止程序重复启动
          mod 权限
          返回值:一个代码操作的句柄:失败:-1
      ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                             int msgflg);
          msgid:msgget返回的操作句柄
          smgp:用于接收数据
          msgsz:指定接收的数据大小
          msgtyp:指定接收的数据类型
          msgflg:标准选项,0
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define IPC_KEY 0x12345678 
//下面两个宏,用于我们赋值我们传输的数据块类型
#define TYPE_S 1
#define TYPE_C 2
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[1024];    /* message data */
};

int main()
{
    int msgid=-1;
      //ftok
      //    //key_t ftok(const char *pathname, int proj_id);
      //        //ftok通过文件的inode节点号和一个proj_id计算出一个key值
      //            //缺点:如果文件被删除,或者替换,那么将打开的不是同一个消息队列
      //
      //1.创建消息队列            
    msgid=msgget(IPC_KEY,IPC_CREAT | 0664);
    if(msgid<0){
        perror("msgget error");
        return -1;
    }
    while(1){
    //2.接收数据
    //struct msgbuf这个结构体需要我们自己定义
    struct msgbuf buf;
    //msgrcv:默认阻塞的获取数据
    //msgid:操作句柄
    //
    //buf:接收数据的结构体,自己定义
    //1024 用于指定接收的数据的最大长度,不包含mtype
    //TYPE_C 用于指定接收的数据类型
    //msgflag 0默认
    //    MSG_NOERROR 当数据长度超过指定长度,则截断数据
    msgrcv(msgid,&buf,1024,TYPE_C,0);
    printf("client say:%s\n",buf.mtext);
    //发送数据
    memset(&buf,0x00,sizeof(struct msgbuf));
    buf.mtype=TYPE_S;
    scanf("%s",buf.mtext);
    msgsnd(msgid,&buf,1024,0);
    }
    msgctl(msgid,IPC_RMID,NULL);       
    return 0;
}

进程2:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define IPC_KEY 0x12345678 
//下面两个宏,用于我们赋值我们传输的数据块类型
#define TYPE_S 1
#define TYPE_C 2
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[1024];    /* message data */
};

int main()
{
    int msgid=-1;
      //ftok
      //    //key_t ftok(const char *pathname, int proj_id);
      //        //ftok通过文件的inode节点号和一个proj_id计算出一个key值
      //            //缺点:如果文件被删除,或者替换,那么将打开的不是同一个消息队列
      //
      //1.创建消息队列            
    msgid=msgget(IPC_KEY,IPC_CREAT | 0664);
    if(msgid<0){
        perror("msgget error");
        return -1;
    }
    while(1){
    struct msgbuf buf;
    //发送数据
    memset(&buf,0x00,sizeof(struct msgbuf));
    buf.mtype=TYPE_C;
    scanf("%s",buf.mtext);
    msgsnd(msgid,&buf,1024,0);
    //2.接收数据
    //struct msgbuf这个结构体需要我们自己定义
    //msgrcv:默认阻塞的获取数据
    //msgid:操作句柄
    //
    //buf:接收数据的结构体,自己定义
    //1024 用于指定接收的数据的最大长度,不包含mtype
    //TYPE_C 用于指定接收的数据类型
    //msgflag 0默认
    //    MSG_NOERROR 当数据长度超过指定长度,则截断数据
    msgrcv(msgid,&buf,1024,TYPE_S,0);
    printf("server say[%s]\n",buf.mtext);
    }
    msgctl(msgid,IPC_RMID,NULL);
    return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

③共享内存

进程1:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
typedef struct {
	char str1[50];
	char str2[50];
	int no1,no2;
} str;
main(int argc, char** argv) {
	int shm_id,i;
	key_t key;
	str *p_map;
	time_t timep;
	struct tm *p;
	char* name = "/dev/shm/myshm2";
	key = ftok(name,0); /*调用ftok 函数,产生标准的key*/
	shm_id=shmget(key,4096,IPC_CREAT); /*调用shmget 函数,获取共享内存区域的ID*/
	if(shm_id==-1) {
		perror("获取共享内存区域的ID 出错");
		return;
	}
	p_map=(str*)shmat(shm_id,NULL,0);/* 调用shmat 函数,映射共享内存*/
	(*p_map).no1=0;
	(*p_map).no2=0;
	printf("This is A.\n");
	fgets((*p_map).str2,sizeof((*p_map).str2),stdin);
	(*p_map).no1=1;
	while(1) {
		if ((*p_map).no2==1) {
			time(&timep);
			gmtime(&timep);
			p=localtime(&timep);
			printf("B:%s%d:%d:%d\n",(*p_map).str1,p->tm_hour,p->tm_min,p->tm_sec);
			(*p_map).str1[0]='\0';
			(*p_map).no2=0;
		} else continue;
		if ((*p_map).no1==0) {
			fgets((*p_map).str2,sizeof((*p_map).str2),stdin);
			(*p_map).no1=1;
		}
	}
	if(shmdt(p_map)==-1) /*调用shmdt 函数,解除进程对共享内存区域的映射*/
		perror("解除映射出错");
}

进程2:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
typedef struct {
	char str1[50];
	char str2[50];
	int no1,no2;
} str;
main(int argc, char** argv) {
	int shm_id,i;
	key_t key;
	str *p_map;
	time_t timep;
	struct tm *p;
	char* name = "/dev/shm/myshm2";
	key = ftok(name,0); /*调用ftok 函数,产生标准的key*/
	shm_id = shmget(key,4096,IPC_CREAT); /*调用shmget 函数,获取共享内存区域的ID*/
	if(shm_id == -1) {
		perror("获取共享内存区域的ID 出错");
		return;
	}
	p_map = (str*)shmat(shm_id,NULL,0);
	(*p_map).no1=0;
	(*p_map).no2=0;
	printf("This is B.\n");
	while(1) {
		if ((*p_map).no1==1) {
			time(&timep);
			gmtime(&timep);
			p=localtime(&timep);
			printf("A:
			       %s%d:%d:%d\n",(*p_map).str2,p->tm_hour,p->tm_min,p->tm_sec);
			(*p_map).str2[0]='\0';
			(*p_map).no1=0;
		} else continue;
		if ((*p_map).no2==0) {
			fgets((*p_map).str1,sizeof((*p_map).str1),stdin);
			(*p_map).no2=1;
		}
	}
	if(shmdt(p_map) == -1) /*调用shmdt 函数,解除进程对共享内存区域的映射*/
		perror("解除映射出错");
}

运行结果:
在这里插入图片描述
在这里插入图片描述

2. 调试教材P285页例7.9

vim input.c

#include <string.h>  
#include <stdio.h>  
#include <ctype.h>  
  
int main()  
{  
    int  c;  
    //用户输入数据  
    while ((c = getchar()) != EOF)  
    {  
        if (isupper(c))  
        {    c = tolower(c);      }  
        //输出数据作为testinput的输入数据  
        if (putchar(c) == EOF)  
        {      fputs("output error\n", stdout);          }  
        if (c == '\n')  
        {   fflush(stdout);          }  
    }  
   return 0;  
} 

vim 2.c

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#define MAXLINE 100  
  
int main()  
{  
    char   line[MAXLINE];  
    FILE  *fpin;  
    //popen的第一个参数是命令,这里执行一个可执行文件  
    if ((fpin = popen("./input", "r")) == NULL)  
    {  
        fputs("popen error\n", stdout);  
    }  
      
    for ( ; ; )  
    {  
        fputs("prompt> ", stdout);  
        fflush(stdout);  
        if (fgets(line, MAXLINE, fpin) == NULL)  
        {      break;        }  
          
        if (fputs(line, stdout) == EOF)  
        {     fputs("fputs error to pipe", stdout);         }  
    }  
      
    if (pclose(fpin) == -1)  
    {        fputs("pclose error\n", stdout);        }  
    puts("");  
    exit(0);  
}  

编译运行
在这里插入图片描述
在这里插入图片描述

六、实验数据及处理结果

七、思考讨论题

对管道、消息队列以及共享内存三种不同进程通信方法进行比较
答:
1.管道(Pipe):管道可用于具有亲缘关系进程间的通信,有名管道,除了具有管道所具有功能外,它
还允许无亲缘关系进程的通信。
2.消息队列:(Messge Queue):消息队列是消息的连接表,包括Posix消息队列SystemV消息队列,它克服了前两种通信方式中信息量有限的的缺点,具有写权限的进程可以按照一定的规划向消息队列中添加新的消息,对消息队列有读权限的进程则可以从消息队列中读取消息。
3.共享内存(Shared memory):可以说这是最有用的进程通信方式,它使得多个进程可以访问同一块内存空间,
不同进程可以及时看到对方进程中对共享内存中数据的更新,这种通信方式需要依靠某种同步机制,如互斥锁和信号量等.

八、参考资料

1.金国庆等,Linux程序设计(第二版),浙江大学出版社,2014年4月
2. Neil Matthew,《Linux程序设计》(第4版), 人民邮电出版社,2014年9月
3. 杨宗德,《Linux高级程序设计》(第三版),人民邮电出版社,2012年11月
4. Daniel P.,《深入理解Linux内核》(第三版),中国电力出版社,2013年1月

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值