精华内容
下载资源
问答
  • 2018-05-21 19:50:17

     

    供大家交流学习,最好自己动手做,这样才有最深切的体会。

     

    1.实验目的

    了解并掌握利用信号通信机制在父子进程间进行通信。

     

     

    2.实验软硬件环境

    • 安装Windows XP的计算机
    • VirtualBox软件,以及在其上安装的Ubuntu虚拟机

     

     

    3.实验内容

        在Linux下利用信号通信机制在父子进程间进行通信。由子进程发送事件信号,父进程获取事件信号后进行相应的处理,将结果输出到终端。

        使用函数:

    • void (*signal(int  signum,   void(* handler)(int)))(int)                                                            设置某一信号的对应动作。                                                                                                           第一个参数      signum指明了所要处理的信号,64种。它可以取除了SIGKILL和SIGSTOP外的任何一种信号。                                                                                                                     第二个参数      handler描述了与信号关联的动作(处理的方式)                                        第二个参数可以取以下三种值                                                                                                   (1)一个无返回值的函数地址    捕获     此函数必须在signal()被调用前声明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。     执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行handler中的函数。当函数执行结束后,控制权返回进程被中断的那一点继续执行。                                                                                                             (2)SIG_IGN   忽略     这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。SIGKILL,SIGSTOP不能被忽略,也不能被处理。   (3)SIG_DFL   默认     这个符号表示恢复系统对信号的默认处理。
       
    • int    kill (pid_t  pid,  int  sig)                                                                                                        用于向进程发送信号

     

     

     

    4.实验程序及分析

    实验程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<signal.h>
    #include<wait.h>
    
    void process()
    {
        puts("\n\tin process recived a signal");
        printf("\tpid here is %d\n",getpid());
        puts("\tprocess end\n");
        //输出运行process函数的进程pid,即接收信号的进程
    
    }
    
    int main()
    {
        signal(SIGUSR2,process);
        //设置信号种类为SIGUSR2,接收到该信号的进程要运行process函数
        int pid;
        pid = fork();
        //创建子进程
        if(pid == 0)
        {
            puts("\nin child");
            puts("deliver a signal to parent ...");
            kill(getppid(),SIGUSR2);
            //发送信号到父进程    
            printf("my pid is %d\nparent's pid is %d\n",getpid(),getppid());
            puts("child end\n");
        }
        else if(pid>0)
        {
            waitpid(pid,NULL,0);
            //等待子进程
            puts("\nin parent");
            printf("my pid is %d\n",getpid());
            puts("parent end\n");
        }
        else
        {
            puts("failed to create a child process\n");
        }
    
    return 0;
    }

     

    终端结果:

     

     

    分析:

    线程共享进程的地址空间,所以线程对资源的改变会反映到进程中,故i之前为3,进入线程后被改为2,在进程中输出为2.并且线程自己返回的tid与pthread_create()函数返回到l的值是一样的。

     

    5.实验截图

     

     

    6.实验心得体会

        此次实验signal()函数利用信号实现了父进程和子进程之间的通信,参照书上相关内容,成功地在Linux下完成了该次实验,让我对信号通信机制有了更深的了解。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    更多相关内容
  • 通过对进程间通信同步/互斥问题的编程实现,加深理解信号量和 P、V 操作的原理; 对 Windows 或 Linux 涉及的几种互斥、同步机制有更进一步的了解;熟悉 Windows 或 Linux 中定义的与互斥、同步有关的函数。
  • 3.(3)进程间信号通信 4.(4)进程的管道通信 编写程序,创建两个子进程。当此程序运行时,系统中有一个父进程和两个子进程。父进程在屏幕上显示“Parent”,子进程分别在屏幕上显示“Child1”和“Child2”。 ...
  • 苏州大学操作系统原理实验实验报告第二个实验。 使用内存共享的方式进行进程间通信,真正的实现通信,而非单个进程的伪通信。 使用MFC的窗口模式实现的。
  • Systemview操作环境的认识与操作-通信系统的仿真实验合集[摘要] 本文档的主要内容详细介绍的是通信系统的仿真实验合集包括了:Systemview操作环境的认识与操作,滤波器使用及参数设计,模拟线性调制系统仿真,DSB...
  • 实验报告详细描述了进程间通信的实现原理,使用管道以及信号实现进出那个间通信,附有源代码实现。
  • 程的创建实验、进程的控制实验、进程互斥实验、守护进程实验、 信号机制实验、进程的管道通信实验、消息的发送与接收实验、 共享存储区通信
  • 要求在该程序中还要使用进程的睡眠、进程图象改换、父进程等待子进程终止、信号的设置与传送(包括信号处理程序)、子进程的终止等有关进程的系统调用。 分别利用UNIX的消息通信机制、共享内存机制(用信号灯实施...
  • 实验六Linux进程间通信24课时 实验目的 理解进程通信原理掌握进程中信号量共享内存消息队列相关的函数的使用 实验原理 Linux下进程通信相关函数除上次实验所用的几个还有 信号信号量又称为信号灯它是用来协调不同...
  • 操作系统实验,理解进程之间如何通信。主要讲解了如何创建进程、进程间如何通信信号量的一些知识。
  • Linux——进程通信 一、实验目的 (1) 熟悉并掌握管道机制,并实现进程间通信 (2) 熟悉并掌握共享内存机制,并实现进程间通信 二、实验内容 任务一: (1)阅读以上父子进程利用管道进行通信的例子(例1),写...

    理论知识

    Linux——Linux C语言编程基础知识

    Linux——进程通信

    一、实验目的

    (1) 熟悉并掌握管道机制,并实现进程间通信

    (2) 熟悉并掌握共享内存机制,并实现进程间通信

    二、实验内容

    任务一:

    (1)阅读以上父子进程利用管道进行通信的例子(例1),写出程序的运行结果并分析。

    (2)编写程序:父进程利用管道将一字符串交给子进程处理。子进程读字符串,将里面的字符反向后再交给父进程,父进程最后读取并打印反向的字符串。

    任务二:

    (1)阅读例2的程序,运行一次该程序,然后用ipcs命令查看系统中共享存储区的情况,再次执行该程序,再用ipcs命令查看系统中共享内存的情况,对两次的结果进行比较,并分析原因。最后用ipcrm命令删除自己建立的共享存储区。

    1、ipcs命令的作用:用于查看系统中共享存储区,消息队列和信号量的情况。如下图:

    [x02620101@localhost x02620101]$ ipcs
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes     nattch  status
    0x0000000f 262145     x02620101 644        1000       0
    0x00000000 294914     x02620101 644        20         0
    
    ------ Semaphore Arrays --------
    key        semid      owner      perms      nsems
    
    ------ Message Queues --------
    key        msqid      owner    perms      used-bytes   messages
    

    2、ipcrm命令的作用:用于删除系统中存在的共享存储区,消息队列等。如:

    ipcrm  -M key 表示根据关键字删除共享存储区

    ipcrm -m id表示根据标识符删除共享存储区

    ipcrm -Q key表示根据关键字删除消息队列

    ipcrm -q id表示根据标识符删除消息队列

    (2)每个同学登陆两个窗口,先在一个窗口中运行例3程序1(或者只登陆一个窗口,先在该窗口中以后台方式运行程序1),然后在另一个窗口中运行例3程序2,观察程序的运行结果并分析。运行结束后可以用ctrl+c结束程序1的运行。

    注:把&加在一个命令的最后,可以把这个命令放到后台执行 ,如gftp &,

    (3)编写程序:使用系统调用shmget(),shmat(),shmdt(),shmctl(),编制程序。要求在父进程中生成一个30字节长的私有共享内存段。接下来,设置一个指向共享内存段的字符指针,将一串大写字母写入到该指针指向的存贮区。调用fork()生成子进程,让子进程显示共享内存段中的内容。接着,将大写字母改成小写,子进程修改共享内存中的内容。之后,子进程将脱接共享内存段并退出。父进程在睡眠5秒后,在此显示共享内存段中的内容(此时已经是小写字母)。

    注:

    需要包含头文件:

    #include <sys/wait.h>

    举例:

    sleep(1);   休眠一秒

    三、代码及运行结果分析

    任务一:

    (1)

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main()
    {
            int x,fd[2];
            char buf[30],s[30];
            pipe(fd);
            while ((x=fork())==-1);
            if (x==0)
            {
                    close(fd[0]);
                    printf("Child Process!\n");
                    strcpy(buf,"This is an example\n");
                    write(fd[1],buf,30);
                    exit(0);
                                               }
            else{
                    close(fd[1]);
                    printf("Parent Process!\n");
                    read(fd[0],s,30);
                    printf("%s\n",s);
            }
            return 0;
    }

     

    分析: 

    调用pipe(fd);创建一个管道后,接着调用fork()函数产生两个进程,首先开始执行子进程,关闭管道出口,通过管道入口向管道中写入内容。父进程中,管道入口关闭,通过管道出口端从管道中读取之前写入内容,最后输出出来。

    (2)

    #include<stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main(){
            int x,count,left,right,temp,fd[2],fe[2];
            char c,buf[30],s[30];
            pipe(fd);
            pipe(fe);
            printf("please input a line of char:\n");
            scanf("%s",buf);
            while((x=fork())==-1);
            if(x==0){
                    close(fd[0]);
                    close(fe[1]);
                    printf("Child Process!\n");
                    write(fd[1],buf,30);
                    read(fe[0],buf,30);
                    printf("%s\n",buf);
                    exit(0);
            }else{
                    close(fd[1]);
                    close(fe[0]);
                    count=0;
                    do{
                            read(fd[0],&c,1);
                            s[count++]=c;
                    }while(c!='\0');
                    printf("Parent Process!\n");
                    printf("%s\n",s);count-=2;
                    for(left=0,right=count;left<=count/2;left++,right--){
                            temp=s[left];
                            s[left]=s[right];
                            s[right]=temp;
                    }
                    write(fe[1],s,30);
                    wait(0);
            }
    }

     

    任务二:

    (1)

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int main(){
            key_t key=200; /*在实际实验过程中,为了避免每个同学建立的共享存储区关键字一样而相互干扰,关键字请用学号末3位*/
            int shmid_1,shmid_2;
            if ((shmid_1=shmget(key,1000,0644|IPC_CREAT))==-1){
                    perror("shmget shmid_1");exit(1);
            }
            printf("First shared memory identifier is %d\n",shmid_1);
            if ((shmid_2=shmget(IPC_PRIVATE,20,0644))==-1){
                    perror("shmget shmid_2");exit(2);
            }
            printf("Second shared memory identifier is %d\n",shmid_2);
            exit(0);
            return 0;
    }

    分析: 

    成功,返回共享内存段的标识符,内核中用于唯一的标识一个对象。对存在于内核存贮空间中的每个共享内存段,内核均为其维护着一个数据结构shmid_ds。 失败,返回-1,设置errno。 

    ①第一个参数key(键值)用来创建IPC标识符,shmget()返回的标识符与key值一一对应,不同的key值返回不同的标识符。 

    ②第二个参数size,决定了共享内存段的大小(若访问已存在的内存段,该参数可设为0)。有最大字节数的限制 

    ③第三个参数shmflag,用于设置访问权限及标识创建条件。 

    对两次的结果进行比较:

    两次运行结束后的 第二个共享标识符是不一样的。在用ipcs查看时,共享内存段中的关键字,共享内存标识符,访问权限,字节等都是不一样的。

    (2)

    程序1:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define SHMKEY 200  /*在实际实验过程中,为了避免每个同学建立的共享存储区关键字一样而相互干扰,关键字请用学号末3位*/
    #define K  1024
    int shmid;
    int main (){
            int i,*pint;
            char *addr;
            //extern int shmat();
            extern cleanup();
            for(i=0;i<20;i++) signal(i,cleanup);
            shmid=shmget(SHMKEY,16*K,0777|IPC_CREAT); /*建立16K共享区SHMKEY */
            addr=shmat(shmid,0,0);/*挂接,并得到共享区首地址 */
            printf ("addr 0x%x\n",addr);
            pint=(int *)addr;
            for (i=0;i<256;i++) *pint++=i;
            pause();/*等待接收进程读 */
    }
    cleanup()
    {
            shmctl(shmid,IPC_RMID,0);
            exit(0);
    }

    程序2: 

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define SHMKEY 200  /*在实际实验过程中,为了避免每个同学建立的共享存储区关键字一样而相互干扰,关键字请用学号末3位*/
    #define K  1024
    int shmid;
    int main ()
    {
            int i,*pint;
            char *addr;
            //extern char * shmat ();
            shmid=shmget(SHMKEY,8*K,0777);/*取共享区SHMKEY的id */
            addr=shmat(shmid,0,0);/*连接共享区*/
            pint=(int *)addr;
            for (i=0;i<256;i++)
                    printf("%d\n",*pint++);/*打印共享区中的内容*/
    }

    分析: 

    首先系统通过调用shmctl对预定义的shmid指向的内存段进行删除操作,防止冲突,接着系统调用shmget创建一个16*1024字节的共享内存段,成功返回共享内存段的标识符给shmid,系统再次调用shmat连接内存段,返回该共享内存段连接到调用进程地址空间上的地址addr。

    程序1后台运行时,该程序开始执行,系统调用shmget创建一个8*1024字节的共享内存段,再通过调用shmat挂接内存段,由系统选择挂接地址,最终输出转换后的挂接地址。最后输出前255的内容。共享存储区机制只为通信进程提供了访问共享存储区的操作条件,而对通信的同步控制则要依靠信号量机制等才能完成。

    (3)

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define SHMKEY 200
    #define K 1024
    int shmid_1,shmid_2;
    int main ()
    {
            int x,y,i,*pint;
            char *addr_1,*addr_2;
            char words[26]={'A','B','C','D','E','F','G','H','I','J',
            'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','X'};
            shmid_1=shmget(SHMKEY,30*K,0777|IPC_CREAT); /*建立16K共享区SHMKEY */
            addr_1=shmat(shmid_1,0,0);/*挂接,并得到共享区首地址*/
            pint=(int *)addr_1;
            printf ("addr_1 0x%x\n",addr_1);
            for (i=0;i<26;i++) {
                    *pint=words[i];
                    pint++;
            }
            while((x=fork())==-1);
            if(x==0){
                    shmid_2=shmget(SHMKEY,30*K,0777|IPC_CREAT); /*建立16K共享区SHMKEY */
                    addr_2=shmat(shmid_2,0,0);/*挂接,并得到共享区首地址*/
                    pint=(int *)addr_2;
                    for(i=0;i<26;i++){
                            printf("%c ",*pint);
                            *pint=*pint+32;
                            pint++;
                            if(i==25)printf("\n");
                    }
                    y=shmdt(addr_2);
                    exit(0);
            }else{
                    sleep(5);
                    pint=(int *)addr_1;
                    for(i=0;i<26;i++){
                            printf("%c ",*pint);
                            pint++;
                            if(i==25)printf("\n");
                    }
            }
    }
    

    四、实验心得

    通过本次实验了解了管道进程间通信形式,掌握利用管道进行进程通信的程序设计,管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道; 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统。数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。基本达到了本次实验的要求。另外,对于共享内存通信的工作机制也有了一定的了解,掌握线程与进程在组成成分上的差别,以及与其相适应的通讯方式和应用目标。知道了操纵共享内存共有shmget()、shmat()、shmdt()、shmctl()4个系统调用。ipcs命令的作用:用于查看系统中共享存储区,消息队列和信号量的情况,ipcrm命令的作用:用于删除系统中存在的共享存储区,消息队列。也让我对管道及共享区的作用和用法,以及对操作系统中各进程之间的通信和协同作用等方面有了更深的了解。总之,本次实验自己收获了很多。

     

    参考文章

    实验三、进程通信(一) ——管道及共享内存

    实验三、 操作系统(OS)--进程通信(管道及共享内存)

    实验三 进程通信(一)

    linux c 休眠函数sleep usleep

    Linux后台进程管理以及ctrl+z(挂起)、ctrl+c(中断)、ctrl+\(退出)和ctrl+d(EOF)的区别

     

    展开全文
  • 2020 操作系统 实验二 进程通信

    千次阅读 2020-12-02 00:54:46
    实验二、进程通信 一、实验名称 进程通信 二、实验目的 掌握用邮箱方式进行进程通信的方法,并通过设计实现简单邮箱理解进程通信中的同步问题以及解决该问题的方法。 三、实验原理 邮箱机制类似于日常使用的信箱。...

    实验二、进程通信

    一、实验名称

    进程通信

    二、实验目的

    掌握用邮箱方式进行进程通信的方法,并通过设计实现简单邮箱理解进程通信中的同步问题以及解决该问题的方法。

    三、实验原理

    邮箱机制类似于日常使用的信箱。对于用户而言使用起来比较方便,用户只需使用send()向对方邮箱发邮件 receive()从自己邮箱取邮件, send()和 receive()的内部操作用户无需关心。因为邮箱在内存中实现,其空间有大小限制。其实send()和 receive()的内部实现主要还是要解决生产者与消费者问题。

    img

    四、实验内容

    1.背景知识介绍

    1.1 生产者消费者模型

    生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。生产者与消费者对缓冲区的访问是互斥关系。

    1.2 Linux 进程间通信之消息队列

    ​ Linux 中进程间的通信方法有很多:管道、命名管道、消息队列、共享内存、信号量。本次实验要求先使用系统调用来编写程序完成进程间通信。我这里采用的是“消息队列”的方式,简单介绍一下。

    ​ 消息队列就是一个消息的链表。可以把消息看做一个记录,具有特定的格式及特定的优

    先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读

    权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。

    ​ 特点是可以实现多个进程间的双向通信,缺点是传输的数据量小,仅可以传递一些控制信息可以比喻成公共邮箱,所有的进程向这个公共邮箱发送消息,或者从这个邮箱取某一类型的消息。取出的消息将从邮箱中消失。从中可以看出消息是有类型的(用一个长整型的数据表示类型),同时它也遵守某些格式(消息的内容是一个结构体)。也可以看出不用关心不同类型的消息的发送先后顺序,取出消息将会取出目标类型的最早的消息。

    1.3 Linux 中信号量的操作

    1.3.1 信号量的数据结构
    union semun{
        int val;
        struct semid_ds *buf;  //semid_ds 指针结构
        unsigned short *array;  //数组类型
        struct seminfo *__buf;  //信号量内部结构
    }
    
    1.3.2 创建信号量:semget

    函数原型:int semget(key_t key, int num_sems:, int sem_flags);

    作用:第一次使用时创建信号量,以后使用时获取信号量。

    Key:一个整型值对应内核中一个信号量对象,不同信号量的key值不一样。

    num_sems:信号量的数目

    sem_flags:设置一组标志,与open函数的标志非常相似,包括信号量的权限等。

    1.3.3 初始化、删除 :semctl

    函数原型:int semctl(int sem_id, int sem_num, int command, …);

    sem_id:信号量id。

    sem_num:信号量的下标,从0开始。

    command:具体的操作命令,有SETVAL(设置初值)、IPC_RMID(移除信号量),这个参数可以没有。

    1.3.4 信号量操作函数:semop

    函数原型:int semop(int sem_id, struct sembuf *sem_ops,size_t num_sem_ops);

    sem_id:信号量的id,用作标识。

    sem_ops:指向一个结构体数组的指针。结构体内容如下

    sembuf 结构在 <linux/sem.h> 中定义

    struct sembuf{
        ushort sem_num;  //信号量的编号
        short sem_op;    //信号量的操作
        short sem_flg;   //信号量的操作标志
    }
    

    sem_num 是信号量的下标,sem_op 是信号量一次操作总需要改变的数值,+1是 v 操作,-1 是 p 操作,sem_flg 通常设置为SEM_UNDO,表示操作系统会跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量,防止其他进程一直处于等待状态。
    num_sem_ops:指的是结构数组的元素个数。

    1.4 Linux 环境下使用 C 语言进行文件读写

    1.4.1 打开文件

    使用 <stdio.h> 头文件中的 fopen() 函数即可打开文件,它的用法为: **FILE *fopen(char filename, char mode);

    可选的模式有 r/w/a/r+/w+/a+,分别对应不同的文件访问权限。

    1.4.2 读、写文件

    将文件打开后,可使用:int fprintf ( FILE *fp, char * format, … ); 对文件进行写入操作。fp 为文件指针,format 为格式控制字符串,… 表示参数列表。fprintf() 返回成功写入的字符的个数,失败则返回负数。

    可使用 system("cat filename "); 进行打印文件中的所有内容

    1.4.3 文件流

    所有的文件(保存在磁盘)都要载入内存才能处理,所有的数据必须写入文件(磁盘)才不会丢失。数据在文件和内存之间传递的过程叫做文件流,类似水从一个地方流动到另一个地方。数据从文件复制到内存的过程叫做输入流,从内存保存到文件的过程叫做输出流。我们可以说,打开文件就是打开了一个流。

    2.设计方案

    2.1 思路分析

    首先说明一下思路:分析一下实验要求,是要模拟一个中间站储存信息,然后两个进程一个写一个读。因为不能使用系统提供的一些调用机制,所以我想了很久如何让两个进程共享同一个“信箱”。逛了很久的论坛,也看了 Linux 下 C 编程的一些书,但是大部分都是调用系统提供的一些机制,共享内存,消息队列等等。于是最后想出来采用的办法是使用文件机制,创建一个文件当做“信箱”,然后 A 进程写文件当做信箱的 “send”B 进程读文件当做信箱的 “receive”

    同时采用信号量的 PV 操作限制同时对同一个文件的操作,使得进程对文件操作是互斥的,避免进程 A 在写文件的时候,进程 B 在读文件。我设计的信箱中只能有一条信件,相当于是生成者消费者模型中的缓冲池。因为只能保存一封邮件的话比较好设计互斥操作,对生成者和消费者来说对信箱的访问就始终是互斥的了,同一时刻只能有一方对信箱进行读写。

    2.2 代码结构说明

    有三个代码文件,分别是自定义头文件 test2.h,发送方 send.c ,接收方 receive.c

    2.2.1 test2.h

    我将需要使用到的创建,销毁邮箱的方法,以及对信号量操作的方法都在这个头文件中声明,并实现。减少总的代码量,使代码更简洁一些。

    1. 定义信号量的数据结构

      // 定义信号量的数据结构
      union semun
      {
          int val;
      };
      
    2. 信号量初始化函数:void sem_init()

    3. P 操作函数:void sem_p()

    4. V 操作函数:void sem_v()

    5. 销毁信号量函数:void sem_destroy()

    6. 创建信箱函数:void createMainbox(char *filename)

    7. 撤销信箱函数:void removeMainbox(char *filename)

    2.2.2 send.c

    主要操作都在 test2.h 这个头文件中定义好了。send.c 的主要功能如下:

    1. 在最开始时创建邮箱
    2. 读入键盘输入
    3. 判断“信箱是否为空”,若为空,则将输入内容写入“信箱”。若“信箱“已满,则提示发送失败。
    4. 最后结束通信时,撤销信箱
    2.2.3 receive.c

    receive.c 的主要功能如下:

    1. 读入键盘输入
    2. 若输入为 “receive” 则判断信箱是否为空。
    3. 若为空则输出错误提示;若不为空则从信箱中取出一封邮件并输出,同时将信箱清空。

    3.预计实验结果

    1. A 进程首先创建信箱,应输出:创建信箱成功!

    2. A 进程往信箱发送一条消息:This is a msg 111

    3. 此时信箱收到这条消息,因为信箱中只能有一条消息的原因,此刻信箱已满。

      若此时 A 进程再发送消息:This is a msg 222,应该提示输出 “邮箱已满,发送失败”

    4. 再 A 进程发送完后,B 进程读取信箱,取出信箱中的信息。此时应输出 A 进程此前发出的第一条消息,即:This is a msg 111

    5. 此时取出这条消息后信息应为空,若 B 进程再次访问信箱,应输出:邮箱为空!

    6. A 进程发送一条信息:This is a msg 333

    7. B 进程访问信箱,此时应输出为:This is a msg 333;编号为 222 的消息因为邮箱已满,应该被直接丢弃。

    8. A 进程结束,此时撤销信箱,应输出:撤销信箱成功!

    4.关键代码分析

    4.1 test2.h

    4.1.1 创建信箱函数
    // 创建信箱函数,根据输入的文件名创建文件
    void createMainbox(char *filename)
    {
        // 0755 代表是对文件的操作权限
        if (creat(filename, 0755) < 0)
        {
            printf("create file %s failure!\n", filename);// 根据传入的文件名创建一个文件
            exit(EXIT_FAILURE);
        }
        else
        {
            printf("创建信箱成功!\n");
        }
    }
    
    4.1.2 撤销信箱函数
    void removeMainbox(char *filename)
    {
        if (remove(filename) == 0)// 删除成功返回 0 
        {
            printf("撤销信箱成功!\n ");
        }
        else
        {
            perror("remove\n");
        }
    }
    
    4.2 send.c 发送信件
    	FILE *fp; // 定义文件指针
        createMainbox("mailbox.txt"); // 创建一个名为 mailbox.txt 的文件作为信箱
        sem_init();// 初始化信号量
        printf("可在下方输入发送消息(输入 end 删除信箱并结束进程)\n");
        while (1)
        {
            printf("发送内容: ");
            char input[256];
            memset(input, 0, 256); 
            scanf("%[^\n]", input);// 这样 scanf 可以读取整行的字符串,包括空格
            getchar();
            if (strncmp(input, "end", 3) == 0) // 输入等于 "end" 时,调用信箱撤销函数
            {
                removeMainbox("mailbox.txt");
                exit(0);
            }
            // 用 r+ 方式打开信箱文件,可读可写,若文件不存在则创建;
            // 若文件存在,则在文件末尾追加写入
            if ((fp = fopen("mailbox.txt", "r+")) == NULL)
            {
                printf("fail to open 1!\n");
                break; /*出错退出*/
            }
            char ch = fgetc(fp);
            if (ch == EOF)// ch == EOF 表示信箱为空,可写
            {
                sem_p();// p 操作
                fprintf(fp, "%s", input);
                printf("发送成功!\n");
                sem_v(); //v操作,释放信号量
            }
            else // 反之则信箱已满,不可写入
                printf("信箱已满,发送失败\n");
            fclose(fp);// 关闭文件流
            usleep(1000); //本进程睡眠.
        }
        sem_destroy(); //把semid指定的信号集从系统中删除
    
    4.3 receive.c 接受信件
    FILE *fp;// 定义文件指针
        sem_init();// 初始化信号量
        while (1)
        {
            printf("输入 receive 从信箱中接受消息:");
            char input[256];
            memset(input, 0, 256);
            scanf("%s", input);
            if (strncmp(input, "receive", 7) == 0)// 输入为 receive 则判断信箱是否有内容
            {
                // 以 r+ 打开信箱
                if ((fp = fopen("mailbox.txt", "r+")) == NULL)
                {
                    printf("fail to open 1!\n");
                    break; /*出错退出*/
                }
                char ch = fgetc(fp);
                fclose(fp);// 关闭文件流
                // 判断是否为空
                if (ch == EOF)
                    printf("信箱为空!\n");
                else
                {
                    sem_p();// p 操作
                    system("cat mailbox.txt");// 显示邮箱文件中的全部内容
                    printf("\n");
                    // 定义文件描述符,打开文件
                    int fd = open("mailbox.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
                    ftruncate(fd, 0);// 清空文件
                    lseek(fd, 0, SEEK_SET);// 移动文件读写指针,移动到文件开头的位置
                    close(fd);
                    sem_v();// V 操作
                }
            }
            else
            {
                printf("输入有误\n");
            }
            usleep(1000);
        }
    

    5.调试记录

    1. 启动两个终端,一个执行 send.c,一个执行 receive.c,A 进程创建信箱

      image-20201201183506762

    2. A 进程先发送消息:This is a msg 111

      image-20201201183556666

    3. A 进程再发送第二条消息:This is a msg 222

      image-20201201183657260

    4. B 进程接收消息

      image-20201201183735611

    5. B 进程再次接收消息

      image-20201201183801706

    6. A 进程发送第三条消息:This is a msg 333

      image-20201201183908228

    7. B 进程接收消息

      image-20201201184011456

    8. A 进程撤销信箱

      image-20201201184039849

    6.实际的实验结果

    image-20201201184120938

    7.实验结果分析

    7.1 结果分析

    通过实际的实验结果与预计的实验结果比较,发现二者是一致的。

    1. 首先 A 进程创建信箱成功,返回了创建成功提示
    2. A 进程向信箱中发送 This is a msg 111 ,收到发送成功提示。此时在 B 进程读取信箱之前又发送了一条 This is a msg 222 ,此时返回了发送失败的提示,是因为我设计的信箱只能保存一条信息。此时上一条发送的 111 的消息还没被 B 进程接收,所以发送失败。
    3. B 进程读取信箱,接收信息。此时返回的是 A 进程最开始发的 111 的信息,这是正确的。再次读取信箱接收信息,返回邮箱为空的提示,是因为 B 进程每次接收一条信息之后,就会从信箱中取走这条信息,所以此时信箱是空的。
    4. A 进程再次发送一条信息 This is a msg 333,信箱为空,发送成功。此时 B 进程接收信息,接收到的是 333 的信息。因为 222 的信息再发送失败的时候就直接被丢弃了,并不会再次发送,所以当邮箱清空后接下来接收到的消息是 333。
    5. 最后 A 进程撤销信箱成功,返回了撤销成功的提示。

    7.2 与系统自带的“消息队列”机制的对比

    除了自己设计的信箱外,我还使用了系统自带的消息队列机制来编写程序,实现进程间的通信,从而做出对比。文件名为 msgqueue.c,下图是调用该程序通信的演示结果。

    image-20201201185744015

    从上图的程序运行结果可以发现,左边进程发送的三条消息 (1)msg111(2)msg222 (3)msg333 ,右边进程都正常的接收到了。

    不同点:
    1. 消息队列机制的数据结构(struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中找到访问入口。因此不存在只能保存一条信息的设定,每次发送都能被正确接受。而我设定的信箱是一个特定的文件,只能保存一条信息,而且如果发送出去的信息还没有被接收而这个文件被删除的话,那发送的消息就丢失了。
    2. 消息队列不需要采用信号量机制来控制同步与互斥的问题
    相同点:
    1. 都能实现进程间的通信。
    展开全文
  • 实验 4:进程间通信...了解IPC通信中的信号、管道、消息、共享存储区的通信原理和基本技术 掌握linux环境中构造这些通信机制的方法和步骤; 熟悉在实现通信中使用的系统调用和编程方式,以及这类程序的调试和技巧。
  • 为解决实时操作系统μC/OS-II串口通信设计中信号量、消息邮箱使用方法的问题,提出了一种以STM32V评估板为硬件平台和μC/OS-II的串口通信程序设计方案。该方案采用Cortex-M3架构的ARM处理器STM32F103VB作为主控制...
  • PAGE PAGE 1 西北师范大学计算机科学与工程学院学生实验报告 学 号 专业 计算机科学与技术 姓名 课程名称 操作系统实验 班级 2011级计师1班 实验名称 实验五 进程通信 课程类型 必修类 一实验目的 1了解Linux系统中...
  •  (1)为什么说系统调用signal()是建立进程与信号之间的联系,而不是接收信号操作?  (2)若子进程向父进程发送信号,父进程接到信号后可以缺省操作、或忽视信号、或执行一函数,各是什么含义?
  • 5、了解什么是信号,利用信号量机制熟悉进程间软中断通信的基本原理, 6、熟悉消息传送的机理 ,共享存储机制 。 二、 实验环境 Ubuntu 20.10,gcc编译器 三、 实验内容 编写一段程序,使用系统调用fork( )创建两...

    在这里插入图片描述

    一、 实验目的

    1、掌握进程的概念,明确进程的含义。
    2、认识并了解进程并发执行的实质,进程的阻塞与唤醒,终止与退出的过程。
    3、熟悉进程的睡眠、同步、撤消等进程控制方法。
    4、分析进程竞争资源的现象,学习解决进程互斥的方法 。
    5、了解什么是信号,利用信号量机制熟悉进程间软中断通信的基本原理,
    6、熟悉消息传送的机理 ,共享存储机制 。

    二、 实验环境

    Ubuntu 20.10,gcc编译器

    三、 实验内容

    1. 编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程并发执行,观察实验结果并分析原因。

    2. 用fork( )创建一个进程,再调用exec( ),用新的程序替换该子进程的内容,利用wait( )来控制进程执行顺序,掌握进程的睡眠、同步、撤消等进程控制方法,并根据实验结果分析原因。

    3. 编写一段多进程并发运行的程序,用lockf( )来给每一个进程加锁,以实现进程之间的互斥,观察并分析出现的现象及原因。

    4. 编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键);捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:
      Child process1 is killed by parent!
      Child process2 is killed by parent!
      父进程等待两个子进程终止后,输出如下的信息后终止:
      Parent process is killed!
      分析利用信号量机制中的软中断通信实现进程同步的机理。

    5. 使用系统调用msgget( ),msgsnd( ),msgrev( ),及msgctl( )编制一长度为1k的消息发送和接收的程序,并分析消息的创建、发送和接收机制及控制原理。

    6. 编制一长度为1k的共享存储区发送和接收的程序,并设计对该共享存储区进行互斥访问及进程同步的措施,必须保证实现正确的通信。

    四、 实验原理 实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),实验步骤

    实验原理:

    实验步骤:

    第一题:

    因为题目说创建2个子进程,而根据fork()函数的返回值可以发现,如果返回值==0表示当前是子进程,返回值>0说明当前是父进程,根据这一点就可以通过if-else语句将程序分开成两个不同的进程运行!
    在这里插入图片描述

    第二题:

    一开始采用跟第一题一样的创建进程的方法,只是在子进程中调用 execl()从而替代了后面的内容:
    在这里插入图片描述
    同时也在父进程中调用wait(),保证父进程不会提前结束:
    在这里插入图片描述

    第三题:

    先创建2个子进程,然后在2个子进程里面都先加锁,通过for循环输出10个数字,执行结束之后再解锁:
    在这里插入图片描述

    第四题:

    先创建2个子进程,然后在进程里面接收信号,后面需要用pause()函数来使程序暂停,直到接收到信号。接收到终止信号之后再向子进程发出信号。
    在这里插入图片描述
    子进程接收到信号后,终止程序的运行。
    在这里插入图片描述
    父进程最后调用wait()保证了父进程在最后结束。
    在这里插入图片描述

    第五题:

    首先在main函数中创建2个进程:
    父进程中调用server(),接收消息。
    子进程中调用client(),发送消息。
    在这里插入图片描述
    client()函数中发送消息:
    在这里插入图片描述
    server()函数中发送消息:
    先调用wait()函数等client发送完消息并结束进程再接收消息。
    在这里插入图片描述

    第六题:

    main()函数中创建1个子进程执行server,父进程执行client
    在这里插入图片描述
    client中发送消息
    在这里插入图片描述
    server中接收消息:
    在这里插入图片描述
    server一接收到消息都会把addr置为-1,以等待client再次发送消息。

    五、 实验结果分析(截屏的实验结果,与实验结果对应的实验分析)

    1、实验结果与实验程序、实验步骤、实验原理、操作系统原理的对应分析;
    2、不同条件下的实验结果反应的问题及原因;
    3、实验结果的算法时间、效率、鲁棒性等性能分析。

    第一题:

    在这里插入图片描述

    在CPU多核的情况下,通过实验结果可以发现,多次执行程序,“父进程”“子进程1”“子进程2”输出的顺序都不一样。说明3个进程的执行顺序都是不确定的,这取决于进程的调度时机。
    然后把CPU调到1核,重启,
    在这里插入图片描述
    在这里插入图片描述
    可以发现在单核的情况下,执行顺序相对固定,都是先执行的父进程再执行2个子进程。因为单核CPU在处理多线程程序时只能执行一跳指令,每个进程都是轮流执行,所以单核CPU在一定程度上控制了实验结果的随机性。

    第二题:

    在这里插入图片描述
    执行程序,可以发现程序把当前文件夹下的所有文件都输出了。

    第三题:

    在这里插入图片描述
    可以发现,每个进程加锁之后,都不会切换到另外2个进程去执行,只有该进程执行完毕之后解锁之后,才会切换到下一个进程去执行。

    第四题:

    正在运行程序:
    在这里插入图片描述
    开启一个新的终端,执行kill -2 54969,向父进程发送中断信号:
    在这里插入图片描述
    最终的运行结果如下:
    在这里插入图片描述

    第五题:
    server中已成功接收到cilent发送的信息并显示出来。
    在这里插入图片描述

    第六题:
    client每发送一条消息,server都会接收之后,client才会发送下一条消息。
    在这里插入图片描述

    六、实验总结

    每一道题都是自己先在网上找了一些资料之后才开始的,花费了比较长的时间,也遇到了许多困难,比如在linux系统中操作,遇到了许多不适应的地方,很多不懂的地方也是自己花了很多时间搜索资料解决的。但是收获也很大,通过本次的实验,我也对进程、消息和共享区有了更进一步的了解。

    七、实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。

    第一题

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main()
    {
    	int p;
    	p = fork();
    	if (p < 0)
    	{
    		//fork()函数的返回值<0表示进程创建失败!
    		printf("子进程创建失败!\n");
    	}
    	else if (p == 0)
    	{
    		//p==0表示这是子进程
    		printf("我是第一个子进程,号码是%d\n", getpid());
    		exit(0); //中断进程
    	}
    	else
    	{
    		//p>0表示当前是父进程
    		//创建第二个子进程
    		int q;
    		q = fork();
    		if (q < 0)
    		{
    			printf("子进程创建失败!\n");
    		}
    		else if (q == 0)
    		{
    			printf("我是第二个子进程,号码是%d\n", getpid());
    			exit(0);
    		}
    		else
    		{
    			printf("我是父进程,号码是%d\n", getpid());
    			exit(0); //中断进程
    		}
    	}
    }
    
    

    第二题

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    int main()
    {
    	int p;
    	p = fork(); //创建子进程
    	switch (p)
    	{
    	case -1:
    		printf("创建失败\n");
    		exit(1);
    	case 0: //子进程
    		execl("/bin/ls", "ls", NULL);
    		printf("execfail!\n");
    		exit(1);
    	default:		//父进程
    		wait(NULL); //先暂停父进程,避免父进程提前结束!
    		printf("lscompleted!\n");
    		exit(0);
    	}
    }
    
    

    第三题

    #include <stdio.h>
    #include <unistd.h>
    int main()
    {
        int p;
        int i;
        p = fork(); //创建子进程
        if(p < 0){
            printf("第一个子进程创建失败\n");
        }else if(p == 0){
            //当前是子进程
            lockf(1,1,0);   //加锁
            for(i = 0;i <10;++i){
                printf("%d\n",i);
            }
            printf("第一个子进程执行完成\n");
            lockf(1,0,0);   //解锁
        }else{
            p = fork();  //创建第二个子进程
            if(p < 0){
                printf("第二个子进程创建失败\n");
            }else if(p == 0){
                //子进程
                lockf(1,1,0);   //加锁
                for(i = 10;i <20;++i){
                    printf("%d\n",i);
                }
                printf("第二个子进程执行完成\n");
                lockf(1,0,0);   //解锁
            }else{
                //当前是父进程
                lockf(1,1,0);   //加锁
                for(i = 20;i <30;++i){
                    printf("%d\n",i);
                }
                printf("父进程执行完成\n");
                lockf(1,0,0);   //解锁
            }
        }
    }
    
    

    第四题

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <signal.h>
    #include<sys/wait.h>
    
    void sighandler(int sig){
        //printf("Response\n");
    }
    
    int main(){
    
        int pid1;
        int pid2;
        pid1 = fork(); //创建第一个子进程
        if(pid1 == 0){
            //第一个子进程
            printf("我是第一个子进程,pid=%d\n",getpid());
            signal(SIGUSR1,sighandler);   //接受信号
            pause();   //程序暂停,直到信号出现
            printf("Child process1 is killed by parent!\n");  
            exit(1);
        }else{
            pid2 = fork(); //创建第二个子进程
            if(pid2 == 0){
                // 第二个子进程
                printf("我是第二个子进程,pid=%d\n",getpid());
                signal(SIGUSR2,sighandler);   //接受信号
                pause();   //程序暂停,直到信号出现
                printf("Child process2 is killed by parent!\n");
                exit(2);
            }else{
                //父进程
                printf("我是父进程,pid=%d\n",getpid());
                signal(SIGINT,sighandler);
                pause();
                kill(pid1,SIGUSR1); //向进程pid1发送用户自定义信号1
                kill(pid2,SIGUSR2); //向进程pid2发送用户自定义信号2
                wait(NULL);
                wait(NULL);
                wait(NULL);         //等待两个子进程
                printf("Parent process is killed!\n");
                exit(0);
            }
        }
    }
    
    

    第五题

    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/msg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include<string.h>
    #include<sys/wait.h>
    
    #define MSGKEY 1024  //消息key
    
    typedef struct msgForm{ //消息结构体
        long  mtype;            /*消息类型*/
        char  mtext[1024];      /*消息的文本*/
    }msgForm;
    
    int msgqid;
    
    void client(){
        msgForm msg;
        msg.mtype = 10; //定义消息类型
        strcpy(msg.mtext,"Hello,World!!!");
        msgqid = msgget(MSGKEY,0777); //创建消息队列
        msgsnd(msgqid,&msg,sizeof(msg),0);
        printf("(client)已发送信息!\n");
        printf("(client)发送的信息为%s\n",msg.mtext);
        exit(0);
    }
    
    void server(){
        msgForm msg;
        msgqid=msgget(MSGKEY,0777|IPC_CREAT); //创建一个所有用户都可以读、写、执行的队列
        wait(0);    //保证cilent发送完消息并结束进程之后才继续执行下面
        msgrcv(msgqid,&msg,sizeof(msg),0,0);    //接受信息
        printf("(server)已接受信息!\n");
        printf("(server)接受到的信息为%s\n",msg.mtext);
        msgctl(msgqid, IPC_RMID,0); //消除消息队列的标识符。
        exit(0);
    }
    
    
    int main(){
        int p;
        p = fork(); //创建子进程
        if(p == 0){
            client();
        }else{
            server();
        }
        return 0;
    }
    

    第六题

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/wait.h>
    
    #define SHMKEY 1024 // 共享存储区的key
    int shmid, i;
    int *addr;
    
    void client()
    {
        int i;
        shmid = shmget(SHMKEY, 1024, 0777 | IPC_CREAT); /*创建一个共享存储区,名字为75,大小为1024字节,不重复创建*/
        addr = shmat(shmid, 0, 0);                      /*共享存储区所附接到的进程虚地址(首地址)*/
        for (i = 9; i >= 0; i--)
        {
            while (*addr != -1); //确保服务端收到了一条信息,再发下一条
            printf("(client)sent\n");
            *addr = i;
        }
        exit(0);
    }
    
    void server()
    {
        shmid = shmget(SHMKEY, 1024, 0777 | IPC_CREAT); //创建一个共享存储区,大小为1024字节
        addr = shmat(shmid, 0, 0);                      //共享存储区所附接到的进程虚地址(首地址)
        do
        {
            *addr = -1;
            while (*addr == -1);//响应客户端
            printf("(server)received\n");
        } while (*addr);
        shmctl(shmid, IPC_RMID, 0); //撤消共享存储区,归还资源
        exit(0);
    }
    
    void main()
    {
        int p;
        p = fork(); //创建子进程
        if(p < 0){
            printf("子进程创建失败!\n");
        }
        else if(p == 0){
            server();
        }else{
            client();
        }
    
    }
    
    

    八、思考题

    1、进程创建与进程并发执行

    (1)系统是怎样创建进程的?
    答:通过fork()系统调用之后,执行以下操作:
    ① 申请空白PCB(过程控制块)。
    ② 为新工序分配资源。
    ③ 初始化PCB。
    ④ 将新进程插入就绪队列。

    (2)当首次调用新创建进程时,其入口在哪里?
    答:进程的进程控制块(PCB)结构中有指向其TTS(任务状态段)的指针,TTS里面存放着进程的入口。

    (3)利用strace 和ltrace -f -i -S ./executable-file-name查看程序执行过程,并分析原因,画出进程家族树。
    在这里插入图片描述 在这里插入图片描述
    执行过程如图所示,父进程先创建的子进程2再创建子进程1,并且在输出内容后被杀死。进程家族树如图所示:
    在这里插入图片描述

    2、进程的睡眠、同步、撤消等进程控制

    (1)可执行文件加载时进行了哪些处理?
    答:进程用exec( )装入命令ls ,exec( )后,子进程的代码被ls的代码取代,这时子进程的PC指向ls的第1条语句,开始执行ls的命令代码。

    (2)什么是进程同步?wait( )是如何实现进程同步的?
    答:进程同步是指:在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。wait()函数一调用,父进程就会先暂停执行,直到有一个进程结束之后才去控制。

    (3)wait( )和exit()是如何控制实验结果的随机性的?
    答:子进程在使用了execl()函数之后,列出了当前目录下所有的文件,执行完这个函数之后才去调用exit(),退出当前进程,才去接着继续执行父进程,所以我们可以发现父进程的lscompleted!永远在最后才输出,这样子就控制了实验结果的随机性。

    3、多进程通过加锁互斥并发运行

    (1)进程加锁和未上锁的输出结果相同吗? 为什么?
    答:不相同。因为进程加锁之后,就保证了在执行其中一个进程的时候,不会切换到另外2个进程去执行,只有解锁之后才会切换到另外2个进程。如果进程未上锁,那么程序就会随时切换到另外2个进程执行,输出结果会不相同。

    4、进程间通过信号机制实现软中断通信

    (1)为了得到实验内容要求的结果,需要用到哪些系统调用函数来实现及进程间的通信控制和同步?
    答:kill()和signal()

    (2)kill( )和signal( )函数在信号通信中的作用是什么?如果分别注释掉它们,结果会如何?
    答:

    • kill()函数的作用是向指定的子进程发送信号,signal()的作用是接收指定的信号,signal()后面需要配合pause()函数一起使用。
    • 如果注释掉kill(),那么子进程将接收不到信号,导致子进程一直在等待信号的出现,父进程也一直在等待子进程结束,最终程序一直阻塞。
    • 如果注释掉signal(),那么父进程也接收不到中断信号,发出中断信号后程序也没反应,程序也是一直阻塞。

    5、消息的发送与接收

    (1)为了便于操作和观察结果,需要编制几个程序分别用于消息的发送与接收?
    答:两个程序,其中client发送消息,server接收消息。

    (2)这些程序如何进行编辑、编译和执行?为什么?
    答:
    ① 两个程序分别编辑,执行gcc client.c -o client和gcc server.c -o server
    ② 执行: ./server和 ./client
    ③ client和server是两种不同的程序,需要分开来编辑、编译和执行,client发送请求,server用于接收消息。

    (3)如何实现消息的发送与接收的同步?
    答:发送程序和接收程序都必须一直做好相互通信的准备。

    6、进程的共享存储区通信

    (1)为了便于操作和观察结果,需要如何合理设计程序来实现子进程间的共享存储区通信?
    答:每个进程都需要开辟一个共享存储区,然后附加到自己的一个内存空间中,就可以正常地进行读写操作了。

    (2)比较消息通信和共享存储区通信这两种进程通信机制的性能和优缺点。
    答:
    ①消息队列的建立比共享区的设立消耗的资源少。前者只是一个软件上设定的问题,后者需要对硬件的操作,实现内存的映像,当然控制起来比前者复杂。如果每次都重新进行队列或共享的建立,共享区的设立没有什么优势。
    ②当消息队列和共享区建立好后,共享区的数据传输,受到了系统硬件的支持,不耗费多余的资源;而消息传递,由软件进行控制和实现,需要消耗一定的cpu的资源。从这个意义上讲,共享区更适合频繁和大量的数据传输。
    ③消息的传递,自身就带有同步的控制。当等到消息的时候,进程进入睡眠状态,不再消耗cpu资源。而共享队列如果不借助其他机制进行同步,接收数据的一方必须进行不断的查询,白白浪费了大量的cpu资源。可见,消息方式的使用更加灵活。

    展开全文
  • 2、熟悉LINUX系统中进程之间软中断通信的基本原理 二、实验内容: 1、编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键);捕捉到中断信号后,父进程用系统调用...
  • 操作系统 实验四 进程间通信实验

    千次阅读 2021-11-26 11:26:02
    实验四 进程间通信实验 一、实验目的 1、了解什么是信号。 2、熟悉LINUX系统中进程之间软中断通信的基本原理。 3、理解进程的同步关系。 4、了解什么是管道。 5、熟悉UNIX/LINUX支持的管道通信方式。 二、...
  • 2.结合实践,加强对数字基带通信系统原理和分析方法的掌握; 3.进一步熟悉systemview软件的使用,掌握主要操作步骤。 二、实验内容 构造一个简单示意性基带传输系统。以双极性PN码发生器模拟一个数据信源,码速率...
  • 1. 在VMWare虚拟机环境或真实物理机器上,安装一个Linux操作系统。 2. 体验Linux操作系统中X Windows系统的使用。 3. 尝试Linux系统键盘命令的使用,并熟练掌握常用的基本命令。 4. 掌握命令行方式下vi编辑器的使用...
  • 实验四:《操作系统》之管道通信

    千次阅读 多人点赞 2020-06-28 14:54:51
    管道通信 往期回顾: Part0. 实验环境 Part1-1.熟悉UKylin环境 Part1-2.熟悉UKylin环境 Part2.进程控制 Part3.进程通信 一、实验目的 1.了解管道的概念。 2.掌握Linux支持的管道通信方式。 二、实验内容 编写一...
  • 西电操作系统上机实验3

    千次阅读 2021-07-04 16:40:09
    实验3:信号通信 一、实验目的   利用信号通信机制在父子进程及兄弟进程间进行通信。 二、实验内容   父进程创建一个有名事件,由子进程发送事件信号,父进程获取事件信号后进行相应的处理。 三、程序框架    ...
  • 大三上学期开的课程操作系统,其中有四次上机实验实验代码都有,有几次代码缺(老师故意的,为了让我们学习)
  • 实验一:实验平台操作基础 一、实验目的: (一)了解实验平台的组成和各个模块的原理和功能; (二)熟悉实验平台的操作; (三)预习接下来的四个实验,熟悉实验中的基本操作; (四)准备其余实验的记录表格...
  • 内蒙古师大计算机与信息工程学院 操作系统课程实验报告 实验三 实习题目 进程的软中断通信 指导教师 职 称 学生姓名 学 号 日 期 精选文库 实现工具 C 语言 实现环境 Linux 内容 编写一段程序 父进程创建一个子进程 ...
  • 实验题 目 进程的管道及消息通 信 小组 合作 否 姓名 班级 学 号 一实验目的 1了解什么是管道 2熟悉LINUX支持的管道通信方式 3了解Linux系统中的进程间通信机制包括共 享内存和信号量 二实验环境 安装了Linux虚拟机...
  • 作业必备:操作系统实验四【软中断通信

    千次阅读 多人点赞 2022-04-06 16:08:57
    实验四:软中断通信*** 大家好呀~~~今天是小白学操作系统系列第四篇。在开始之前,~~让我们先来摆烂 啊不,阅读一些材料。不了解没有办法做实验哦~ 若觉文字太多,可直接阅读红体字~~ 【实验必读材料】: 一、信号的...
  • 操作系统课程设计—实验九基于信号量机制的并发程序设计 实验九 基于信号量机制的并发程序设计 1、实验目的 (1) 回顾操作系统进程、线程的有关概念,针对经典的同步、互斥、死锁与饥饿问题进行并发 程序设计与实现。...
  • 自己设计一个程序,该程序创建一个子进程,使父子进程合作,...要求在该程序中还要使用进程的睡眠、进程图象改换、父进程等待子进程终止、信号的设置与传送(包括信号处理程序)、子进程的终止等有关进程的系统调用。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,465
精华内容 12,186
关键字:

操作系统信号通信实验