操作系统A

李美蓉

目录

  • 1 操作系统引论
    • 1.1 计算机操作方式的演变
    • 1.2 什么是操作系统
    • 1.3 操作系统的结构
    • 1.4 操作系统的特性
  • 2 进程管理
    • 2.1 进程描述
    • 2.2 进程状态及转换
    • 2.3 进程控制
    • 2.4 进程的并发执行
    • 2.5 进程同步与互斥
    • 2.6 信号量机制及应用
    • 2.7 生产者消费者问题
    • 2.8 读者写者问题
    • 2.9 哲学家就餐问题
    • 2.10 进程通信
    • 2.11 消息缓冲区队列机制
  • 3 线程
    • 3.1 什么是线程
    • 3.2 线程实现
  • 4 处理机调度
    • 4.1 处理机调度概述
    • 4.2 作业调度
    • 4.3 进程调度
    • 4.4 实时调度
  • 5 死锁
    • 5.1 死锁概念及资源分配图
    • 5.2 死锁的必要条件及预防
    • 5.3 死锁避免
    • 5.4 死锁检测和解除
  • 6 存储器管理
    • 6.1 存取器概述及连续分配方式(一)
    • 6.2 动态分区分配及可重定位分区分配
    • 6.3 分页存储管理
    • 6.4 页表结构
    • 6.5 分段及段页式存储管理方式
  • 7 虚拟存储器管理
    • 7.1 虚拟存储技术
    • 7.2 请求分页存储管理
    • 7.3 页面置换算法
    • 7.4 抖动与工作集
    • 7.5 请求分段存储管理
  • 8 磁盘存储器系统
    • 8.1 磁盘存储器的结构
    • 8.2 磁盘调度
    • 8.3 廉价磁盘冗余阵列(RAID)
    • 8.4 提高磁盘I/O的其他方法
    • 8.5 磁盘可靠性技术
  • 9 输入输出系统
    • 9.1 I/O硬件系统
    • 9.2 I/O控制方式
    • 9.3 中断处理程序及设备驱动程序
    • 9.4 设备无关性软件
    • 9.5 缓冲管理
    • 9.6 用户层软件及Spooling
  • 10 文件系统
    • 10.1 文件及逻辑结构
    • 10.2 文件目录结构
    • 10.3 连续分配及链接分配
    • 10.4 索引分配
    • 10.5 空闲空间管理
  • 11 操作系统实验
    • 11.1 实验一  Linux基本命令
    • 11.2 实验二 进程管理
    • 11.3 实验三 进程间通信
    • 11.4 实验四 处理机调度
    • 11.5 实验五 存储管理
    • 11.6 实验六 文件管理
实验三 进程间通信

3.1实验目的

Linux系统的进程通信机构 (IPC) 允许在任意进程间大批量地交换数据。本实验的目的是使学生理解和熟悉Linux支持的消息通讯机制及管道通信机制的基本原理;理解进程对资源的竞争,掌握解决进程互斥的方法。

3.2 背景知识

阅读Linux系统的msg.c、sem.c和shm.c等源码文件,熟悉Linux的通信机制。

3.3 实验内容

1)编程实现进程的消息通信:消息的创建,发送和接收。

  使用系统调用msgget (), msgsnd (), msgrcv (), msgctl () 编制一长度为1k的消息的发送和接收程序

  ②观察程序,说明控制消息队列系统调用msgctl () 在此起什么作用

  (2)编程实现进程的管道通信。

 使用系统调用pipe()建立一条管道,两个子进程p1P2分别向管道各写一句话,而父进程则从管道中读出来自于两个子进程的信息并显示在屏幕上。

 观察程序中的sleep(1)起什么作用?

 子进程1和2为什么也能对管道进行操作?

 (3比较上述(1),(2)两种通信机制的不同特点

3.4 参考程序

3.4.1 进程消息通信

     使用系统调用msgget( ), msgsnd( ), msgrcv( )msgctl()编制一长度为1K的消息发送和接收的程序 

〈程序设计〉

1)为了便于操作和观察结果,用一个 程序为“引子”,先后fork( )两个子进程,SERVERCLIENT,进行通信。

