进程高级通讯

管道函数

原理

#include “unistd.h”

int pipe(int p[2])          返回:成功时为0,出错时为-1。

      该文件返回两个文件描述字:p[0]—打开读,p[1]—打开写.管道的读写和文件的读写一样。例:

 具体实现

        要求,编写一段程序,实现进程的管道通信。使用系统调用pipe()建立一条管道线,两个子进程P1和P2分别向管道各写一句话:

  child 1 is sending a message!

  child 2 is sending a message!

        而父进程则从管道中读出来自两个子进程的信息,并显示子进程号,显示在屏幕上。  

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include<stdlib.h>
int pid1, pid2;
int main() {
	int fd[2];
	int pid1, pid2; //存储进程id
	char OutPipe[100], InPipe[100];  //用于存储发送和接受消息

	pipe(fd);  //创建一个管道,通过fd返回两个文件描述符,fd[0]用于读取数据,fd[1]用于写入数据。

	while ((pid1 = fork()) == -1);   //循环创建子进程1,直到成功创建为止
	if (pid1 == 0) {
//子进程1的代码块。输出进程1的ID。使用 lockf 锁定写入管道的操作,确保数据发送的原子性。将消息写入管道。等待5秒钟。解锁管道。退出子进程1。
		printf("child process1 %d\n", getpid());
		lockf(fd[1], 1, 0);
		sprintf(OutPipe, "child1 is sending a message!");
		write(fd[1], OutPipe, 50);
		sleep(5);
		lockf(fd[1], 0, 0);
		exit(0);
	}
//父进程和子进程2的创建代码块。创建子进程2,与子进程1类似。输出父进程的ID。等待子进程1结束之后。从管道中读取数据到 InPipe。打印读取到的消息。等待子进程2结束。再次从管道中读取数据到 InPipe。打印读取到的消息。退出父进程。
	else {
		while ((pid2 = fork()) == -1);
		if (pid2 == 0) {
			printf("child process2 % d\n", getpid());
			lockf(fd[1], 1, 0);
			sprintf(OutPipe, "child2 is sending a message!");
			write(fd[1], OutPipe, 50);
			sleep(5);
			lockf(fd[1], 0, 0);
			exit(0);
		}
		else {
			printf("parent process %d\n", getpid());
			wait(0);
			read(fd[0], InPipe, 50);
			printf("%s\n", InPipe);
			wait(0);
			read(fd[0], InPipe, 50);
			printf("%s\n", InPipe);
			exit(0);
		}
	}
}


 

 消息交换

原理

共享系统调用函数

(1)创建/获取内存区:

        int shmget(key_t key,int size,int shmflag);

        key—关键字,必须是本地唯一的。

        size—共享内存大小(字节),内核会把这个数值向上舍入取最近的虚拟内存帧的大小。

        shnflg—标志参数,指定选项及其权限位。

        IPC_CREAT—创建新的信号量集

        IPC_EXCEL—如果信号量集已经存在,则返回错误。

        <XXX XXX XXX>-- 和文件、目录一样权限。

        返回一个共享内存ID--shmid

        语法格式:shmid=shmget(key,size,flag)

(2)连接共享内存:

        把内存变量和共享内存连接起来,便于使用共享内存。

        char *shmat(int shmid,char *shmaddr,int shmflag);

        shmid—由shmget函数产生的共享内存ID;

        shmaddr—字符型指针变量;

        shmflg—共享内存标志参数,含义同上。

        语法格式:virtaddr=shmat(id,addr,flag)

        其中:id是共享存储区的标识符,addr是用户要使用共享存储区附接的虚地址,若addr是0,系统选择一个适当的地址来附接该共享区。flag规定对此区的读写权限,以及系统是否应对用户规定的地址做舍入操作。如果flag中设置了shm_rnd即表示操作系统在必要时舍去这个地址。如果设置了shm_rdonly,既表示只允许读操作。viraddr是附接的虚地址。

(3)分离共享内存

        把内存指针变量和共享内存分离。

        int shmdt(char *shmaddr);

        shmaddr—内存指针变量;

        其中,当调用成功时,返回0值,调用不成功,返回-1,addr是系统调用shmat所返回的地址。

(4)释放共享内存:

        int shmctl(int shmid,int cmd,struct shmid_ds *buf);

        其中:调用成功时返回0,否则返回-1。shmid为被共享存储区的标识符。cmd规定操作的类型。规定如下:

        IPC_STAT: 返回包含在制定的shmid相关数据结构中的状态信息,并且把它放置在用户存储区中的*buf指针所指的数据结构中。执行此命令的进程必须有读取允许权。

        IPC_SET:  对于指定的shmid,为它设置有效用户和小组标识和操作存取权。

        IPC_RMID  删除制定的shmid以及与它相关的共享存储区的数据结构。

        SHM_LOCK: 在内存中锁定指定的共享存储区,必须是超级用户才可以进行此操作。

        BUF 是一个用户级数据结构地址。

例子:

 消息队列调用函数

