《操作系统》实验报告——进程通信「终于解决」

(31) 2023-03-27 20:32

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说《操作系统》实验报告——进程通信「终于解决」,希望能够帮助你!!!。
《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第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;
}

 《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第2张

分析: 

调用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);
        }
}

 《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第3张

任务二:

(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;
}

《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第4张

分析: 

成功,返回共享内存段的标识符,内核中用于唯一的标识一个对象。对存在于内核存贮空间中的每个共享内存段,内核均为其维护着一个数据结构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++);/*打印共享区中的内容*/
}

《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第5张

《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第6张

分析: 

首先系统通过调用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");
                }
        }
}

《操作系统》实验报告——进程通信「终于解决」_https://bianchenghao6.com/blog__第7张

四、实验心得

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

 

参考文章

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

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

实验三 进程通信(一)

linux c 休眠函数sleep usleep

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

 

上一篇

已是最后文章

下一篇

已是最新文章

发表回复