2SERVER端建立一个Key75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER SERVER每接收到一个消息后显示一句(server)received”。

3CLIENT端使用Key75的消息队列,先后发送类型从101的消息,然后退出。最后的一个消息,既是 SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句(client)sent”。

4)父进程在 SERVER CLIENT均退出后结束。

参考程序:

    #include <stdio.h>

#include <sys/types.h>

#include <sys/msg.h>

#include <sys/ipc.h>

#define MSGKEY 75          /*定义关键词MEGKEY*/

struct msgform                /*消息结构*/

{

       long mtype;

       char mtxt[1030];         /*文本长度*/

}msg;

int msgqid,i;

 

void CLIENT( )

{

       int i;

       msgqid=msgget(MSGKEY,0777);

       for(i=10;i>=1;i--)

       {

               msg.mtype=i;

               printf("(client)sent\n");

              msgsnd(msgqid,&msg,1024,0);       /*发送消息msgmsgid消息队列*/

       }

       exit(0);

}

 

void SERVER( )

{

  msgqid=msgget(MSGKEY,0777|IPC_CREAT); /*由关键字获得消息队列*/

  do

  {

         msgrcv(msgqid,&msg,1030,0,0);  /*从队列msgid接受消息msg*/

         printf("(server)receive\n");

  }while(msg.mtype!=1);             /*消息类型为1时,释放队列*/

   msgctl(msgqid, IPC_RMID,0);

   exit(0);

}

 

main()

{

  if(fork()) SERVER();

       else CLIENT( );

      wait(0);

       wait(0);

}

<结果>

从理想的结果来说,应当是每当Client发送一个消息后,server接收该消息,Client再发送下一条。也就是说(Client)sent”和“(server)received”的字样应该在屏幕上交替出现。实际的结果大多是,先由 Client 发送两条消息,然后Server接收一条消息。此后ClientServer交替发送和接收消息.最后一次接收两条消息. Client Server 分别发送和接收了。

10条消息,与预期设想一致

 <分析>

message的传送和控制并不保证完全同步,当一个程序不再激活状态的时候,它完全可能继续睡眠,造成上面现象,在多次send message 后才 receive message.这一点有助于理解消息转送的实现机理。

消息通信的特点:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息的传递,自身就带有同步的控制.当等到消息的时候,进程进入睡眠状态,不再消耗CPU资源。

3.4.2 进程的管道通信

编制一段程序,实现进程的管道通信。使用系统调用pipe()建立一条管道先。两个子进程p1和分别向管道各写一句话:

   Child 1  process  is  sending  a message!

   Child 2  process  is  sending  a message!

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

   #include  <unistd.h>

   #include <stdio.h>

   #include <signal.h>

   Int pid1,pid2;

   Main()

   {

     Int fd[3];

     Char  outpipe[100],inpipe[100];

     Pipe(fd);     /*创建一个管道*/

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

     If  (pid1= =0)

      {

        Printf”p1\n”);

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

        Sprintf(outpipe,”child 1 process is sending a message!”);

/*把串放入数组outpipe*/

        Write(fd[1],outpipe,50);  /*向管道写长为50字节的串*/

         Sleep(1);       /*自我阻塞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,”child 2 process 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);  /*从管道中读长为50字节的串*/

   Printf(“%s\n”,inpipe);

   Wait(0);

   Read(fd[0],inpipe,50);

  Printf(“%s\n”,inpipe);

  Exit(0);

}

}

}

运行结果:

child 1  process is sending a message!

child 2  process is sending a message!

相关知识:

(1)无名管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

(2)有名管道(named pipe):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

3)管道通信的特点:管道只能承载无格式字节流。

4)观察程序中的sleep(1)起什么作用?

答:延长子进程占用管道的时间,并没有让子进程1先输出而子进程2后输出的作用。

5)子进程12为什么也能对管道进行操作?

答:因为该pipe管道属于无名管道,调用pipe()的父进程及其子孙进程均能识别此文件描述符,能利用该文件(管道)进行通信。