操作系统——进程高级通讯

智能2112杨阳

一、目的

1、了解进程的创建,进程检查;

2、了解与进程有关函数的实现。如:fork(),exit(),wait(),getpid(),getppid()等

3、了解在多用户环境下进程之间的通信机制。

4、掌握用系统管道实现进程之间的信息传递的方法。

5、掌握用共享内存及消息队列实现进程之间的信息传递。

二、要求

  1. 掌握在Linux操作系统下,用管道方式实现进程之间通信各个系统调用函数的使用、参数设置等。
  2. 掌握在Linux操作系统下,用共享内存方式及消息队列方式实现进程之间通信各个系统调用函数的使用、参数设置等。

三、准备

  1. 管道函数:

      #include “unistd.h”

      int pipe(int p[2])          返回:成功时为0,出错时为-1。

      该文件返回两个文件描述字:p[0]—打开读,p[1]—打开写.管道的读写和文件的读写一样。例:

操作系统——进程高级通讯_第1张图片操作系统——进程高级通讯_第2张图片

操作系统——进程高级通讯_第3张图片

  1. 共享内存系统调用函数:

以下函数的原形在:sys/types.h 、sys/ipc.h和sys/shm.h包含文件中。

(1)创建/获取内存区:

int shmget(key_t key,int size,int shmflag);

key—关键字,必须是本地唯一的。

size—共享内存大小(字节),内核会把这个数值向上舍入取最近的虚拟内存帧的大小。

shnflg—标志参数,指定选项及其权限位。

           IPC_CREAT—创建新的信号量集

           IPC_EXCEL—如果信号量集已经存在,则返回错误。

           -- 和文件、目录一样权限。

返回一个共享内存ID--shmid

语法格式:shmid=shmget(key,size,flag)

(2)连接共享内存:

    把内存变量和共享内存连接起来,便于使用共享内存。

char *shmat(int shmid,char *shmaddr,int shmflag);

shmid—由shmget函数产生的共享内存ID;

shmaddr—字符型指针变量;

shmflg—共享内存标志参数,含义同上。

语法格式:virtaddr=shmat(id,addr,flag)

其中:id是共享存储区的标识符,addr是用户要使用共享存储区附接的虚地址,若addr是0,系统选择一个适当的地址来附接该共享区。flag规定对此区的读写权限,以及系统是否应对用户规定的地址做舍入操作。如果flag中设置了shm_rnd即表示操作系统在必要时舍去这个地址。如果设置了shm_rdonly,既表示只允许读操作。viraddr是附接的虚地址。

(3)分离共享内存

把内存指针变量和共享内存分离。

int shmdt(char *shmaddr);

shmaddr—内存指针变量;

其中,当调用成功时,返回0值,调用不成功,返回-1,addr是系统调用shmat所返回的地址。

(4)释放共享内存:

int shmctl(int shmid,int cmd,struct shmid_ds *buf);

其中:调用成功时返回0,否则返回-1。shmid为被共享存储区的标识符。cmd规定操作的类型。规定如下:

  IPC_STAT: 返回包含在制定的shmid相关数据结构中的状态信息,并且把它放置在用户存储区中的*buf指针所指的数据结构中。执行此命令的进程必须有读取允许权。

  IPC_SET:  对于指定的shmid,为它设置有效用户和小组标识和操作存取权。

  IPC_RMID  删除制定的shmid以及与它相关的共享存储区的数据结构。

  SHM_LOCK: 在内存中锁定指定的共享存储区,必须是超级用户才可以进行此操作。

  BUF 是一个用户级数据结构地址。

 范例:

操作系统——进程高级通讯_第4张图片

操作系统——进程高级通讯_第5张图片

  1. 消息队列系统调用函数:

以下函数的原形在:sys/types.h 、sys/ipc.h和sys/msg..h包含文件中。

(1) msgget函数用于创建一个新的消息队列或访问一个已存在的消息队列

int msgget(key_t key,int msgflag);

key:指定为常值IPC_PRIVATE或系统中唯一的关键字值。

msgflag:  IPC_CREAT—创建消息队列

          IPC_EXCL—检查消息队列是否存在

          <权限>。

返回:成功时为非负标识符,出错时为一1。返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的。

(2)使用msgget打开一个消息队列后,我们使用msgsnd往其上放置一个消息。

Int msgsnd(int msgid,struct msgbuf *msgf,size_t nbytes,int flag);

