《操作系统》实验报告——进程通信

42 篇文章 6 订阅

理论知识

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)的区别

 

1. 实验目的 1) 加深对进程概念的理解,明确进程和程序的区别。 2) 进一步认识并发执行的实质。 3) 分析进程争用资源的现象,学习解决进程互斥的方法。 4) 学习解决进程同步的方法。 5) 了解Linux系统中进程通信的基本原理。   进程是操作系统中最重要的概念,贯穿始终,也是学习现代操作系统的关键。通过本次实验,要求理解进程的实质和进程管理的机制。在Linux系统下实现进程从创建到终止的全过程,从中体会进程的创建过程、父进程和子进程之间的关系、进程状态的变化、进程之间的互斥、同步机制、进程调度的原理和以管道为代表的进程间的通信方式的实现。 2. 内容及要求:   这是一个设计型实验,要求自行编制程序。   使用系统调用pipe()建立一条管道,两个子进程分别向管道写一句话:   Child process1 is sending a message!   Child process2 is sending a message!   父进程从管道读出来自两个子进程的信息,显示在屏幕上。   要求: 1) 父进程先接收子进程1发来的消息,然后再接收子进程2发来的消息。 2) 实现管道的互斥使用,当一个子进程正在对管道进行写操作时,另一子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定。 3) 实现父子进程的同步,当子进程把数据写入管道后,便去睡眠等待;当父进程试图从一空管道中读取数据时,也应等待,直到子进程将数据写入管道后,才将其唤醒。 3.相关的系统调用 1) fork() 用于创一个子进程。 格式:int fork(); 返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。 2) wait() 常用来控制父进程与子进程的同步。 在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,父进程从wait()返回继续执行原来的程序。 返回值:大于0时,为子进程的ID值;等于-1时,调用失败。 3) exit() 是进程结束时最常调用的。 格式:void exit( int status); 其中,status为进程结束状态。 4) pipe() 用于创建一个管道 格式:pipe(int fd); 其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型数组,fd[0]是管道的读端口,用于从管道读出数据,fd[1] 是管道的写端口,用于向管道写入数据。 返回值:0 调用成功;-1 调用失败。 5) sleep() 调用进程睡眠若干时间,之后唤醒。 格式:sleep(int t); 其中t为睡眠时间。 6) lockf() 用于对互斥资源加锁和解锁。在本实验中,该调用的格式为: lockf(fd[1],1,0);/* 表示对管道的写入端口加锁。 lockf(fd[1],0,0);/* 表示对管道的写入端口解锁。 7) write(fd[1],String,Length) 将字符串String的内容写入管道的写入口。 8) read(fd[0],String,Length) 从管道的读入口读出信息放入字符串String中。 4.程序流程 父进程: 1) 创建管道; 2) 创建子进程1; 3) 创建子进程2; 4) 等待从管道中读出子进程1写入的数据,并显示在屏幕上; 5) 等待从管道中读出子进程2写入的数据,并显示在屏幕上; 6) 退出。 子进程: 1) 将管道的写入口加锁; 2) 将信息“Child process n is sending message!”输入到变量OutPipe中,n=1,2; 3) 将OutPipe中信息写入管道; 4) 睡眠等待; 5) 将管道的写入口解锁; 6) 退出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Starzkg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值