实验项目四 进程通信
一、 实验类型
本实验为综合性实验。
二、 实验目的
1. 了解什么是消息,熟悉消息传送原理。
2. 了解和熟悉共享存储机制。
3. 掌握消息的发送与接收的实现方法。
三、 实验预备知识
任务一 消息的发送和接收
1. 实验基本原理
消息(message )是一个格式化的可变长的信息单元。消息机制允许由一个进程给其它任意的进程发送一个消息。当一个进程收到多个消息时,可将它们排成一个消息队列。消息使用两种重要的数据结构:一是消息首部,其中记录了一些与消息有关的信息,如消息数据的字节数;二是消息队列头表,其每一表项是作为一个消息队列的消息头,记录了消息队列的有关信息。
消息机制的数据结构:
struct msgform
{
long mtype;
char mtext[1024];
};
(1)消息首部
记录一些与消息有关的信息,如消息的类型、大小、指向消息数据区的指针、消息队列的链接指针等。
(2)消息队列头表
其每一项作为一个消息队列的消息头,记录了消息队列的有关信息如指向消息队列中第一个消息和指向最后一个消息的指针、队列中消息的数目、队列中消息数据的总字节数、队列所允许消息数据的最大字节总数,还有最近一次执行发送操作的进程标识符和时间、最近一次执行接收操作的进程标识符和时间等。
(3)消息队列的描述符
UNIX 中,每一个消息队列都有一个称为关键字(key )的名字,是由用户指定的;消息队列有一消息队列描述符,其作用与用户文件描述符一样,也是为了方便用户和系统对消息队列的访问。
2. msgget( )函数
头文件:
#include
#include
#include
函数原型:
int msgget(key_t key, int msgflag);
函数说明:
创建一个消息,获得一个消息的描述符。系统内核将搜索消息队列头表,确定是否有指定名字的消息队列。若无,系统内核将分配一新的消息队列头,并对它进行初始化,然后给用户返回一个消息队列描述符,否则它只是检查消息队列的许可权便返回。
系统调用格式:msgqid=msgget(key,flag)。其中:msgqid 是该系统调用返回的描述符,失败则返回-1。msgget()函数的第一个参数是消息队列对象的关键字(key),函数将它与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建。而函数进行的具体操作是由第二个参数msgflg 控制的。它可以取下面的几个值:
IPC_CREAT :
如果消息队列对象不存在,则创建之,否则则进行打开操作;
IPC_EXCL:
和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建之,否则产生一个错误并返回。
如果单独使用IPC_CREAT 标志,msgget()函数要么返回一个已经存在的消息队列对象的标识符,要么返回一个新建立的消息队列对象的标识符。如果将IPC_CREAT 和IPC_EXCL标志一起使用,msgget()将返回一个新建的消息对象的标识符,或者返回-1。如果消息队列对象已存在,IPC_EXCL 标志本身并没有太大的意义,但和IPC_CREAT 标志一起使用可以用来保证所得的消息队列对象是新创建的而不是打开的已有的对象。
除了以上的两个标志以外,在msgflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。
3. msgsnd( )函数
头文件:
#include
#include
#include
函数原型:
int msgsnd(int msqid, const void *msgp, intt msgsz, int msgflg);
函数说明:
在消息队列上进行收发消息,为了发送消息,调用进程对消息队列进行写入时必须有写权能。成功执行时返回0,失败时返回-1。其中msgqid 是返回消息队列的描述符;msgp 是指向用户消息缓冲区的一个结构体指针。缓冲区中包括消息类型和消息正文,即
{
long mtype; /*消息类型*/
char mtext[ ]; /*消息的文本*/
}
msgsz 指示由msgp 指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX( ) 系统可调用参数来确定。msgflag 参数是控制函数行为的标志,取值可以是:0, 表示忽略。若在标志msgflag 中未设置IPC_NOWAIT位,则当该消息队列中的字节数超过最大值时,或系统范围的消息数超过某一最大值时,调用msgsnd 进程睡眠。若是设置
IPC_NOWAIT,则在此情况下,msgsnd 立即返回。
对于msgsnd( ),系统内核须完成以下工作:
(1)对消息队列的描述符和许可权及消息长度等进行检查。若合法才继续执行,否则返回;
(2)系统内核为消息分配消息数据区。将用户消息缓冲区中的消息正文,拷贝到消息数据区;
(3)分配消息首部,并将它链入消息队列的末尾。在消息首部中须填写消息类型、消息大小和指向消息数据区的指针等数据;
(4)修改消息队列头中的数据,如队列中的消息数、字节总数等。最后,唤醒等待消息的进程。
4. msgrcv ( )函数
头文件:
#include
#include
#include
函数原型:
int msgrcv(int msqid, void *msgp, int msgsz, long msgtyp, int msgflg);
函数说明:
msgrcv ( ) 函数用来接收一消息。从指定的消息队列中接收指定类型的消息。其中,msgqid,msgp,msgsz,msgflg 与msgsnd 中的对应参数相似,msgtyp 是规定要读的消息类型,msgflg 规定倘若该队列无消息,系统内核应做的操作,是控制函数行为的标志,取值可以是:0, 表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG ,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如此时设置了IPC_NOWAIT标志,则立即返回,若在flag 中设置了MS_NOERROR,且所接收的消息大于size ,则系统内核截断所接收的消息。
对于msgrcv 系统调用,系统内核须完成下述工作:
(1)对消息队列的描述符和许可权等进行检查。若合法,就往下执行;否则返回;
(2)根据msgtyp 的不同分成三种情况处理:
msgtyp=0,接收该队列的第一个消息,并将它返回给调用者;
msgtyp 为正整数,接收类型type 的第一个消息;
msgtyp 为负整数,接收小于等于type 绝对值的最低类型的第一个消息。
(3)当所返回消息大小等于或小于用户的请求时,系统内核便将消息正文拷贝到用户区,并从消息队列中删除此消息,然后唤醒睡眠的发送进程。但如果消息长度比用户要求的大时,则做出错返回。
5. msgctl ( )函数
头文件:
#include
#include
#include
函数原型:
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
函数说明:
系统调用msgctl ( )用来控制对消息队列的操作。其中,函数调用成功时返回0,不成功则返回-1。buf 是用户缓冲区地址,供用户存放控制参数和查询结果;cmd 是规定的命令。命令可分三类:
(1)IPC_STAT。查询有关消息队列情况的命令。如查询队列中的消息数目、队列中的最大字节数、最后一个发送消息的进程标识符、发送时间等;
(2)IPC_SET。按buf 指向的结构中的值,设置和改变有关消息队列属性的命令。如改变消息队列的用户标识符、消息队列的许可权等;
(3)IPC_RMID。消除消息队列的标识符。
msgqid_ds 结构定义如下:
struct msgqid_ds
{ struct ipc_perm msg_perm; /*许可权结构*/
short pad1[7]; /*由系统使用*/
ushort msg_qnum; /*队列上消息数*/
ushort msg_qbytes; /*队列上最大字节数*/
ushort msg_lspid; /*最后发送消息的PID*/
ushort msg_lrpid; /*最后接收消息的PID*/
time_t msg_stime; /*最后发送消息的时间*/
time_t msg_rtime; /*最后接收消息的时间*/
time_t msg_ctime; /*最后更改时间*/
};
struct ipc_perm
{ ushort uid; /*当前用户*/
ushort gid;
ushort cuid;
ushort cgid;
ushort mode;
{ short pid1; long pad2;}
}
6. 样例程序
//client_ex.c 发送端
#include
#include
#include
#include
#include
#define MSGKEY 75
struct msgform
{
long mtype;
char mtext[1024];
}msg;
int msgqid;
/*当前进程组*/ /*创建用户*/ /*创建进程组*/ /*存取许可权*/ /*由系统使用*/
void client()
{ msgqid=msgget(MSGKEY,0777); /*打开75#消息队列*/
msg.mtype=1;
msg.mtext[1]='a';
printf("(client)sent\n");
msgsnd(msgqid,&msg,1024,0); /*发送消息*/
exit(0);
}
int main( )
{ client( );
}
//server_ex.c 接收端
#include
#include
#include
#include
#include
#define MSGKEY 75
struct msgform
{
long mtype;
char mtext[1024];
}msg;
int msgqid;
void server( )
{ msgqid=msgget(MSGKEY,0777|IPC_CREAT); /*创建75#消息队列*/
msgrcv(msgqid,&msg,1024,0,0); /*接收消息*/
printf("%c ",msg.mtext[msg.mtype]);
printf("(server)received\n");
msgctl(msgqid,IPC_RMID,0); /*删除消息队列,归还资源*/
exit(0);
}
int main( )
{ server( );
}
任务二 使用共享存储区通信
1. 实验基本原理
共享存储区(Share Memory)是UNIX 系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。进程之间便可通过对共享存储区中数据的读、写来进行直接通信。图4.1列出二个进程通过共享一个共享存储区来进行通信的例子。其中,进程A 将建立的共享存储区附接到自己的AA' 区域,进程B 将它附接到自己的BB' 区域。
进程A 的虚空间
内存地址
进程B 的虚空间
图4.1 共享存储区
应当指出,共享存储区机制只为进程提供了用于实现通信的共享存储区和对共享存储区进行操作的手段,然而并未提供对该区进行互斥访问及进程同步的措施。因而当用户需要使用该机制时,必须自己设置同步和互斥措施才能保证实现正确的通信。
2. shmget ( )函数
头文件:
#include
#include
#include
函数原型:
int shmget(key_t key, size_t size, int shmflg);
函数说明:
得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符。其中,key 是共享存储区的名字;size 是其大小(以字节计); shmflg 是用户设置的标志,如IPC_CREAT。IPC_CREAT表示若系统中尚无指名的共享存储区,则由系统内核建立一个共享存储区;若系统中已有共享存储区,便忽略IPC_CREAT,并返回此共享内存的标识符。在shmflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。
3. shmat ( )函数
头文件:
#include
#include
#include
函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数说明:
把共享内存区对象映射到调用进程的地址空间,系统调用的返回值是共享存储区所附接到的进程虚地址。连接共享内存标识符为shmid 的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。其中,shmid 是共享存储区的标识符;shmaddr 是用户给定的,将共享存储区附接到进程的虚地址空间;shmflg 规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。
4. shmdt ( )函数
头文件:
#include
#include
#include
函数原型:
int shmdt(const void *shmaddr);
函数说明:
与shmat 函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此共享内存。其中,shmaddr 是要断开连接的虚地址,亦即以前由连接的系统调用shmat( ) 所返回的虚地址。调用成功时,返回0值,调用不成功,返回-1。本函数调用并不删除所指定的共享内存区,而只是将先前用shmat 函数连接(attach )好的共享内存脱离(detach )目前的进程。
5. shctl ( )函数
头文件:
#include
#include
#include
函数原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数说明:
系统调用shmctl ( )用来控制对共享存储区的操作,对其状态信息进行读取和修改。其中,shmid 是共享存储区的标识符,buf 是用户缓冲区地址,cmd 是操作命令。命令可分为多种类型:
(1)用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符等;
(2)用于设置或改变共享存储区的属性。如共享存储区的许可权、当前连接的进程计数等;
(3)对共享存储区的加锁和解锁命令;
(4)删除共享存储区标识符等。
上述的查询是将shmid 所指示的数据结构中的有关成员,放入所指示的缓冲区中;而设置是用由buf 所指示的缓冲区内容来设置由shmid 所指示的数据结构中的相应成员。
cmd 有下列几种数值:
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf 中;
IPC_SET:改变共享内存的状态,把buf 所指的shmid_ds结构中的uid 、gid 、mode 复制到共享内存的shmid_ds结构内; IPC_RMID:删除这片共享内存
6. 样例程序
//client_ex.c 发送端
#include
#include
#include
#include
#include
#define SHMKEY 75
int shmid,i;
int *addr;
void client( )
{ int i;
shmid=shmget(SHMKEY,1024,0777); /*打开共享存储区*/
addr=shmat(shmid,0,0); /*获得共享存储区首地址*/
while (*addr!=-1);
printf("(client) sent\n");
*addr=100;
exit(0);
}
int main( )
{ client( );
}
//server_ex.c 接收端
#include
#include
#include
#include
#include
#define SHMKEY 75
int shmid,i;
int *addr;
void server( )
{ shmid=shmget(SHMKEY,1024,0777|IPC_CREAT); /*创建共享存储区*/
addr=shmat(shmid,0,0); /*获取首地址*/
*addr=-1;
while (*addr==-1);
printf("%d",*addr);
printf(" (server) received\n");
shmctl(shmid,IPC_RMID,0); /*撤消共享存储区,归还资源*/
exit(0);
}
int main( )
{ server( );
}
四、 实验内容
1. 根据消息传送机理,使用系统调用msgget( ), msgsnd( ), msgrev( ), 及msgctl( )编制一长度为1k 的消息发送和接收的程序,要求在程序中完成10次消息的发送和接收,每次发送消息结束和接收消息结束都需给出相应的屏幕提示,且每次发送的的内容不少于一个字符,并能在接收端输出。
2. 根据共享存储区原理,使用系统调用shmget( ), shmat( ), shmdt( ), 及shctl( )编制程序,要求创建一个长度为1k 的共享存储区,并完成10次数据的发送和接收,每次发送数据结束和接收数据结束都需给出相应的屏幕提示,且每次发送的的数据应能在接收端输出。
五、 实验报告要求
1. 列出实验内容所要求的程序清单,并以截图形式记录相应运行结果。
对实验运行结果进行分析: 试比较实验中两种方法实现进程通信的不同
合肥师范学院实验报告
姓名:李梦影 课程名称:计算机操作系统 院(系):计算机科学与技术系 专业/年级:13级计算机专升本
实验项目四 进程通信
一、 实验类型
本实验为综合性实验。
二、 实验目的
1. 了解什么是消息,熟悉消息传送原理。
2. 了解和熟悉共享存储机制。
3. 掌握消息的发送与接收的实现方法。
三、 实验预备知识
任务一 消息的发送和接收
1. 实验基本原理
消息(message )是一个格式化的可变长的信息单元。消息机制允许由一个进程给其它任意的进程发送一个消息。当一个进程收到多个消息时,可将它们排成一个消息队列。消息使用两种重要的数据结构:一是消息首部,其中记录了一些与消息有关的信息,如消息数据的字节数;二是消息队列头表,其每一表项是作为一个消息队列的消息头,记录了消息队列的有关信息。
消息机制的数据结构:
struct msgform
{
long mtype;
char mtext[1024];
};
(1)消息首部
记录一些与消息有关的信息,如消息的类型、大小、指向消息数据区的指针、消息队列的链接指针等。
(2)消息队列头表
其每一项作为一个消息队列的消息头,记录了消息队列的有关信息如指向消息队列中第一个消息和指向最后一个消息的指针、队列中消息的数目、队列中消息数据的总字节数、队列所允许消息数据的最大字节总数,还有最近一次执行发送操作的进程标识符和时间、最近一次执行接收操作的进程标识符和时间等。
(3)消息队列的描述符
UNIX 中,每一个消息队列都有一个称为关键字(key )的名字,是由用户指定的;消息队列有一消息队列描述符,其作用与用户文件描述符一样,也是为了方便用户和系统对消息队列的访问。
2. msgget( )函数
头文件:
#include
#include
#include
函数原型:
int msgget(key_t key, int msgflag);
函数说明:
创建一个消息,获得一个消息的描述符。系统内核将搜索消息队列头表,确定是否有指定名字的消息队列。若无,系统内核将分配一新的消息队列头,并对它进行初始化,然后给用户返回一个消息队列描述符,否则它只是检查消息队列的许可权便返回。
系统调用格式:msgqid=msgget(key,flag)。其中:msgqid 是该系统调用返回的描述符,失败则返回-1。msgget()函数的第一个参数是消息队列对象的关键字(key),函数将它与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建。而函数进行的具体操作是由第二个参数msgflg 控制的。它可以取下面的几个值:
IPC_CREAT :
如果消息队列对象不存在,则创建之,否则则进行打开操作;
IPC_EXCL:
和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建之,否则产生一个错误并返回。
如果单独使用IPC_CREAT 标志,msgget()函数要么返回一个已经存在的消息队列对象的标识符,要么返回一个新建立的消息队列对象的标识符。如果将IPC_CREAT 和IPC_EXCL标志一起使用,msgget()将返回一个新建的消息对象的标识符,或者返回-1。如果消息队列对象已存在,IPC_EXCL 标志本身并没有太大的意义,但和IPC_CREAT 标志一起使用可以用来保证所得的消息队列对象是新创建的而不是打开的已有的对象。
除了以上的两个标志以外,在msgflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。
3. msgsnd( )函数
头文件:
#include
#include
#include
函数原型:
int msgsnd(int msqid, const void *msgp, intt msgsz, int msgflg);
函数说明:
在消息队列上进行收发消息,为了发送消息,调用进程对消息队列进行写入时必须有写权能。成功执行时返回0,失败时返回-1。其中msgqid 是返回消息队列的描述符;msgp 是指向用户消息缓冲区的一个结构体指针。缓冲区中包括消息类型和消息正文,即
{
long mtype; /*消息类型*/
char mtext[ ]; /*消息的文本*/
}
msgsz 指示由msgp 指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX( ) 系统可调用参数来确定。msgflag 参数是控制函数行为的标志,取值可以是:0, 表示忽略。若在标志msgflag 中未设置IPC_NOWAIT位,则当该消息队列中的字节数超过最大值时,或系统范围的消息数超过某一最大值时,调用msgsnd 进程睡眠。若是设置
IPC_NOWAIT,则在此情况下,msgsnd 立即返回。
对于msgsnd( ),系统内核须完成以下工作:
(1)对消息队列的描述符和许可权及消息长度等进行检查。若合法才继续执行,否则返回;
(2)系统内核为消息分配消息数据区。将用户消息缓冲区中的消息正文,拷贝到消息数据区;
(3)分配消息首部,并将它链入消息队列的末尾。在消息首部中须填写消息类型、消息大小和指向消息数据区的指针等数据;
(4)修改消息队列头中的数据,如队列中的消息数、字节总数等。最后,唤醒等待消息的进程。
4. msgrcv ( )函数
头文件:
#include
#include
#include
函数原型:
int msgrcv(int msqid, void *msgp, int msgsz, long msgtyp, int msgflg);
函数说明:
msgrcv ( ) 函数用来接收一消息。从指定的消息队列中接收指定类型的消息。其中,msgqid,msgp,msgsz,msgflg 与msgsnd 中的对应参数相似,msgtyp 是规定要读的消息类型,msgflg 规定倘若该队列无消息,系统内核应做的操作,是控制函数行为的标志,取值可以是:0, 表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG ,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如此时设置了IPC_NOWAIT标志,则立即返回,若在flag 中设置了MS_NOERROR,且所接收的消息大于size ,则系统内核截断所接收的消息。
对于msgrcv 系统调用,系统内核须完成下述工作:
(1)对消息队列的描述符和许可权等进行检查。若合法,就往下执行;否则返回;
(2)根据msgtyp 的不同分成三种情况处理:
msgtyp=0,接收该队列的第一个消息,并将它返回给调用者;
msgtyp 为正整数,接收类型type 的第一个消息;
msgtyp 为负整数,接收小于等于type 绝对值的最低类型的第一个消息。
(3)当所返回消息大小等于或小于用户的请求时,系统内核便将消息正文拷贝到用户区,并从消息队列中删除此消息,然后唤醒睡眠的发送进程。但如果消息长度比用户要求的大时,则做出错返回。
5. msgctl ( )函数
头文件:
#include
#include
#include
函数原型:
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
函数说明:
系统调用msgctl ( )用来控制对消息队列的操作。其中,函数调用成功时返回0,不成功则返回-1。buf 是用户缓冲区地址,供用户存放控制参数和查询结果;cmd 是规定的命令。命令可分三类:
(1)IPC_STAT。查询有关消息队列情况的命令。如查询队列中的消息数目、队列中的最大字节数、最后一个发送消息的进程标识符、发送时间等;
(2)IPC_SET。按buf 指向的结构中的值,设置和改变有关消息队列属性的命令。如改变消息队列的用户标识符、消息队列的许可权等;
(3)IPC_RMID。消除消息队列的标识符。
msgqid_ds 结构定义如下:
struct msgqid_ds
{ struct ipc_perm msg_perm; /*许可权结构*/
short pad1[7]; /*由系统使用*/
ushort msg_qnum; /*队列上消息数*/
ushort msg_qbytes; /*队列上最大字节数*/
ushort msg_lspid; /*最后发送消息的PID*/
ushort msg_lrpid; /*最后接收消息的PID*/
time_t msg_stime; /*最后发送消息的时间*/
time_t msg_rtime; /*最后接收消息的时间*/
time_t msg_ctime; /*最后更改时间*/
};
struct ipc_perm
{ ushort uid; /*当前用户*/
ushort gid;
ushort cuid;
ushort cgid;
ushort mode;
{ short pid1; long pad2;}
}
6. 样例程序
//client_ex.c 发送端
#include
#include
#include
#include
#include
#define MSGKEY 75
struct msgform
{
long mtype;
char mtext[1024];
}msg;
int msgqid;
/*当前进程组*/ /*创建用户*/ /*创建进程组*/ /*存取许可权*/ /*由系统使用*/
void client()
{ msgqid=msgget(MSGKEY,0777); /*打开75#消息队列*/
msg.mtype=1;
msg.mtext[1]='a';
printf("(client)sent\n");
msgsnd(msgqid,&msg,1024,0); /*发送消息*/
exit(0);
}
int main( )
{ client( );
}
//server_ex.c 接收端
#include
#include
#include
#include
#include
#define MSGKEY 75
struct msgform
{
long mtype;
char mtext[1024];
}msg;
int msgqid;
void server( )
{ msgqid=msgget(MSGKEY,0777|IPC_CREAT); /*创建75#消息队列*/
msgrcv(msgqid,&msg,1024,0,0); /*接收消息*/
printf("%c ",msg.mtext[msg.mtype]);
printf("(server)received\n");
msgctl(msgqid,IPC_RMID,0); /*删除消息队列,归还资源*/
exit(0);
}
int main( )
{ server( );
}
任务二 使用共享存储区通信
1. 实验基本原理
共享存储区(Share Memory)是UNIX 系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。进程之间便可通过对共享存储区中数据的读、写来进行直接通信。图4.1列出二个进程通过共享一个共享存储区来进行通信的例子。其中,进程A 将建立的共享存储区附接到自己的AA' 区域,进程B 将它附接到自己的BB' 区域。
进程A 的虚空间
内存地址
进程B 的虚空间
图4.1 共享存储区
应当指出,共享存储区机制只为进程提供了用于实现通信的共享存储区和对共享存储区进行操作的手段,然而并未提供对该区进行互斥访问及进程同步的措施。因而当用户需要使用该机制时,必须自己设置同步和互斥措施才能保证实现正确的通信。
2. shmget ( )函数
头文件:
#include
#include
#include
函数原型:
int shmget(key_t key, size_t size, int shmflg);
函数说明:
得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符。其中,key 是共享存储区的名字;size 是其大小(以字节计); shmflg 是用户设置的标志,如IPC_CREAT。IPC_CREAT表示若系统中尚无指名的共享存储区,则由系统内核建立一个共享存储区;若系统中已有共享存储区,便忽略IPC_CREAT,并返回此共享内存的标识符。在shmflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。
3. shmat ( )函数
头文件:
#include
#include
#include
函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数说明:
把共享内存区对象映射到调用进程的地址空间,系统调用的返回值是共享存储区所附接到的进程虚地址。连接共享内存标识符为shmid 的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。其中,shmid 是共享存储区的标识符;shmaddr 是用户给定的,将共享存储区附接到进程的虚地址空间;shmflg 规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。
4. shmdt ( )函数
头文件:
#include
#include
#include
函数原型:
int shmdt(const void *shmaddr);
函数说明:
与shmat 函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此共享内存。其中,shmaddr 是要断开连接的虚地址,亦即以前由连接的系统调用shmat( ) 所返回的虚地址。调用成功时,返回0值,调用不成功,返回-1。本函数调用并不删除所指定的共享内存区,而只是将先前用shmat 函数连接(attach )好的共享内存脱离(detach )目前的进程。
5. shctl ( )函数
头文件:
#include
#include
#include
函数原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数说明:
系统调用shmctl ( )用来控制对共享存储区的操作,对其状态信息进行读取和修改。其中,shmid 是共享存储区的标识符,buf 是用户缓冲区地址,cmd 是操作命令。命令可分为多种类型:
(1)用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符等;
(2)用于设置或改变共享存储区的属性。如共享存储区的许可权、当前连接的进程计数等;
(3)对共享存储区的加锁和解锁命令;
(4)删除共享存储区标识符等。
上述的查询是将shmid 所指示的数据结构中的有关成员,放入所指示的缓冲区中;而设置是用由buf 所指示的缓冲区内容来设置由shmid 所指示的数据结构中的相应成员。
cmd 有下列几种数值:
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf 中;
IPC_SET:改变共享内存的状态,把buf 所指的shmid_ds结构中的uid 、gid 、mode 复制到共享内存的shmid_ds结构内; IPC_RMID:删除这片共享内存
6. 样例程序
//client_ex.c 发送端
#include
#include
#include
#include
#include
#define SHMKEY 75
int shmid,i;
int *addr;
void client( )
{ int i;
shmid=shmget(SHMKEY,1024,0777); /*打开共享存储区*/
addr=shmat(shmid,0,0); /*获得共享存储区首地址*/
while (*addr!=-1);
printf("(client) sent\n");
*addr=100;
exit(0);
}
int main( )
{ client( );
}
//server_ex.c 接收端
#include
#include
#include
#include
#include
#define SHMKEY 75
int shmid,i;
int *addr;
void server( )
{ shmid=shmget(SHMKEY,1024,0777|IPC_CREAT); /*创建共享存储区*/
addr=shmat(shmid,0,0); /*获取首地址*/
*addr=-1;
while (*addr==-1);
printf("%d",*addr);
printf(" (server) received\n");
shmctl(shmid,IPC_RMID,0); /*撤消共享存储区,归还资源*/
exit(0);
}
int main( )
{ server( );
}
四、 实验内容
1. 根据消息传送机理,使用系统调用msgget( ), msgsnd( ), msgrev( ), 及msgctl( )编制一长度为1k 的消息发送和接收的程序,要求在程序中完成10次消息的发送和接收,每次发送消息结束和接收消息结束都需给出相应的屏幕提示,且每次发送的的内容不少于一个字符,并能在接收端输出。
2. 根据共享存储区原理,使用系统调用shmget( ), shmat( ), shmdt( ), 及shctl( )编制程序,要求创建一个长度为1k 的共享存储区,并完成10次数据的发送和接收,每次发送数据结束和接收数据结束都需给出相应的屏幕提示,且每次发送的的数据应能在接收端输出。
五、 实验报告要求
1. 列出实验内容所要求的程序清单,并以截图形式记录相应运行结果。
对实验运行结果进行分析: 试比较实验中两种方法实现进程通信的不同
合肥师范学院实验报告
姓名:李梦影 课程名称:计算机操作系统 院(系):计算机科学与技术系 专业/年级:13级计算机专升本