其中:msgid—由msgget()创建函数返回。

      *msgf—是一个结构指针,该结构具有如下的模板,

      struct msgbuf{

         long mtype;/*消息的类型*/

         char mtext[1];/*消息的数据*/

                    };

        在实际应用中,1个字节的消息是没有任何用途的,因此,用户可以根据自己的需要定义消息结构的内容。但:

        第一成员必须是long mtype;   必须进行强制类型转换: (struct msgbuf *)

               nbytes—实际消息块的大小。这是位于长整数消息类型之后的用户自定义数据的长度sizeof(message)-sizeof(long)。

               flag—选项标志位:可以是0,也可以是IPC-NOWAIT

0—  不使用标志,

                     IPC-NOWAIT—表示当消息队列满时不等待,返回错误信息。

(3)使用msgrcv函数从某个消息队列中读出一个消息。

int msgrcv(int msgid,struct msgbuf *msgp,size_t nbytes,long type,int flag);

           msgid—消息队列编号;

           msgp—存放消息的内容;

           nbyte—为信息数据的长度,即mtext参数的长度。

           type—接受消息的类型;type =0返回队列内第一项信息;type>0返回队列内第一项与mtype相同的信息type<0返回队列内第一项mtype小于或等于mtype绝对值的信息。

           flag—接受选项标志;0或IPC-NOWAIT

               IPC-NOWAIT—表示没有消息不等待,返回错误

               0—等待消息到来,进程挂起。

(4)删除消息队列:

int msgctl(int msgid,int cmd,struct msgid_ds *buf);

msgctl()提供了几种方式来控制信息队列的运作。参数msgid为欲处理的信息队列识别代码,参数cmd为欲控制的操作,有下列几种数值:

IPC__STAT    把信息队列的msqid—ds结构数据复制到参数buf.

IPC__SET    将参数buf所指的msqid—ds结构中的msqid.uid、msg_perm.gtd、msg_perm.mode和msg_qbytes参数复制到信息队列的msqid—ds结构内。

IPC__RMID    删除信息队列和其数据结构。

范例:

操作系统——进程高级通讯_第6张图片

操作系统——进程高级通讯_第7张图片

操作系统——进程高级通讯_第8张图片

四、要求

  1. 编写一段程序,实现进程的管道通信。

      使用系统调用pipe()建立一条管道线,两个子进程P1和P2分别向管道各写一句话:

     

      child 1 is sending a message!

      child 2 is sending a message!

代码

#include

#include

#include

#include

int main(){

 int fd[3],pid1,pid2;

 char OutPipe[100],InPipe[100];

 pipe(fd);

 while((pid1=fork())==-1);

 if(pid1==0){

  printf("p1\n");

  lockf(fd[1],1,0);

  sprintf(OutPipe,"child1 is sending a message!");

  write(fd[1],OutPipe,50);

  sleep(1);

  lockf(fd[1],0,0);

  exit(0);

 }else{

  while((pid2=fork())==-1);

  if(pid2==0){

   printf("p2\n");

   lockf(fd[1],1,0);

   sprintf(OutPipe,"child2 is sending a message!");

   write(fd[1],OutPipe,50);

   sleep(1);

   lockf(fd[1],0,0);

   exit(0);

  }else{

   printf("parent\n");

   wait(0);

   read(fd[0],InPipe,50);

   printf("%s\n",InPipe);

   wait(0);

   read(fd[0],InPipe,50);

   printf("%s\n",InPipe);

   exit(0);

  }

 }

 return 0;

}

消息队列

通过一个进程来发送数据

#include

#include

#include

#include

#include

#include

typedef struct data

{

    long type;

    char text[128];

}msgstruct;

int main(int argc,char * argv[])

{

    if(argc<3)

    {

        printf("error\n");

        exit(0);

    }

    msgstruct val;

    memset(&val,0,sizeof(val));

    sscanf(argv[1],"%d",&val.type);

    strcpy(val.text,argv[2]);

    int msgid=msgget((key_t)1234,IPC_CREAT | 0644);

    assert(msgid != -1);

    msgsnd(msgid,&val,strlen(val.text),0);

    msgctl((key_t)1234,IPC_RMID,NULL);

    exit(0);

}

通过一个进程来接收数据

#include

#include

#include

#include

#include

#include

typedef struct data