(1) msgget函数用于创建一个新的消息队列或访问一个已存在的消息队列

        int msgget(key_t key,int msgflag);

        key:指定为常值IPC_PRIVATE或系统中唯一的关键字值。

        msgflag:  IPC_CREAT—创建消息队列

        IPC_EXCL—检查消息队列是否存在<权限>。

        返回:成功时为非负标识符,出错时为一1。返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的。

(2)使用msgget打开一个消息队列后,我们使用msgsnd往其上放置一个消息。

        Int msgsnd(int msgid,struct msgbuf *msgf,size_t nbytes,int flag);

        其中:msgid—由msgget()创建函数返回。

      *msgf—是一个结构指针,该结构具有如下的模板,

      struct msgbuf{

         long mtype;/*消息的类型*/

         char mtext[1];/*消息的数据*/

                    };

        在实际应用中,1个字节的消息是没有任何用途的,因此,用户可以根据自己的需要定义消息结构的内容。但:

        第一成员必须是long mtype;   必须进行强制类型转换: (struct msgbuf *)

     nbytes—实际消息块的大小。这是位于长整数消息类型之后的用户自定义数据的长度sizeof(message)-sizeof(long)。

         flag—选项标志位:可以是0,也可以是IPC-NOWAIT0—  不使用标志,

        IPC-NOWAIT—表示当消息队列满时不等待,返回错误信息。

(3)使用msgrcv函数从某个消息队列中读出一个消息。

        int msgrcv(int msgid,struct msgbuf *msgp,size_t nbytes,long type,int flag);

        msgid—消息队列编号;

        msgp—存放消息的内容;

        nbyte—为信息数据的长度,即mtext参数的长度。

        type—接受消息的类型;type =0返回队列内第一项信息;type>0返回队列内第一项与mtype相同的信息type<0返回队列内第一项mtype小于或等于mtype绝对值的信息。

         flag—接受选项标志;0或IPC-NOWAIT

         IPC-NOWAIT—表示没有消息不等待,返回错误

         0—等待消息到来,进程挂起。

(4)删除消息队列:

        int msgctl(int msgid,int cmd,struct msgid_ds *buf);

        msgctl()提供了几种方式来控制信息队列的运作。参数msgid为欲处理的信息队列识别代码,参数cmd为欲控制的操作,有下列几种数值:

        IPC__STAT    把信息队列的msqid—ds结构数据复制到参数buf.

    IPC__SET    将参数buf所指的msqid—ds结构中的msqid.uid、msg_perm.gtd、msg_perm.mode和msg_qbytes参数复制到信息队列的msqid—ds结构内。

        IPC__RMID    删除信息队列和其数据结构。

范例:

具体实现

 客户端函数

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define MSGKEY 75  //定义消息队列的键值。
struct msgform{  //定义了一个结构体,包含消息类型 mtype 和消息内容 mtext。
    long mtype;
    char mtext[1000];
}msg;
int msgqid;  //消息队列标识符
void client(){
//打开或创建一个消息队列,使用 msgget 函数。如果队列已存在,则会返回其标识符;否则将创建一个新的消息队列。
//使用循环向消息队列发送10条消息,每次设置不同的消息类型 mtype,然后调用 msgsnd 向消息队列中发送消息。
    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);
}
int main(){  //调用 client() 函数,启动消息发送的过程。
    client();

    return 0;
}

服务端函数

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define MSGKEY 75
struct msgform{
    long mtype;
    char mtext[1000];
}msg;
int msgqid;
void server(){
//创建一个消息队列,使用 msgget 函数,如果队列已存在,则返回其标识符;否则创建一个新的消息队列。
//使用循环接收消息队列中的消息,调用 msgrcv 函数。这里的参数 1030 表示接收消息的最大长度。
//每次接收到消息后,打印 "(server)received"。
//当接收到的消息类型 msg.mtype 等于 1 时,退出循环。
//最后调用 msgctl 函数删除消息队列并归还资源,使用参数 IPC_RMID。
    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);
}
int main(){
    server();
    
    return 0;
}

 

实验小结

        通过本次实验,我了解到管道是一种常见的进程间通信方式,特别适合于具有父子关系的进程间通信。在实验中,我们可以创建匿名管道或命名管道,并通过读写管道来实现进程间通信。

        共享内存允许多个进程访问同一块物理内存,提供了高效的进程间通信方式。在实验中,你可以创建共享内存段并将其映射到不同进程的地址空间中,从而实现进程间数据共享和通信。同时也要注意避免由于共享内存带来的竞争条件和同步问题,确保数据的一致性和完整性。

        消息队列提供了一种异步的进程间通信方式,允许进程通过发送和接收消息来实现数据交换。在实验中,我们创建消息队列,并通过消息发送和接收来实现进程间通信。注意消息队列的消息类型、大小限制以及错误处理等方面的注意事项。

        通过本次实验,我更深入地理解操作系统中进程间通信的原理和实践,为后续的系统编程和开发打下坚实的基础。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值