zoukankan      html  css  js  c++  java
  • FZU操作系统课程实验 实验一

    实验1

    【实验名称】:并发程序设计(实验1

    【实验目的】:掌握在程序中创建新进程的方法, 观察并理解多道程序并发运行的现象。

    【实验原理】:fork():建立子进程。子进程得到父进程地址空间的一个复制。

    返回值:成功时,该函数被调用一次,但返回两次,fork()对子进程返回0,对父进程返回子进程标识符(非0值)。不成功时对父进程返回-1,没有子进程。

    【实验内容】:首先分析一下程序执行时其输出结果有哪几种可能性,然后实际调试该程序观察事实上际输出情况,比較两者的差异,分析当中的原因。

    void main (void)

    {                    

                           int   x=5;

                           if( fork( ) )

                           {

                              x+=30;

                              printf (%d ,x);

                           }

                          else 

                            printf(%d ,x);

                         printf((%d ,x);

    }

    【实验要求】:每一个同学必须独立完毕本实验、提交实验报告、源程序和可运行程序。实验报告中必须包括估计的实验结果,关键代码的分析,调试记录,实际的实验结果,实验结果分析等内容。

    一.关键代码分析

    一个现有进程能够调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一差别是子进程中返回0值而父进程中返回子进程ID

    子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间副本,这意味着父子进程间不共享这些存储空间。

    也就是说,当程序执行到if条件推断执行fork()函数后,父进程执行的是条件成立成立的代码,即

    x+=30;

    printf (%d ,x);

    子进程执行的是条件不成立的代码,即

    printf(%d ,x);

    依据并发运行的工作方式及特征,我们估计可能的实验结果例如以下:

     

     

    二.估计实验结果

    推測有6种情况(事实上就是这4个数的全排列)

    35

    35

    35

    5

    5

    5

    35

    5

    5

    5

    35

    35

    5

    35

    5

    35

    5

    35

    5

    5

    35

    35

    35

    5

    三.调试记录

    1. 先建立一个.c为后缀的文档,如test.c

    2. 编辑源程序:

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {  

                           int   x=5;

                           if( fork( ) )

                           {

                              x+=30;

                              printf (%d ,x);

                           }

                          else 

                            printf(%d ,x);

                         printf((%d ,x);

    }

    3. 在命令行下编辑 gcc –o test.out test.c

    然后编辑       ./test.out

    执行程序

    4. 实际实验结果:

    35

    35

    (一串字符命令,省略)5

    5

    PS:那段字符命令,父进程执行完就会出现,所以都是在输出两个35后出现)

    反复步骤3N次后发现还是这个结果。

    四.实验结果分析

    老师说其他结果的出现是小概率事件。我们不敢苟同,这都基本上能够当不可能事件处理了。

    于是我们推測,这个跟传说中的时间片有关。

    由于这个程序执行所需的时间实在是太短,默认先执行的父进程全然能够在系统给定的时间片内执行完,假设没有出现什么其它情况而挂起,子进程就仅仅能等到父进程执行完后才干执行。

    所以呢,当然就仅仅有一种情况了。

    五.实验结果处理

    只是,我们能够利用时间片的特性制造出6种结果。

    主角登场了,sleep(1) 延时一秒函数

    能够把延时函数理解为一段要执行1秒的代码。以下的提到的进程,都是因在这段延时代码中执行时进程时间片用完挂起(如果在这个延时代码中执行了0.6秒),再次调度回来时,仅仅要再执行0.4秒就能够了跳出这个延时函数了。

    第一种:

    由于默认先执行的是父进程(我用的系统是这样),我们感觉对子进程太不公平了,所以我们制造的第一种情况的源代码是:

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {    int   x=5;

                           if( fork( ) )

                           {

                               x+=30;

                               sleep(1);

                              printf (%d ,x);

                           }

                          else 

                            printf(%d ,x);

                         printf((%d ,x);

    }

    我们推測时间片是0.6秒,无论对不正确,重点是分析。

    父进程执行0.6秒后,时间片用完挂起,调度执行子进程,子进程执行完后调度回父进程接着执行。所以我们推測结果是

    5

    5

    35

    35

    (一串字符命令,省略)

    哇塞,执行完也是!

    另外一种:

    这里我们突然想到假设在子进程前也加个延时函数会如何,

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {    int   x=5;

                           if( fork( ) )

                           {

                              x+=30;

                              sleep(1);

                              printf (%d ,x);

                           }

                          else {

                               sleep(1);

                              printf(%d ,x);

    }

                         printf((%d ,x);

    }

    只是,这个仅仅能调试完再依据答案去分析,由于我们也不知道真正的时间片是多少,仅仅是推測。执行结果为

    35

    35

    (一串字符命令,省略)5

    5

    我们继续如果时间片为0.6秒,父进程执行0.6秒后挂起,轮到子进程执行0.6秒后也挂起,调度回父进程继续执行,由于之前已经执行了0.6秒,所以这次调度时间片就够用了。如果成立

    所以,下面都是如果时间片为0.6秒,这个时间是非常合理的。

    第三种:

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {  

                            int   x=5;

                           if( fork( ) )

                           {

                               x+=30;

                               sleep(1);

                              printf (%d ,x);

                           }

                          else {

                              printf(%d ,x);

                              sleep(1);

    }

                         printf((%d ,x);

    }

    与前面的分析类似,父进程执行0.6秒后挂起,调度子进程会先执行第一条指令输出x5,然后进入延时函数执行0.6秒(前面执行1条指令的时间能够忽略不计)后挂起,调度回父进程执行完,再调度回子进程。所以输出为

    5

    35

    35

    (一串字符应该是提示输入的命令,省略)5

    第四种:

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {    int   x=5;

                           if( fork( ) )

                           {

                              x+=30;

                              printf (%d ,x);

                              sleep(1);

                           }

                          else {

                              printf(%d ,x);

                              sleep(1);

    }

                         printf((%d ,x);

    }

    分析同上,输出结果为

    35

    5

    35

    (一串字符命令,省略)5

    第五种:

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {    int   x=5;

                           if( fork( ) )

                           {

                              x+=30;

                              printf (%d ,x);

                              sleep(1);

                           }

                          else {

                              printf(%d ,x);

    }

                         printf((%d ,x);

    }

    执行结果为,

    35

    5

    5

    35

    (一串字符命令,省略)

    第六种:

    细致看下上面5种情况,会发现还有种比較奇葩的没有出现。

    5

    35

    5

    35

    (一串字符命令,省略)

    须要从父进程(延时挂起)->子进程(输出然后延时挂起)->父进程(延时然后输出然后再延时挂起)->子进程(输出结束进程)->父进程(延时然后输出结束进程),代码为

    #include<stdio.h>

    #include<unistd.h>                 

    void main (void)

    {    int   x=5;

                           if( fork( ) )

                           {

                              x+=30;

                              sleep(1);

                              printf (%d ,x);

                              sleep(1);

                           }

                          else {

                              printf(%d ,x);

                              sleep(1);

    }

                         printf((%d ,x);

    }

  • 相关阅读:
    R语言从基础入门到高级
    Web前端工程师职业学习路线图,分享!
    IOS中nil/Nil/NULL的区别
    Core Animation系列之CADisplayLink
    CADisplayLink 及定时器的使用
    iOS定时器NSTimer的使用方法
    IOS中定时器NSTimer的开启与关闭
    【IOS基础知识】NSTimer定时器使用
    IOS 实现自定义的导航栏背景以及自定义颜色的状态栏(支持7.0以及低版本)
    iOS7中计算UILabel中字符串的高度
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4466753.html
Copyright © 2011-2022 走看看