Linux进程间通信1


前言

进程之间可能会存在特定的协同工作的场景,而协同就必须要进行进程间通信,协同工作可能有以下场景

数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事件。
·进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的
所有陷入和异常,并能够及时知道它的状态改变

那如何实现进程间通信呢?
由于一个进程是不能访问到另一个进程的资源的,即进程之前是具有独立性的。

那么进程之间要通信,就不能使用属于进程的资源,而应该使用一份公共的资源。

所以进程间通信的本质是:由OS参与,提供一份所以进程都能访问的公共资源。

而公共资源是什么,例如:文件、队列、内存块。


管道

管道,又称为“匿名管道”,是UNIX系统IPC(进程间通信)最古老的形式。
特点:
1.它是半双工的(即同一时间数据只能在一个方向上流动),具有固定的读端和写端。
2.它只能用于具有亲缘关系的进程之间的通信(父子进程或者兄弟进程)。
3.它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不 是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

在这里插入图片描述
管道,管道这个名字就好比如这个东西就是一个水管,水管有两个头,决定了一边进,那么也就决定了另外一边出,就比如一个父进程从管道的一端写入,子进程从另外一端读出。这就是一个简单的运用了。父子都是从同一个端口写,都是从同一个端口读,所以运用过程中,我们规划好了谁写谁读后,就需要关闭另外一个,就比如决定了父进程写,就要关闭父进程的读端口。
制造管道函数原型:
在这里插入图片描述
参数:参数为一个数组大小可以储存2个int的指针
返回值:创建成功返回0,失败返回-1
使用须知:pipefd[0]是为读打开的,pipefd[1]是为写打开的。



使用函数小demo:

#include <unistd.h>  
#include <stdio.h>
#include <string.h>

int main()
{
   int fd[2];
   int pid;
   char buf[128];//存放数据

   if(pipe(fd) == -1)//假如创建失败
   {
      printf("creat pipe error\n");
   }

   pid = fork();//创建子进程
   
   if(pid > 0)//父进程
   {
      close(fd[0]);//关闭读端
      write(fd[1],"hello from father\n",strlen("hello from father\n"));
   }
   else//子进程
   {
      close(fd[1]);//关闭写端
      read(fd[0],buf,128);
      printf("read from father: %s\n",buf);
   }

   return 0;
}

运行结果:
在这里插入图片描述
管道读写的特性:
由于fork后我们不知道是子进程先执行还是父进程先执行,如果是子进程先运行,执行到read的时候,由于管道没有数据,所以子进程会阻塞,直到管道中有数据了才会读取。

命名管道 / FIFO

FIFO,也称为命名管道,FIFO文件。
特点:
1.FIFO可以在无关的进程之间交换数据,与无名管道不同。
2.FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。


一旦创建了一个FIFO,就可以用一般的文件I/O函数操作它。它和无名管道十分类似,只不过它是文件,往文件里写就像往管道里写一样。
函数原型:

#include <sys/stat.h>
 
int mkfifo(const char *pathname, mode_t mode);

参数:
用mkfifo函数就能创建一个FIFO文件(有名管道),mode参数与open函数中的mode相同。mode记录待创建文件的访问权限,0600表示可读可写的文件。

当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
·若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO,只写 open 要阻塞到某个其他进程为读而打开它。
·若指定了O_NONBLOCK,则只读 open 立即返回,而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。
一般都不设置O_NONBLOCK,因为都希望写入数据后再读,不然意义不大。

返回值:创建成功返回0,失败返回-1


运用函数小demo:
read.c:

#include <unistd.h>  
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>


int main()
{
   char buf[128] = {0}; 
   if((mkfifo("./pipe",0600) == -1) && errno != EEXIST)//创建失败或者已经存在
   {
      printf("mkfifo failed\n");
      perror("why");//打印失败原因
   }
   
   int fd = open("./pipe",O_RDONLY);//以只读方式打开FIFO,这里如果管道没有信息会阻塞到有信息

   read(fd,buf,20);//读取
   printf("read buf: %s\n",buf);

   close(fd);//关闭FIFO


   return 0;
}

write:

#include <unistd.h>  
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>


int main()
{
   char *str = "hello\n";
   int fd = open("./pipe",O_WRONLY);//以只写方式打开FIFO

   write(fd,str,strlen(str));//写入数据

   close(fd);//关闭FIFO


   return 0;
}

运行结果:
这里先运行read.c看是否他会阻塞到有数据写入
在这里插入图片描述
看到程序一直卡在一个地方,然后在运行write.c
在这里插入图片描述
现象已经表明,read函数会一直阻塞到有数据写入。

消息队列

消息队列,是消息的链表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

特点:
1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
2.消息队列独立于发送和接受进程。进程终止时,消息队列及其内容并不会被删除。
3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。


函数原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
 
int msgget(key_t key, int msgflq);

参数:
Key:大于0的32位整数,就是每个消息的“名字”,也可以理解为“密码”。
msgglq: msgflq为参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定消息队列的存取权限,一般传入IPC_CREAT,表示:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符(后续代码示例传入IPC_CREAT|0777,表示创建可读可写可执行的消息队列)

返回值:成功返回消息队列的标识符,出错-1,原因储存在error中


其次要介绍信息接收以及发送函数:
首先要认识一个结构体的组成:

#include <stdio.h>
 
struct msgbuf{
    long mtype;//信息的“名字”,必须大于0
    char mtext[自定义大小];//信息包含的数据
};

接收函数:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
 
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

发送函数:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
 
int msgsnd(int msgid, const void *msgp, ssize_t msgsz, int msgflq);

参数:(两个函数的参数差不多都是一样的)


区别就是发送函数少了一个msgtyp


msgid:消息队列的标识符
*msgp:传入msgbuf结构体类型的地址
msgsz:收取消息用sizeof计算缓存区的大小
msgtyp:消息的“名字”,发送消息时定义
msgflq:通常为0,即阻塞的方式


运用函数小demo:
send:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
 
struct msgbuf{
	long mtype;
	char mtext[128];
};
 
int main()
{
	struct msgbuf sendbuf={888,"message from quen\n"};//消息的“名字”为888
 
	int msgid=msgget(0x1234,IPC_CREAT|0777);//获取消息的标识符,消息队列的“名字”为0x1234
 
	msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),8880);//计算要发送的数据的大小,所以传入数组名即可
	return 0;
}

read:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
 
struct msgbuf{
	long mtype;
	char mtext[128];
};
 
int main()
{
	struct msgbuf readbuf;//存储接受到的消息
 
	int msgid=msgget(0x1234,IPC_CREAT|0777);//获取消息队列的标识符,队列“名字”同样为0x1234
 
	msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),0);//计算接受到的数据用sizeof
	printf("read from que: %s\n",readbuf.mtext);
 
	return 0;
}

运行结果:
在这里插入图片描述

  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值