操作系统课程设计

 

 

 

 

进程间通讯

 :本课程设计通过消息队列,来模拟完成一个简单的客户端与服务器之间的通讯(交互)。有助于深入理解Linux环境中各进程间通讯的机制和步骤。

 

关键词: 消息队列 客户端/服务器  进程间通讯  shell命令

 

进程间通讯是操作系统的一个基本功能。理解好这个功能的原理可以帮助我们更好的理解操作系统原理。本文论述了如何模拟实现一个 Linux文件系统,主要完成对文件与目录进行一些基本的操作功能。本课程设计通过消息队列,来模拟完成一个简单的客户端与服务器之间的通讯(交互)。本课程设计有助于深入理解Linux环境中各进程间通讯的机制和步骤。功能如下:客户端接收用户输入的shell命令,发送给服务器端,服务器接收到命令后执行,并将执行结果返回给客户端,客户端显示输出结果。

 

 

2       系统总体框架设计

  

1)程序设计思路:

A.为了便于操作和观察结果,编制了2个程序和1个头文件(功能模块)。2个程序分别用于客户端(用户请求)和服务器端(请求处理),头文件包含了通信过程中需要用到的函数。

 

B.在server程序中建立一个keyMSGKEY1的客户端消息队列和一个keyMSGKEY2的服务器端消息队列,当接收到来自客户端消息队列的消息(shell命令)后,将消息经过处理(将shell命令执行后的结果重定向)的结果链接到服务器端消息队列中。

 

C.在client程序中使用客户端消息队列(keyMSGKEY1,将用户输入的shell命令作为消息链接到客户端信息队列,经过服务器的处理,在从服务器端消息队列取出处理结果并打印出来。

 

D.调试时将俩个程序分别编辑、编译,生成clientserver的可执行程序。分别在俩个终端执行这俩个程序。

 

 

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;

}

 

²        撤销队列 msgid1msgid2

         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()

 

撤销队列 msgid1msgid2

 

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     //指定消息队列1key

#define MSGKEY2 (key_t)0x4000     //指定消息队列2key

#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;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值