实验三 进程的管道通信实验
实验三进程的管道通信
一、实验目的
(1)了解什么是管道
(2)熟悉UNIX/LINUX支持的管道通信方式
二、实验学时
4学时
三、实验内容
编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句话:
Child 1 is sending a message!
Child 2 is sending a message!
父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。四、实验指导
一、什么是管道
UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。
所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。
1、有名管道
一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod( )建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open( )打开。
2、无名管道
一个临时文件。利用pipe( )建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe( )的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。二种管道的读写方式是相同的,本文只讲无名管道。
3、pipe文件的建立
分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户文件描述符
4、读/写进程互斥
内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。
为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上, 锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。
三、所涉及的系统调用
1、pipe( )
建立一无名管道。
系统调用格式
pipe(filedes)
参数定义
int pipe(filedes);
int filedes[2];
其中,filedes[1]是写入端,filedes[0]是读出端。
该函数使用头文件如下:
#include
#inlcude
#include
2、read( )
系统调用格式
read(fd,buf,nbyte)
功能:从fd所指示的文件中读出nbyte个字节的数据,并将它们送至由指针buf所指示的缓冲区中。如该文件被加锁,等待,直到锁打开为止。
参数定义
int read(fd,buf,nbyte);
int fd;
char *buf;
unsigned nbyte;
3、write( )
系统调用格式
read(fd,buf,nbyte)
功能:把nbyte 个字节的数据,从buf所指向的缓冲区写到由fd所指向的文件中。如文件加锁,暂停写入,直至开锁。
参数定义同read( )。
四、参考程序
#include
#include
#include
int pid1,pid2;
main( )
{
int fd[2];
char outpipe[100],inpipe[100];
pipe(fd); /*创建一个管道*/
while ((pid1=fork( ))= =-1);
if(pid1= =0)
{
lockf(fd[1],1,0);
sprintf(outpipe,"child 1 process is sending message!");
/*把串放入数组outpipe中*/
write(fd[1],outpipe,50); /*向管道写长为50字节的串*/
sleep(5); /*自我阻塞5秒*/
lockf(fd[1],0,0);
exit(0);
}
else
{
while((pid2=fork( ))= =-1);
if(pid2= =0)
{ lockf(fd[1],1,0); /*互斥*/
sprintf(outpipe,"child 2 process is sending message!");
write(fd[1],outpipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else
{ wait(0); /*同步*/
read(fd[0],inpipe,50); /*从管道中读长为50字节的串*/
printf("%s/n",inpipe);
wait(0);
read(fd[0],inpipe,50);
printf("%s/n",inpipe);
exit(0);
}
}
}
五、运行结果
延迟5秒后显示
child 1 process is sending message!
再延迟5秒
child 2 process is sending message!
实验四进程间通信
一、实验目的
Linux系统的进程通信机构 (IPC) 允许在任意进程间大批量地交换数据。本实验的目的是了解和熟悉Linux支持的消息通讯机制及信息量机制。
二、实验学时
4学时
三、实验内容
利用msgget( )、msgsnd( )、msgrcv( )、msgctl( )等系统调用编写两个程序client.c和server.c,分别用于消息的发送和接收。
server建立一个key为75的消息队列,等待其它进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出server。server每接收到一个消息后显示一句“(server)received”。
client使用key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,即是server端需要的结束信号。client每发送一条消息后显示一句“(client)sent”。
四、实验要求
阅读Linux系统的msg.c、sem.c和shm.c等源码文件,熟悉Linux的三种机制。
五、实验步骤
利用msgget( )、msgsnd( )、msgrcv( )、msgctl( )等系统调用编写两个程序client.c和server.c,分别用于消息的发送和接收。
server建立一个key为75的消息队列,等待其它进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出server。server每接收到一个消息后显示一句“(server)received”。
client使用key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,即是server端需要的结束信号。client每发送一条消息后显示一句“(client)sent”。
server.c参考程序如下:
#include
#include
#include
#include
#include
#define MSGKEY 75
struct msgform
{
long mtype;
char mtext[1000];
}msg;
int msgqid;
void server()
{
msgqid=msgget(MSGKEY,0777|IPC_CREAT); /*创建75#消息队列*/ do
{
msgrcv(msgqid,&msg,1030,0,0); /*接收消息*/
printf("(server)received\n");
}while(msg.mtype!=1);
msgctl(msgqid,IPC_RMID,0); /*删除消息队列,归还资源*/ exit(0);
}
main()
{
server();
}
client.c参考程序如下:
#include
#include
#include
#include
#include
#define MSGKEY 75
struct msgform
{
long mtype;
char mtext[1000];
}msg;
int msgqid;
void client()
{
int i;
msgqid=msgget(MSGKEY,0777); /*打开75#消息队列*/
for(i=10;i>=1;i--)
{
msg.mtype=i;
printf("(client)sent\n");
msgsnd(msgqid,&msg,1024,0); /*发送消息*/
}
exit(0);
}
main()
{
client();
}
将上述两个程序分别编译为server和client,并按以下方式执行:
./server &
ipcs –q
./client
Client和server分别发送和接收了10条消息。观察运行结果,注意发送方发送消息和接收方接收消息的顺序。
涉及到的系统调用:
1、msgget( )
系统调用格式
int msgget(key_t key, int msgflg);
功能:获取与某个键关联的消息队列标识。消息队列被建立的情况有两种:(1)如果键的值是IPC_PRIVATE。
(2)或者键的值不是IPC_PRIVATE,并且键所对应的消息队列不存在,同时标志中指定IPC_CREAT。
参数定义
key:消息队列关联的键。
msgflg:消息队列的建立标志和存取权限。
返回说明:
成功执行时,返回消息队列标识值。失败返回-1,errno被设为以下的某个值:
EACCES:指定的消息队列已存在,但调用进程没有权限访问它,而且不拥有CAP_IPC_OWNER权限
EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
ENOENT:key指定的消息队列不存在同时msgflg中不指定IPC_CREAT标志ENOMEM:需要建立消息队列,但内存不足
ENOSPC:需要建立消息队列,但已达到系统的限制
该函数使用头文件如下:
#include
#include
#include
2、msgsnd( )和msgrcv( )
系统调用格式
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
功能:在消息队列上进行收发消息。为了发送消息,调用进程对消息队列必须有写权限。接收消息时必须有读权限。
参数定义
msqid:消息队列的识别码。
msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[100]; /* 消息文本 */
};
msgsz:消息的大小。
msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。
msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。
该函数使用头文件如下:
#include
#include
#include
返回说明:
成功执行时,msgsnd()返回0,msgrcv()返回拷贝到mtext数组的实际字节数。失败两者都返回-1,errno被设为以下的某个值:
[对于msgsnd]
EACCES:调用进程在消息队列上没有写权限,同时没有CAP_IPC_OWNER权限EAGAIN:由于消息队列的msg_qbytes的限制和msgflg中指定IPC_NOWAIT 标志,消息不能被发送
EFAULT:msgp指针指向的内存空间不可访问
EIDRM:消息队列已被删除
EINTR:等待消息队列空间可用时被信号中断
EINVAL:参数无效
ENOMEM:系统内存不足,无法将msgp指向的消息拷贝进来
[对于msgrcv]
E2BIG:消息文本长度大于msgsz,并且msgflg中没有指定MSG_NOERROR EACCES:调用进程没有读权限,同时没具有CAP_IPC_OWNER权限
EAGAIN:消息队列为空,并且msgflg中没有指定IPC_NOWAIT
EFAULT:msgp指向的空间不可访问
EIDRM:当进程睡眠等待接收消息时,消息已被删除
EINTR:当进程睡眠等待接收消息时,被信号中断
EINVAL:参数无效
ENOMSG:msgflg中指定了IPC_NOWAIT,同时所请求类型的消息不存在
3、msgctl( )
系统调用格式
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:在指定的消息队列上执行某种控制操作。
参数定义
msqid:消息队列识别码。
cmd:操作命令,可能值在下面给出:
IPC_STAT:将msqid所指定的消息队列的信息拷贝一份到buf指针所指向的地址。调用者必须对消息队列有读权限。
IPC_SET:将由buf所指向的msqid_ds结构的一些成员写入到与这个消息队列关联的内核结构。同时更新的字段有msg_ctime。结构体的以下成员会被更新:msg_qbytes,msg_perm.uid,msg_perm.gid和msg_perm.mode的低九位。调用进程的有效标识必须匹配消息队列属主(msg_perm.uid)或建立者
(msg_perm.uid),或者调用者必须拥有相关的特权(如Linux下的
CAP_IPC_RESOURCE)。
IPC_RMID:删除指定的消息队列,唤醒所有等待中的读者和写者进程。
IPC_INFO:(Linux特有命令)获取系统范围内消息队列的制约和其它参数,并储存在buf指向的结构。这一操作需要将msginfo类型造型成msqid_ds,msginfo定义于
struct msginfo {
int msgpool; /* 用于保存消息数据的缓冲池大小,字节为单位,尚未被使用 */
int msgmap; /* 消息映射中的最大入口,尚未被使用 */
int msgmax; /* 能够写入单个消息的最大字节数 */
int msgmnb; /* 能够写入消息队列的最大字节数; 队列建立期间用于初始化msg_qbytes字段 */
int msgmni; /* 系统允许的最大消息队列数 */
int msgssz; /* 消息段大小,尚未被使用 */
int msgtql; /* 队列上能够存放的最大消息数,尚未被使用 */
unsigned short int msgseg; /* 消息可用的最大段数,尚未被使用 */ };
MSG_INFO:(Linux特有命令)返回和IPC_INFO命令相同的信息。除了下面字段用消息队列耗费的系统资源信息填充外:msgpool字段包含有当前系统存在的消息数。msgmap字段包含有系统范围内所有队列的消息总量。msgtql字段包含有系统范围所有消息的总字节数。
MSG_STAT:(Linux特有命令)返回和IPC_STAT命令相同的信息。区别在于msqid参数并非队列标识,而是内核内部存放所有消息队列信息数组的索引。
buf:用于描述某个消息队列的元数据,下面给出它的原型:
struct msqid_ds {
struct ipc_perm msg_perm; /* 队列的属主和权限 */
time_t msg_stime; /* 最后一次msgsnd()操作的时间
*/
time_t msg_rtime; /* 最后一次msgrcv()操作的时间
*/
time_t msg_ctime; /* 最后一次队列改变的时间 */
unsigned long __msg_cbytes; /* 当前队列上包含的字节数 */
msgqnum_t msg_qnum; /* 当前队列上包含的消息数 */
msglen_t msg_qbytes; /* 队列上允许的最大字节数 */
pid_t msg_lspid; /* 最后一次执行msgsnd()的进程标识 */
pid_t msg_lrpid; /* 最后一次执行msgrcv()的进程标识 */
};
ipc_perm结构定义在
struct ipc_perm {
key_t key; /* 提供给msgget()的键 */
uid_t uid; /* 属主的有效UID */
gid_t gid; /* 属主的有效GID */
uid_t cuid; /* 建立者的有效UID */
gid_t cgid; /* 建立者的有效GID */
unsigned short mode; /* 权限位 */
unsigned short seq; /* 系列号 */
};
该函数使用头文件如下:
#include
#include
#include
返回说明:
成功执行时,IPC_STAT,IPC_SET和IPC_RMID返回0。IPC_INFO 或 MSG_INFO 返回内核内部记录所有消息队列信息数组的最高可用入口索引。MSG_STAT返回队列标识。失败返回-1,errno被设为以下的某个值:
EACCES:权限不足
EFAULT:buf所指向的空间不可访问
EIDRM:消息队列已被删除
EINVAL:参数无效
EPERM:操作不允许
任务4:并发进程间通过共享存储区实现数据传送
参考程序:
#include
#include
#include
#define SHMKEY 75
int shmid,i,p1,p2;
int *addr;
void CLIENT()/*发送过程*/
{
int i;
shmid=shmget(SHMKEY,1024,0777);/* 打开共享存储区*/
addr=(int *)shmat(shmid,0,0); /*获取共享存储区的首地址*/
for (i=29;i>=0;i--)
{
while (*addr!=-1);/*判断是否可写*/
*addr=i;/*写数据*/
printf("%d",*addr);
printf("(client)sent\n");
}
exit(0);
}
void SERVER()/*接收进程*/
{
int i;
shmid=shmget(SHMKEY,1024,0777|IPC_CREAT); /*创建共享存储区*/
addr=(int *)shmat(shmid,0,0);
do
{
*addr=-1;
while (*addr==-1);/*判断信息是否传来*/
printf("%d",*addr+30); /*读,并加工数据*/
printf("(server)received\n");
}while(*addr);
shmctl(shmid,IPC_RMID,0); /*撤消共享存储区,归还资源*/
exit(0);
}
main()
{
while ((p1=fork())==-1);/*父进程*/
if (p1==0) SERVER();/*子进程p1*/
while ((p2=fork())==-1); /*父进程*/
if (p2==0) CLIENT();/*子进程p2*/
wait(0); /*父进程*/
wait(0); /*父进程*/
}
程序说明:
用主程序作为“引子”,先后fork()两个子进程,SERVER和CLIENT,让它们利用共享存储区的方式进行通信:
CLIENT端获取一个KEY为75的共享区,当共享区第一个字节的值为-1时,表示SERVER端空闲。这时CLIENT向共享区中填入要发送的数据,同时显示自己发送的数据,然后显示一句“(client)sen t”。
SERVER端获取(如果还没有就创建)一个KEY为75的共享存储区,并将第一个字节置为-1,作为数据空的标志,等待其他进程发来的消息。当该字节的值发生变化时,表示收到了信息,SERVER取得该数据,进行处理,然后将处理结果显示出来,再显示一句“(server)received”。
发送和接收的过程循环进行30次。
父进程在SERVER和CLIENT均退出后结束。
涉及到的系统调用:
1、shmget( )
用于创建(或者获取)一个由key键值指定的共享内存对象,返回该对象的系统标识符:shmid;
系统调用格式:
shmid=shmget(key,size,flag)
该函数使用头文件如下:
#include
#include
#include
参数定义
int shmget(key,size,flag);
key_t key;
int size,flag;
其中,key是共享存储区的名字;size是其大小(以字节计);flag是用户设置的标志,如IPC_CREAT。IPC_CREAT表示若系统中尚无指名的共享存储区,则由核心建立一个共享存储区;若系统中已有共享存储区,便忽略IPC_CREAT。
2、shmat( )
共享存储区的附接。从逻辑上将一个共享存储区附接到进程的虚拟地址空间上。用于建立调用进程与由标识符shmid指定的共享内存对象之间的连接。
系统调用格式:
virtaddr=shmat(shmid,addr,flag)
该函数使用头文件如下:
#include
#include
#include
参数定义
char *shmat(shmid,addr,flag);
int shmid; /*SHM标识符*/
char *addr; /*相当于偏移量*/
int flag; /*标志*/
其中,shmid是共享存储区的标识符;addr是用户给定的,将共享存储区附接到进程的虚地址空间;flag规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。
该系统调用的作用是将共享内存附接到进程的数据段上。实际上是将共享内存在主存中的地址+addr后赋值给进程中的某一指针。addr相当于偏移量,相对于共享内存在主存中的起始地址。返回值是共享存储区所附接到的进程虚地址virtaddr。调用失败时返回(char*)-1。
3、shmdt( )
用于断开调用进程与共享内存对象之间的连接,成功时返回0,失败时返回-1。
系统调用格式:
int shmdt(shmaddr)
char *shmaddr;/*采用shmat函数的返回值*/
4、shmctl( )
共享存储区的控制,对其状态信息进行读取和修改。用于对已创建的共享内存对象进行查询、设置、删除等操作。
系统调用格式:
shmctl(shmid,cmd,buf)
该函数使用头文件如下:
#include
#include
#include
参数定义
int shmctl(shmid,cmd,buf);
int shmid; /*由shmget获取的SHM的标识符*/
int cmd; /*将对SHM进行的控制命令*/
struct shmid_ds *buf; /*存放操作数*/
其中,buf是用户缓冲区地址,cmd是操作命令。命令可分为多种类型:(1)用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符等;
(2)用于设置或改变共享存储区的属性。如共享存储区的许可权、当前连接的进程计数等;
(3)对共享存储区的加锁和解锁命令;
(4)删除共享存储区标识符等。
作用是对共享内存进行由cmd指定的控制操作。cmd的值比较有用的是下面两个:
IPC_SET对于规定的shmid,设置有效用户和组标识及操作权限。
IPC_RMID连同其相关的SHM段数据结构一起,删除规定的shmid。执行IPC_SET或IPC_RMID的进程必须具有Owner/Creator或超级用户的有效用户标识。
系统创建的SHM仅仅是内存中一块连续的区域,本身并没有结构。用户进程对共享内存不能直接进行存取,需要将共享内存附接在进程的数据段上,进程才能对其进行存取,实现方法是:用户进程可以根据需要自行定义一个数据结构(如pidtos),然后将共享内存的地址用函数shmat赋值给指向结构pidtos的指针buf,相当于是给指针变量分配内存,让buf指向共享内存的起始处。然后就可用数组的方法,按数据结构的长度等分共享内存。这个过程可称之为共享内存的"结构化"。
运行程序,观察运行结果,并分析。
四.思考题
进程的高级通信有哪几种类型?
Linux系统编程实验六进程间通信
实验六:进程间通信 实验目的: 学会进程间通信方式:无名管道,有名管道,信号,消息队列, 实验要求: (一)在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道(二)在进程中为SIGBUS注册处理函数,并向该进程发送SIGBUS信号(三)创建一消息队列,实现向队列中存放数据和读取数据 实验器材: 软件:安装了Linux的vmware虚拟机 硬件:PC机一台 实验步骤: (一)无名管道的使用 1、编写实验代码pipe_rw.c #include
//1、子进程先关闭了管道的写端 close(pipe_fd[1]); //2、让父进程先运行,这样父进程先写子进程才有内容读sleep(2); //3、读取管道的读端,并输出数据 if(read(pipe_fd[0],buf_r, r_num)<0) { printf(“read error!”); exit(-1); } printf(“%s\n”,buf_r); //4、关闭管道的读端,并退出 close(pipe_fd[1]); } else if(pid>0) //父进程执行代码 { //1、父进程先关闭了管道的读端 close(pipe_fd[0]); //2、向管道写入字符串数据 p_wbuf=&str1; write(pipe_fd[1],p_wbuf,sizof(p_wbuf)); p_wbuf=&str2; write(pipe_fd[1],p_wbuf,sizof(p_wbuf)); //3、关闭写端,并等待子进程结束后退出 close(pipe_fd[1]); } return 0; } /*********************** #include
实验三 进程通信(一)
操作系统实验报告 实验三、进程通信(一) ——管道及共享内存 一、实验目的 熟悉和掌握LINUX系统的管道通信和共享内存通信。 二、实验内容 (一)、管道通信 1、实验原理: 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统。 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。 利用系统调用pipe()可创建一个简单的管道。 int fd[2]; pipe(fd); 调用成功,fd[0]存放供读进程使用的文件描述符(管道出口),fd[1]存放供写程使用的文件描述符(管道入口)。 一个进程在由pipe()创建管道后,一般再fork()一个子进程,然后通过管道实现父子进程间的通信,子进程将从父进程那里继承所有打开的文件描述符。则父子两个进程都能访问组成管道的两个文件描述符,这样子进程可以向父进程发送消息(或者相反)。 发送进程利用文件系统的系统调用write(fd[1],buf,size),把buf中size个字符送入fd[1],接收进程利用read(fd[0],buf,size),把从fd[0]读出的size个字符置入buf中。这样管道按FIFO(先进先出)方式传送信息。 2、实验内容: #include
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); printf("x value in Parent Process:%d!\n",x); } } (1)阅读上述父子进程利用管道进行通信的例子,写出程序的运行结果并分析。 程序运行x=fork()后,创建里一个子进程,在子进程里x的返回值为0,在父进程里x的返回值为7684,则子进程会执行if语句里的代码段,先关闭了管道的读入端,再在屏幕上输出“Child Process!”,然后将“This is an example\n”存入buf数组中,通过调用write()函数将buf数组的内容从管道的写入端写入,而父进程会执行else语句里的代码段,先关闭了管道的写入端,再在屏幕上输出“Parent Process!”,然后通过调用read ()函数将buf数组的内容从管道的读入端读入冰存储在s数组里,接着通过printf()函数将s数组里的内容输出到屏幕上,最后在屏幕上输出父进程中x的值。 (2)编写程序:父进程利用管道将一字符串交给子进程处理。子进程读字符串,将里面的字符反向后再交给父进程,父进程最后读取并打印反向的字符串。 #include
实验三 进程通信doc(DOC)
实验三进程通信(二) 实验目的: 1、理解进程通信原理和基本技术 2、了解linux系统进程间通信机构(IPC); 3、理解linux关于共享内存的概念; 4、掌握linux支持进程间共享内存的系统调用; 5、巩固进程同步概念。 实验内容: (一)闹钟。用c语言编程,实现进程间通过信号进行通信。 用fork()创建两个进程,子进程在等待5秒后用系统调用kill()向父进程发送SIGALRM 信号,父进程用系统调用signal()捕捉SIGALRM信号。参考程序如下: #include
实验三 进程间通信
实验三进程间通信(2学时) 一、实验目的 (1)了解什么是信号。 (2)熟悉LINUX系统中进程之间软中断通信的基本原理。 (3)熟悉LINUX支持的管道通信方式。 二、实验内容 (1)编写一段程序,使其现实进程的软中断通信。 即:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按 ctrl+c 键);当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止: Child Process11 is killed by Parent! Child Process12 is killed by Parent! 父进程等待两个子进程终止后,输出如下的信息后终止 Parent Process is killed! 要求:运行以下参考程序并分析结果。 <参考程序> #include
signal(SIGALRM,alarming);/*接受SIGALRM*/ waiting(); kill(p1,16); /*向p1发软中断信号16*/ kill(p2,17); /*向p2发软中断信号17*/ wait(0); /*同步*/ wait(0); printf("parent process is killed!\n"); exit(0); //会暂时停止目前进程的执行,直到有信号来到或子进程结束。 } else { wait_mark=1; signal(17,stop); signal(SIGINT,SIG_IGN); /*忽略 ^c信号*/ while (wait_mark!=0); lockf(1,1,0); printf("child process2 is killed by parent!\n"); lockf(1,0,0); exit(0); } } else { wait_mark=1; signal(16,stop); signal(SIGINT,SIG_IGN); /*忽略^c信号*/ while (wait_mark!=0); lockf(1,1,0); printf("child process1 is killed by parent!\n"); lockf(1,0,0); exit(0); } } void waiting() { sleep(5); if (wait_mark!=0) kill(getpid(),SIGALRM); } void alarming()
进程间通信实验报告
进程间通信实验报告 班级:10网工三班学生姓名:谢昊天学号:1215134046 实验目的和要求: Linux系统的进程通信机构 (IPC) 允许在任意进程间大批量地交换数据。本实验的目的是了解和熟悉Linux支持的消息通讯机制及信息量机制。 实验内容与分析设计: (1)消息的创建,发送和接收。 ①使用系统调用msgget (), msgsnd (), msgrev (), 及msgctl () 编制一长度为1k 的消息的发送和接收程序。 ②观察上面的程序,说明控制消息队列系统调用msgctl () 在此起什么作用? (2)共享存储区的创建、附接和段接。 使用系统调用shmget(),shmat(),sgmdt(),shmctl(),编制一个与上述功能相同的程序。(3)比较上述(1),(2)两种消息通信机制中数据传输的时间。 实验步骤与调试过程: 1.消息的创建,发送和接收: (1)先后通过fork( )两个子进程,SERVER和CLIENT进行通信。 (2)在SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER 。SERVER每接收到一个消息后显示一句“(server)received”。 (3)CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后的一个消息,既是 SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(client)sent”。 (4)父进程在 SERVER和 CLIENT均退出后结束。 2.共享存储区的创建,附接和断接: (1)先后通过fork( )两个子进程,SERVER和CLIENT进行通信。 (2)SERVER端建立一个KEY为75的共享区,并将第一个字节置为-1。作为数据空的标志.等待其他进程发来的消息.当该字节的值发生变化时,表示收到了该消息,进行处理.然后再次把它的值设为-1.如果遇到的值为0,则视为结束信号,取消该队列,并退出SERVER.SERVER 每接收到一次数据后显示”(server)received”. (3)CLIENT端建立一个为75的共享区,当共享取得第一个字节为-1时, Server端空闲,可发送请求. CLIENT 随即填入9到0.期间等待Server端再次空闲.进行完这些操作后, CLIENT退出. CLIENT每发送一次数据后显示”(client)sent”. (4)父进程在SERVER和CLIENT均退出后结束。 实验结果: 1.消息的创建,发送和接收: 由 Client 发送两条消息,然后Server接收一条消息。此后Client Server交替发送和接收消息。最后一次接收两条消息。Client 和Server 分别发送和接收了10条消息。message 的传送和控制并不保证完全同步,当一个程序不再激活状态的时候,它完全可能继续睡眠,造成上面现象。在多次send message 后才 receive message.这一点有助于理解消息转送的实现机理。
操作系统实验三(进程通信)
暨南大学本科实验报告专用纸 一、实验目的 学习如何利用管道机制或消息缓冲队列进行进程间的通信,并加深对上述通信机制的理解。提高学生分析问题和解决问题的能力,并学习撰写规范的科学研究报告。 二、实验环境及设备 (一)实验室名称:计算机实验室 (二)主要仪器设备:PC 机、Linux 操作系统环境 三、实验内容 编写一段程序,使用管道来实现父子进程之间的进程通信。子进程项父进程发送自己的进程表示符,以及某字符串。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。 四、实验调试分析 1、实验函数说明 (1)pipe 头文件 :#include
头文件:#include
实验报告三进程管理及进程通信
实验三进程管理及进程通信 实验环境: Linux操作系统 实验目的: (1)利用Linux提供的系统调用设计程序,加深对进程概念的理解。 (2)体会系统进程调度的方法和效果。 (3)了解进程之间的通信方式以及各种通信方式的使用。 实验方法: 用vi 编写c 程序(假定程序文件名为prog1.c)编 译程序 $ gcc -o prog1.o prog1.c 或 $ cc -o prog1.o prog1.c 运行 $./prog1.o 实验内容及步骤: 实验1 编写程序。显示进程的有关标识(进程标识、组标识、用户标识等)。经过5 秒钟后,执行另一个程序,最后按用户指示(如:Y/N)结束操作。 编程截图:
运行结果: 实验2 参考例程1,编写程序。实现父进程创建一个子进程。体会子进程与父进程分 别获得不同返回值,进而执行不同的程序段的方法。 例程1:利用fork()创建子进程 /* 用fork()系统调用创建子进程的例子*/ main() { int i; if (fork()) /*父进程执行的程序段*/ i=wait(); /* 等待子进程结束*/{ printf("It is parent process.\n"); printf("The child process,ID number %d, is finished.\n",i); } else{
Printf(“It is child process.\n”); Sleep(10); Exit(); } } 运行结果: 思考: 子进程是如何产生的?又是如何结束的?子进程被创建后它的运行环境是怎样建立的? 答:是由父进程用fock()函数创建形成的,通过exit()函数自我结束,子进程被创建后核心 将其分配一个进程表项和进程标识符,检查同时运行的进程数目,并且拷贝进程表项的数据,由子进程继承父进程所有文件。 实验3 参考例程2,编写程序。父进程通过循环语句创建若干子进程。探讨进程的家族树 以及子进程继承父进程的资源的关系。 例程2:循环调用fork()创建多个子进程。 /*建立进程树*/ #include
实验三进程通信
实验三进程通信 一.实验学时与类型 学时:2,课外学时:自定 实验类型:设计性实验 二.实验目的 了解Linux的软中断、管道、消息队列、共享存储区等进程间通信方式。 三.实验容 1. 软中断通信机制 (1) 请编写一个程序:循环输出“how are you?”,在按下Ctrl+C后中断显示,输出“Byebye!”后退出程序。 #include
pid=fork(); if(pid>0) { printf(“A\n”); kill(pid,12); } else if(pid==0) { signal(12,func); while(k==1) sleep(1); printf(“B\n”); } } 2. 管道机制 (1) 父子进程通过管道传送一串字符。要求:子进程随机从键盘输入一串字符,通过管道发给父进程,父进程从管道中将消息读出并显示出来。 #include
实验三进程间通信
实验三进程间通信班级: xxx 学号: xxx 姓名: xxx 分数: 1、实验目的: Linux系统的进程通信机构(IPC)允许在任意进程间大批量的交换数据。本实验的目的是了解和熟悉Linux支持的通信机制、共享存储区机制及信号量机制。 2、实验预备内容: 阅读Linux系统的等源码文件,熟悉Linux的三种通信机制。 3、实验内容: (1)消息的创建,发送和接收 (2)使用系统调用msgget(),msgsnd(),msgrev()及msgctl()编制一长度为1k的消息发送和接收程序。 <程序设计> (1)为了便于操作和观察结果,用一个程序作为“引子”,先后fork()两个子进程,SERVER和CLIENT,进行通信。 (2)SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER 每接收到一个消息后显示一句“(server)received”。 (3)CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,即是SERVER端需要的结束信号。CLIENT每发送一条 信息后显示一句“(client)sent”。 (4)父进程在SERVER和CLIENT均退出后结束。 4、源代码及运行截图: #include <> #include
操作系统实验三进程地管道通信
实验三进程的管道通信 一、实验目的: (1)加深对进程概念的理解,明确进程和程序的区别; (2)学习进程创建的过程,进一步认识并发执行的实质; (3)分析进程争用资源的现象,学习解决进程互斥的方法; (4)学习解决进程同步的方法; (5)掌握Linux系统进程间通过管道通信的具体实现方法。 二、实验内容及要求: (1)使用系统调用pipe()建立一条管道线,两个子进程分别向管道写一句话(写的内容自己定,但要有该进程的一些信息); (2)父进程从管道中读出来自两个子进程的消息,显示在屏幕上;(3)要求:父进程首先接收子进程p1发来的消息,然后再接收子进程p2发来的消息; (4)两个子进程要并发执行; (5)实现管道的互斥使用。当一个子进程正在对管道进行写操作时,另一个欲写入管道的子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定; (6)实现父子进程的同步,当父进程试图从一空管道中读取数据时,便进入等待状态,直到子进程将数据写入管道返回后,才将其唤醒。 三、实现: 相关的系统调用
fork() 用于创一个子进程。 格式:int fork(); 返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。 wait() 常用来控制父进程与子进程的同步。 在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,父进程从wait()返回继续执行原来的程序。 返回值:大于0时,为子进程的ID值;等于-1时,调用失败。 exit() 是进程结束时最常调用的。 格式:void exit( int status); 其中,status为进程结束状态。 pipe() 用于创建一个管道 格式:pipe(int fd); 其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型 数组,fd[0]是管道的读端口,用于从管道读出数据,fd[1]是管道的写端口,用于向管道写入数据。 返回值:0 调用成功;-1 调用失败。 sleep() 使调用进程睡眠若干时间,之后唤醒。 格式:sleep(int t); 其中t为睡眠时间。
操作系统上实验报告3
操作系统实验三报告 实验题目: 进程管理及进程通信 实验环境: 虚拟机Linux操作系统 实验目的: 1.利用Linux提供的系统调用设计程序,加深对进程概念的理解。 2.体会系统进程调度的方法和效果。 3.了解进程之间的通信方式以及各种通信方式的使用。
实验内容: 例程1: 利用fork()创建子进程 #include<> #include<> #include<> main() { int i; if (fork()) i=wait(0); /*父进程执行的程序段*/ /* 等待子进程结束*/ printf("It is parent process.\n"); printf("The child process,ID number %d, is finished.\n",i); } else{ printf("It is child process.\n"); sleep(10); /*子进程执行的程序段*/ exit(1); /*向父进程发出结束信号*/ } } 运行结果: 思考:子进程是如何产生的又是如何结束的子进程被创建后它的运行环境是怎样建立的
答:子进程是通过函数fork()创建的,通过exit()函数自我结束的,子进程被创建后核心将为其分配一个进程表项和进程标识符,检查同时运行的进程数目,并且拷贝进程表项的数据,由子进程继承父进程的所有文件。 例程2: 循环调用fork()创建多个子进程 #include<> #include<> #include<> main() { int i,j; printf(“My pid is %d, my father’s p id is %d\n”,getpid() ,getppid()); for(i=0; i<3; i++) if(fork()==0) printf(“%d pid=%d ppid=%d\n”, i,getpid(),getppid()); else { j=wait(0); Printf(“ %d:The chile %d is finished.\n” ,getpid(),j); } } 运行结果:
实验五 进程间通信(二)
湖北工业大学工程技术学院实验报告 课程名称:操作系统实验内容:实验五进程间通信(二) 学院:工程技术学院专业班级:11gb软件2班 教师:贺红艳成绩: 一、实验目的 1、掌握linux系统中进程通信的基本原理。 2、学会使用linux系统中关于进程通信的一些系统调用。 3、掌握信号与共享存储区的使用方法。 二、相关知识背景: (一)信号 1、信号的基本概念 每个信号都对应一个正整数常量(称为signal number,即信号编号。定义在系统头文件
实验三 进程间通信
实验三进程间通信 UNIX/LINUX系统的进程间通信机构(IPC)允许在任意进程间大批量地交换数据。本实验的目的是了解和熟悉LINUX支持的信号量机制、管道机制、消息通信机制及共享存储区机制。 (一)信号量机制实验 【实验目的】 1、了解什么是信号。 2、熟悉LINUX系统中进程之间软中断通信的基本原理。 【实验内容】 本次实验共3部分,前两部分必做。 1、编写一段程序,使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止:Child process 1 is killed by parent! Child process 2 is killed by parent! 父进程等待两个子进程终止后,输出以下信息后终止: Parent process is killed! <参考程序> # include
实验四进程的管道通信实验
实验四进程的管道通信实验 【实验目的】 1、了解什么是管道 2、熟悉UNIX/LINUX支持的管道通信方式 【实验内容】 1、编制一段程序,实现进程的管道通信。使用pipe()建立一条管道线。两个子进程p1和p2分别向管道各写一句话: Child 1 is sending message! Child 2 is sending message! 而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。 <参考程序> # include
lockf(fd[1],0,0); exit(0); } else { wait(0); read(fd[0],InPipe,50); printf(“%s\n”,InPipe); wait(0); read(fd[0],InPipe,50); printf(“%s\n”,InPipe); exit(0); } } } 实验要求:运行程序并分析结果。 2.在父进程中用pipe()建立一条管道线,往管道里写一句话,两个子进程接收这句话。【实验报告】 1、列出调试通过程序的清单,分析运行结果。 2、给出必要的程序设计思路和方法(或列出流程图)。 3、总结上机调试过程中所遇到的问题和解决方法及感想。 【实验相关资料】 一、什么是管道 UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。 所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。 句柄 读出端 句柄 二、管道的类型: 1、有名管道 一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod( )建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以
实验三2_Linux进程间通信
实验三2_Linux进程间通信 实验三 Linux进程间通信 一、实验目的 熟悉Linux下进程间通信机制,能够使用系统提供的各种通信机制实现并发进程间的数据交换。 二、实验题目 分别使用Linux下的共享存储区、消息、管道等通信机制,编程实现并发进程之间的相互通信。 三、背景材料 (一)需要用到的系统调用 实验可能需要用到的主要系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。 fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行; wait() 等待子进程执行完成; getpid() 获取当前进程id; shmget() 建立一个共享存储区; shmctl() 操纵一个共享存储区; shmat() 把一个共享存储区附接到进程内存空间; shmdt() 把一个已经附接的共享存储区从进程内存空间断开; msgget() 建立一个消息队列; msgctl() 操纵一个消息队列; msgsnd() 发送消息; msgrcv() 接收消息;
signal() 设置对信号的处理方式或处理过程; pipe() 打开管道; lockf() 锁定一个文件。 (二)使用共享存储区的示例程序 下面程序主要用来演示共享存储区的使用方法:首先要使用shmget得到共享存储区句柄(可以新建或连接已有的共享存储区,以关键字标识),然后使用shmat挂接到进程的存储空间(这样才能够访问),当使用完后,使用shmctl释放(shmctl 还可以完成一些其他功能)。这种使用逻辑也适用于消息和信号量。示例程序代码如下: #include
实验二,进程通信,管道共享内存
操作系统实验报告 实验二:进程通信(一)——管道及共享存 一、实验目的 ?了解进程之中相互通信的方式 ?加深对管道通信的了解 ?了解共享存通信的程序设计方法 ?了解和熟悉Linux支持的共享存储区机制 二、实验容和步骤 任务一、 (1)阅读以上父子进程利用管道进行通信的例子(例1),写出程序的运行结果并分析。 (2)编写程序:父进程利用管道将一字符串交给子进程处理。子进程读字符串,将里面的字符反向后再交给父进程,父进程最后读取并打印反向的字符串 任务二、 1)阅读例2的程序,运行一次该程序,然后用ipcs命令查看系统中共享存储区的情况,再次执行该程序,再用ipcs命令查看系统中共享存的情况,对两次的结果进行比较,并分析原因。最后用
ipcrm命令删除自己建立的共享存储区。(有关ipcs和ipcrm介绍见后面一页) (2)每个同学登陆两个窗口,先在一个窗口中运行例3程序1(或者只登陆一个窗口,先在该窗口中以后台方式运行程序1),然后在另一个窗口中运行例3程序2,观察程序的运行结果并分析。 运行结束后可以用ctrl+c结束程序1的运行。 (3)编写程序:使用系统调用shmget(),shmat(),shmdt(),shmctl(),编制程序。要求在父进程中生成一个30字节长的私有共享存段。 接下来,设置一个指向共享存段的字符指针,将一串大写字母写入到该指针指向的存贮区。调用fork()生成子进程,让子进程共享存段中的容。接着,将大写字母改成小写,子进显示程修改共享存中的容。之后,子进程将脱接共享存段并退出。父进程在睡眠5秒后,在此显示共享存段中的容(此时已经是小写字母)。 三、代码及运行结果分析 1.任务1(1) ①代码: #include
实验四进程的管道通信实验
实验四进程的管道通信实验 (实验估计学时:2学时) 实验目的 1、了解什么是管道 2、熟悉UNIX/LINUX 支持的管道通信方式 实验内容 编写程序实现进程的管道通信。用系统调用pipe()建立一管道,二个子进程P1和P2分别向管道各写一句话: Child 1 is sending a message! Child 2 is sending a message! 父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。 实验指导 一、什么是管道 UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。 所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者一消费者方 式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。 读出端 写入端 、管道的类型: 1、有名管道 一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod()建立。它 克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样, 需先用open()打开。 2、无名管道 一个临时文件。利用pipe()建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe()的进程及其子孙进程才能识别此文件描述 符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。 二种管道的读写方式是相同的,本文只讲无名管道。 3、pipe文件的建立 分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户 文件描述符 4、读/写进程互斥 内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。 为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的 直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若 是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结 点上锁而睡眠的进程。