进程间通讯
摘 要:本课程设计通过消息队列,来模拟完成一个简单的客户端与服务器之间的通讯(交互)。有助于深入理解Linux环境中各进程间通讯的机制和步骤。
关键词: 消息队列 客户端/服务器 进程间通讯 shell命令
言
进程间通讯是操作系统的一个基本功能。理解好这个功能的原理可以帮助我们更好的理解操作系统原理。本文论述了如何模拟实现一个 Linux文件系统,主要完成对文件与目录进行一些基本的操作功能。本课程设计通过消息队列,来模拟完成一个简单的客户端与服务器之间的通讯(交互)。本课程设计有助于深入理解Linux环境中各进程间通讯的机制和步骤。功能如下:客户端接收用户输入的shell命令,发送给服务器端,服务器接收到命令后执行,并将执行结果返回给客户端,客户端显示输出结果。
2 系统总体框架设计
(1)程序设计思路:
A.为了便于操作和观察结果,编制了2个程序和1个头文件(功能模块)。2个程序分别用于客户端(用户请求)和服务器端(请求处理),头文件包含了通信过程中需要用到的函数。
B.在server程序中建立一个key为MSGKEY1的客户端消息队列和一个key为MSGKEY2的服务器端消息队列,当接收到来自客户端消息队列的消息(shell命令)后,将消息经过处理(将shell命令执行后的结果重定向)的结果链接到服务器端消息队列中。
C.在client程序中使用客户端消息队列(key为MSGKEY1),将用户输入的shell命令作为消息链接到客户端信息队列,经过服务器的处理,在从服务器端消息队列取出处理结果并打印出来。
D.调试时将俩个程序分别编辑、编译,生成client和server的可执行程序。分别在俩个终端执行这俩个程序。
3 数据结构设计
本课程设计中需要用到每个消息的数据结构,如下:
typedef struct msgbuf{
long mtype; //消息类型
char mtext[MAX_SIZE]; //消息文本
}msgbuf;
4 程序中各函数功能
本课程设计用c语言编写,由相关的函数来实现,相关函数见表所示,各函数完成功能如下:
(1)功能函数:
² 创建或者打开消息队列的函数
int msg_init(key_t msgkey){
int msgid; //消息队列描述符
msgid=msgget(msgkey,IPC_CREAT|0660);
/ /如果创建消息队列失败
if(msgid==-1)
{
perror("msgget()");
exit(1);
}
return msgid;
}
² 删除消息队列函数
void msg_rmv(int msgid){
int result;
result=msgctl(msgid,IPC_RMID,NULL); //消除消息队列描述符为msgid的队列
//如果消除失败
if(result==-1)
{
perror("msgctl()");
exit(1);
}
}
² 接收指定类型的消息
int read_message(int msgid,long type,msgbuf *buf)
{
int result,length;
length=sizeof(msgbuf)-sizeof(long); //接收的消息长度
result=msgrcv(msgid,buf,length,type,0);
//如果接收失败
If(result==-1)
{
perror("msgrcv()");
exit(1);
}
return result;
}
² 向指定的消息队列发送消息
int send_message(int msgid,msgbuf *buf)
{
int result,length;
length=sizeof(msgbuf)-sizeof(long); //发送的消息的长度
result=msgsnd(msgid,buf,length,0);
//如果发送失败
if(result==-1)
{
perror("msgsnd()");
exit(1);
}
return result;
}
² 撤销队列 msgid1和msgid2
void stop()
{
quit=1;
msg_rmv(msgid1);
msg_rmv(msgid2);
}
(2)系统函数:
² Signal():用来接收并设置对信号的处理方法
² Read():完成从fd所指示的文件读出nbyte个字节的数据并将他们送至buf缓冲区
² Msgget():打开或创建一个消息,获取消息描述符
² Msgsnd():向指定的消息队列发送一个消息
² Msgrcv():接收指定类型的消息
² Msgctl():完成消息队列的操纵
² memset(void *s,int c,size_t n):将已开辟内存空间 s 的首 n 个字节的值设为值 c
² Getpid():获取当前进程ID号
² Open():打开一个文件,获取文件描述符
² Perror():用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 错误 (stderr)
函数功能表
| 函数列表 | 功能说明 |
| Int msg_init(key_t msgkey) | 打开或创建消息队列 |
| void msg_rmv(int msgid) | 删除消息队列 |
| Int read_message(int msgid,long type,msgbuf *buf) | 接收指定类型的消息 |
| Int send_message(int msgid,msgbuf *buf) | 向指定的消息队列发送消息 |
| void stop() | 撤销队列 msgid1和msgid2 |
| Signal() | 接收并设置对信号的处理方法 |
| Read() | 读文件 |
| Msgget() | 打开或创建一个消息 |
| Msgsnd() | 发送消息 |
| Msgrcv() | 接收消息 |
| Msgctl() | 对消息进行操纵 |
| Memset() | 将已有的空间设值 |
| Getpid() | 获取进程号 |
| Open() | 打开文件,获取描述符 |
| Perror() | 将上一个函数的错误输出到标准 |
5 程序运行过程及结果
² 分别在2个终端里运行client.out server.out
² 在运行client.out的终端里输入shell命令 请求,例如:ls
² 在运行server.out的终端中显示接收到的shell命令
² 在运行client.out的终端中打印shell命令的执行结果
运行Server.out的终端结果显示
运行client.out的终端结果显示
6 结 束 语
本课程设计模拟实现了一个客户端与服务器之间通讯的的部分功能。通过本课程的设计,使我进一步的理解了进程间的通讯机制和原理。这课程设计的过程中,特别是调试程序的过程中,遇到了不小的麻烦,对于一些系统函数使用不是特别的理解,参数的设置也不太熟悉,通过这次的学习,基本上理解了这些系统函数的功能和使用。本设计也有一些缺陷,并不是对于所有的shell命令都有正确的结果输出,比如cd,有待进一步的改进。
参 考 文 献
[1] 操作系统概念(第七版)影印版 高等教育出版社,2007
[2] 费翔林 Linux操作系统实验教程 高等教育出版社,2009
[3] 张红光 蒋跃军 Unix操作系统实验教程 机械工业出版社,2006
[4] 汤小丹 梁红兵 汤子瀛等编著 计算机操作系统 西安电子科技大学出版设,2007
[5] 陆松年 訾小超 潘理 龚玲 编著 操作系统实验教程 电子工业出版社 2010
附录:
1、客户端程序
Client.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include "mesg.h"
int main()
{
int msgid1; //客户端消息队列的描述符
int msgid2; //服务器端消息队列的描述符
int mtype; //消息类型
char comstr[256];
msgbuf snd_buf,rcv_buf; //用户接收消息或发送消息的缓冲区
msgid1=msg_init(MSGKEY1); //打开客户端消息队列
msgid2=msg_init(MSGKEY2); //打开服务器端消息队列
printf("Please enter a shell command\n"); //输入一个shell命令
//如果要退出,输入“quit”
printf("if you want quit, please enter a string(quit)\n");
for(;;)
{
//将缓冲区清空
memset(rcv_buf.mtext,'\0',MAX_SIZE);
memset(snd_buf.mtext,'\0',MAX_SIZE);
//输入字符
printf("a shell command:");
gets(comstr);
//如果输入的字符为quit则退出
if(!strcmp(comstr,"quit"))
return 0;
mtype=getpid(); //用当前进程的ID号作为但前消息的消息类型号
snd_buf.mtype=mtype;
strcpy(snd_buf.mtext,comstr);
send_message(msgid1,&snd_buf); //将输入的shell命令作为消息发送到客户端消息队列中
read_message(msgid2,mtype,&rcv_buf); //从服务器端消息队列中接收类型号为mtype的消息放入缓冲区
//将当前消息(即shell命令的结果)打印出来
printf("Excute result: \n");
printf("%s\n",rcv_buf.mtext);
}
return 0;
2、服务器端程序
Server.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/stat.h>
#include "mesg.h"
int msgid1; //客户端消息队列的描述符
int msgid2; //服务器端消息队列的描述符
int quit=0;
//删除消息队列函数
void stop()
{
quit=1;
msg_rmv(msgid1);
msg_rmv(msgid2);
}
int main()
{
int mtype; //消息类型号
int fd; //文件描述符
char comstr[256];
//用户缓冲区
msgbuf snd_buf,rcv_buf;
msgid1=msg_init(MSGKEY1); //创建客户端消息队列
msgid2=msg_init(MSGKEY2); //创建服务器端消息队列
//中断退出程序
signal(SIGINT,stop);
signal(SIGQUIT,stop);
while(!quit)
{
//将用户缓冲区清空
memset(rcv_buf.mtext,'\0',MAX_SIZE);
memset(snd_buf.mtext,'\0',MAX_SIZE);
//接收来自客户端的消息,放入缓冲区
read_message(msgid1,0,&rcv_buf);
//打印从客户端接收到的shell命令
printf("Received shell command : %s\n",rcv_buf.mtext);
strcpy(comstr,rcv_buf.mtext);
//将shell命令执行后的结果重定向到test文档中
strcat(comstr," > test");
system(comstr);
fd=open("./test",O_RDONLY); //打开文件test
read(fd,snd_buf.mtext,MAX_SIZE); //将test里面的内容读入到缓冲区中
snd_buf.mtype=rcv_buf.mtype;
send_message(msgid2,&snd_buf); //将shell命令的执行结果作为消息添加到服务器端消息队列中
}
return 0;
}
3、功能模块程序
Mesg.h
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#define MSGKEY1 (key_t)0x3000 //指定消息队列1的key
#define MSGKEY2 (key_t)0x4000 //指定消息队列2的key
#define MAX_SIZE 1024 //消息文本的大小
//用户缓冲区的一个结构体指针
typedef struct msgbuf{
long mtype; //消息类型
char mtext[MAX_SIZE]; //消息文本
}msgbuf;
//创建或者打开消息队列的函数
int msg_init(key_t msgkey){
int msgid; //消息队列描述符
msgid=msgget(msgkey,IPC_CREAT|0660);
//如果创建消息队列失败
if(msgid==-1)
{
perror("msgget()");
exit(1);
}
return msgid;
}
//删除消息队列
void msg_rmv(int msgid){
int result;
result=msgctl(msgid,IPC_RMID,NULL); //消除消息队列描述符为msgid的队列
//如果消除失败
if(result==-1)
{
perror("msgctl()");
exit(1);
}
}
//接收指定类型的消息
int read_message(int msgid,long type,msgbuf *buf)
{
int result,length;
length=sizeof(msgbuf)-sizeof(long); //接收的消息长度
result=msgrcv(msgid,buf,length,type,0);
//如果接收失败
if(result==-1)
{
perror("msgrcv()");
exit(1);
}
return result;
}
//向指定的消息队列发送消息
int send_message(int msgid,msgbuf *buf)
{
int result,length;
length=sizeof(msgbuf)-sizeof(long); //发送的消息的长度
result=msgsnd(msgid,buf,length,0);
//如果发送失败
if(result==-1)
{
perror("msgsnd()");
exit(1);
}
return result;
}