一【实验目的】
1.理解进程间通信原理;
2.掌握进程中信号量、共享内存、消息队列相关的函数的使用;
3.支撑网络空间安全专业的专业核心能力、综合创新能力。
二【实验要求】
以下每个实验均要求:
1.“实验源代码”处:粘贴所编写的程序源码,务必添加关键语句的注释;
2.“实验结果”:截图(包括编写的程序和运行结果)粘贴到“实验结果”下方,截图需看到本人的名字及学号;
3.有“讨论”的题目,请务必认真回答;
三【实验内容】
4-1 编写程序实现以下功能:
利用匿名管道实现父子进程间通信,要求
父进程发送字符串“hello child”给子进程;
子进程收到父进程发送的数据后,给父进程回复“hello farther”;
父子进程通信完毕,父进程依次打印子进程的退出状态以及子进程的pid。
【源代码】
4-2 编写程序实现以下功能:利用匿名管道实现兄弟进程间通信,要求兄进程发送字符串“This is elder brother ,pid is (兄进程进程号)”给弟进程;弟进程收到兄进程发送的数据后,给兄进程回复“This is younger brother ,pid is(第进程进程号)”;
实验步骤:父进程先创建一个子进程(这个就是兄进程) 然后通过判断进入父进程中,然后再次创建一个进程 这个进程就是弟进程 父进程先创建一个子进程(这个就是兄进程) 然后通过判断进入父进程中,然后再次创建一个进程 这个进程就是弟进程。
【源代码】
4-3 编写程序实现以下功能:
利用有名管道文件实现进程间通信,要求
写进程向有名管道文件写入10次“hello world”;
读进程读取有名管道文件中的内容,并依次打印。
【源程序】
4-4 编写代码完成以下功能:
创建共享内存,写进程通过键盘不断向内存写入“hello world”;
如果结束写操作,则通过键盘输入“end”;
读进程从共享内存读取数据,并打印。直到读到“end”为止。
【源程序】
4-5 编写代码完成以下功能:
进程A向消息队列发送消息“hello,world”
进程B从消息队列读取消息,并打印。
进程C向消息队列发送“自己在姓名”
进程D从消息队列中取出姓名字符串,并打印
消息队列也叫报文队列,是一个消息的链表。可以把消息看作是一个记录,具有特定的格式以及优先级。对消息队列具有写权限的进程可以按照一定的规则向消息队列中添加消息,而对消息队列具有写权限的进程可以从消息队列中读走消息。和管道相似的是,消息一旦从消息队列中被读走,则消息队列中便不在存在此条消息。
补充知识:IPC消息队列的缺省最大数为16;每个消息缺省最大值为8192字节;队列中的最大值缺省为16384字节;每个消息队列都有其对应的属性信息,存储在struct_msqid_ds结构体中。每个消息队列都有一个对应的id,标识消息队列的唯一性。
【源程序】
4-6(选做) 在文件读写代码(commu-file-server.c和commu-file-client.c)的基础上,编写程序实现使用信号量机制来协调读、写进程对文件的访问。
【源程序】
commu-file-server.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>main(int argc, char * argv[])
{int fd;time_t now;char * message;if(argc!=2){printf("errror usage!\nusage: server filename\n");exit(1);}if((fd=open(argv[1],O_CREAT|O_WRONLY|O_TRUNC,0644))==-1){perror("open");exit(1);}while(1){//time:获得时间参数,ctime:将时间转换为字符串time(&now);//获取当前日历时间(从1970-01-01 00:00:00到现在的秒数),返回值存储在变量nowmessage=ctime(&now);//返回带格式的日期和时间信息if((lseek(fd,0,SEEK_SET))==-1){perror("lseek");exit(1);}if(write(fd,message,strlen(message))==-1){perror("write");exit(1);}sleep(1);}
}commu-file-client.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>main(int argc, char *argv[])
{int fd,len;char buf[128];if(argc!=2){printf("error usage!\nusage: client filename");exit(1);}if((fd=open(argv[1],O_RDONLY))==-1){perror("open");exit(1);}while((len=read(fd,buf,128))>0){write(1,buf,len);}close(fd);
}
附录:
Linux下进程通信相关函数有:
信号量
信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。要调用的第一个函数是semget,用以获得一个信号量ID。
int semget(key_t key, int nsems, int flag);
key是IPC结构的关键字,flag将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。nsems是该集合中的信号量数。如果是创建新 集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。
semctl函数用来对信号量进行操作。
int semctl(int semid, int semnum, int cmd, union semun arg);
不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。
semop函数自动执行信号量集合上的操作数组。
int semop(int semid, struct sembuf semoparray[], size_t nops);
semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就是指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。
当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。
共享内存
共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。首先要用的函数是shmget,它获得一个共享存储标识符。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);
当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。
void *shmat(int shmid, void *addr, int flag);
shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址,进程可以对此进程进行读写操作。
断开共享内存连接:
与shmat函数相反,shmdt是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
函数原型
int shmdt(const void *shmaddr)
函数传入值
shmaddr:连接的共享内存的起始地址
函数返回值
成功:0
出错:-1,错误原因存于error中
附加说明
本函数调用并不删除所指定的共享内存区,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)目前的进程
错误代码
EINVAL:无效的参数shmaddr。
消息队列
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。
1.创建新消息队列或取得已存在消息队列
原型:int msgget(key_t key, int msgflg);
参数:
key:键值,可以指定,也可以由函数ftok生成。
msgflg:IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。
IPC_EXCL值,若没有该队列,则返回-1;若已存在,则返回0。
2.向队列读/写消息
原型:
msgrcv从队列中取用消息:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgsnd将数据放到消息队列中:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msqid:消息队列的标识码
msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息
msgsz:消息的大小。
msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。
msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。
3.设置消息队列属性
原型:int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
参数:msgctl 系统调用对 msgqid 标识的消息队列执行 cmd 操作,系统定义了 3 种 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID
IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。
IPC_SET : 该命令用来设置消息队列的属性,要设置的属性存储在buf中。
IPC_RMID : 从内核中删除 msqid 标识的消息队列。