{

    long type;

    char text[128];

}msgstruct;

int main(int argc,char * argv[])

{

    if(argc<2)

    {

        printf("error\n");

        exit(0);

    }

    long type = 0;

    sscanf(argv[1],"%d",&type);

    msgstruct val;

    memset(&val,0,sizeof(val));

    int msgid=msgget((key_t)1234,IPC_CREAT | 0644);

    assert(msgid != -1);

    printf("type:%d\n",val.type);

    printf("data:%s\n",val.text);

    exit(0);

}

操作系统——进程高级通讯_第9张图片

      而父进程则从管道中读出来自两个子进程的信息,并显示子进程号,显示在屏幕上。 

  1. 编写两个程序,分别通过共享内存和消息队列实现两个进程的信息交换。

共享内存

代码

msg_service.c

#include

#include

#include

#include

#include

#include

#include

struct msgbuf // 消息队列结构体

{

       long mtype;

       char mtext[128];

       char ID[4];

};

int main()

{

       struct msgbuf sendbuf, readbuf;

       int msgId;

       key_t key;

       int readret;

       pid_t pid;

       key = ftok("a.c", 1);                          // 获取 key 值

       msgId = msgget(key, IPC_CREAT | 0755); // 创建消息队列

       if (msgId == -1)

       {

              printf("create message queue failed!~\n");

              perror("msgget");

              return -1;

       }

       printf("create msgage queue succeeded!~ msgId = %d\n", msgId);

       system("ipcs -q"); // 显示当前消息队列信息

       // init msgbuf

       sendbuf.mtype = 100;

       pid = fork();

       // parent process write 100

       if (pid > 0)

       {

              while (1)

              {

                     memset(sendbuf.mtext, 0, 128);

                     printf("please input to message queue:\n");

                     fgets(sendbuf.mtext, 128, stdin);

                     // send message to message queue

                     msgsnd(msgId, (void *)&sendbuf, strlen(sendbuf.mtext), 0);

              }

       }

       // child process read 200

       if (pid == 0)

       {

              while (1)

              {

                     memset(readbuf.mtext, 0, 128);

                     msgrcv(msgId, (void *)&readbuf, 128, 200, 0);

                     printf("datas from client: %s\n", readbuf.mtext);

              }

       }

       return 0;

}

msg_service.c

#include

#include

#include

#include

#include

#include

#include

struct msgbuf // 消息队列结构体

{

       long mtype;

       char mtext[128];

       char ID[4];

};

int main()

{

       struct msgbuf sendbuf, readbuf;

       int msgId;

       key_t key;

       int readret;

       pid_t pid;

       key = ftok("a.c", 1);                          // 获取 key 值

       msgId = msgget(key, IPC_CREAT | 0755); // 创建消息队列

       if (msgId == -1)

       {

              printf("create message queue failed!~\n");

              perror("msgget");

              return -1;

       }

       printf("create msgage queue succeeded!~ msgId = %d\n", msgId);

       system("ipcs -q"); // 显示当前消息队列信息

       // init msgbuf

       sendbuf.mtype = 100;

       pid = fork();

       // parent process write 100

       if (pid > 0)

       {

              while (1)

              {

                     memset(sendbuf.mtext, 0, 128);

                     printf("please input to message queue:\n");

                     fgets(sendbuf.mtext, 128, stdin);

                     // send message to message queue

                     msgsnd(msgId, (void *)&sendbuf, strlen(sendbuf.mtext), 0);

              }

       }

       // child process read 200

       if (pid == 0)

       {

              while (1)

              {

                     memset(readbuf.mtext, 0, 128);

                     msgrcv(msgId, (void *)&readbuf, 128, 200, 0);

                     printf("datas from client: %s\n", readbuf.mtext);

              }

       }

       return 0;

}

操作系统——进程高级通讯_第10张图片

五、总结 

1必须掌握进程的创建,会创建两个子进程;

2要了解消息队列的创建机制,先定义key值,通过ftok获取键值,本实验直接给出了key,然后创建消息队列,发送接收消息等一些列操作;

3同样要知道共享内存的工作机制,创建一块共享内存区,用shmat函数将共享内存段映射到调用进程的数据段中,想共享内存里写入读出数据;

4要有明确的逻辑思路,先创建两个进程,分别在进程中进行操作;

总的来说收获满满。

你可能感兴趣的:(java,apache,前端)