进程间通信

一.进程间通信介绍

进程运行是具有独立性的,所以想让其进行通信是一件很困难的事情。
想要进程实现通信,就必须让不同的进程看到同一份资源(指的就是某一块内存)。
1.进程间通信目的

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

2.进程间通信分类

  • (1)管道
    匿名管道pipe 命名管道
  • (2)System V IPC
    System V 消息队列 System V 共享内存 System V 信号量
  • (3)POSIX IPC
    消息队列 共享内存 信号量 互斥量 条件变量 读写锁
二.管道

1.概念:
管道是Linux进程间通信的一种形式,我们把把一个进程连接到另一个进程之间的数据流称为管道。

2.匿名管道
(1)是基于文件进行进程间通信的
(2)用于两个拥有血缘关系的进程之间,常用于父子进程。(为什么)
必须让父子进程之间看到同一份资源。

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码。

//父子进程之间进行读写的通信管道,进程之间有联系,有同一份资源。
  1 #include<iostream>
  2 #include<unistd.h>
  3 #include<stdio.h>
  4 #include<string.h>
  5 #include<sys/wait.h>
  6 #include<sys/types.h>
  7 #include<stdlib.h>
  8 
  9 int main()
 10 {
 11     //数组只能被整体初始化,不能被整体赋值。
 12     int fd[2] = {0};
 13 
 14     pipe(fd);
 15 
 16     pid_t id = fork();
 17     if(id == 0)
 18     {
 19         //child;w
 20         close(fd[0]);
 21 
 22         char message[] = "hello ....pipe ....";
 23         while(1)
 24         {
 25             write(fd[1], message, strlen(message));
 26             sleep(1);
 27         }
 28         exit(0);
 29     }
 30     else
 31     {
 32         //father;r
 33         close(fd[1]);
 34 
 35         char buf[1024];
 36         while(1)
 37         {
 38             ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
 39 
 40 
 41             if(s > 0)
 42             {
 43                 buf[s] = 0;
 44                 printf("I am father, got child message %s\n",buf);
 45             }
 46             else if(s == 0)
 47             {
 48                 printf("read file end\n");
 49                 break;
 50             }
 51             else
 52             {
 53                 printf("read error\n");
 54                 break;
 55             }
 56         }
 57         waitpid(id ,NULL, 0);
 58     }
 59     return 0;
 60 }

多进程共享的内存资源 -----》临界资源,
将访问临界资源的代码叫临界区
任何时候,只允许由一个人访问临界资源,这种叫互斥。
在保证临界资源安全的前提下(通常是互斥),让多进程访问临界资源具有一定的顺序性,我们称之为同步(协调进程步调,避免饥饿问题)。
原子性:通常对临界资源的操作,要么不访问,要么访问完,1条汇编语句完成的叫原子性。

3.管道的读写规则

  • (1)当没有数据可读时
    O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

  • (2)当管道满的时候
    O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
    O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

  • (3)如果所有管道写端对应的文件描述符被关闭,则read返回0

  • (4)如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出

  • (5)当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。

  • (6)当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

4.管道的特点

  • (1)只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创 建,然后该进程调用fork,此后父、子进程之间就可应用该管道。

  • (2) 管道提供流式服务

  • (3)一般而言,进程退出,管道释放,所以管道的生命周期随进程

  • (4)一般而言,内核会对管道操作进行同步与互斥

  • (5)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

5.命名管道

  • (1)管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • (2) 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • (3) 命名管道是一种特殊类型的文件

6.创建一个命名管道

  • (1)命名管道可以从命令行上进行创建,命令行方法是使用下面这个命令:

mkfifo filename

  • (2)命名管道也可以从程序中创建,相关函数有:

int mkfifo(const char** filename,mode_t mode)//第一个参数是文件,第二个参数是权限

  • (3)创建命名管道
int main(int argc, char* argv[])
{
	mkfifo("p2",0644);
	return 0;
}

7.匿名管道和命名管道的区别

  • (1)匿名管道由pipe函数创建并打开。
  • (2)命名管道 由mkfifo函数创建,打开用open
  • (3)FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

8.命名管道的打开规则

  • (1)如果当前打开操作是为读而打开FIFO时
    O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    O_NONBLOCK enable:立刻返回成功
  • (2)如果当前打开操作是为写而打开FIFO时
    O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    O_NONBLOCK enable:立刻返回失败,错误码为ENXI

9.用命名管道实现server —》 client通信

//Server
  1 #include<iostream>
  2 #include<unistd.h>
  3 #include<stdio.h>
  4 #include<sys/types.h>
  5 #include<sys/stat.h>
  6 
  7 #include<fcntl.h>
  8 int main()
  9 {
 10     mkfifo("fifo", 0644);
 11     int fd = open("fifo",O_RDONLY);
 12     if(fd < 0)
 13     {
 14         printf("open error\n");
 15         return 1;
 16     }
 17 
 18     char buf[1024];
 19     while(1)
 20     {
 21         ssize_t s = read(fd,buf,sizeof(buf) - 1);
 22         if(s > 0)
 23         {
 24             buf[s] = 0;
 25             printf("clien --> server%s\n", buf);
 26         }
 27         else if(s == 0)
 28         {
 29             printf("client quit\n");
 30             break;
 31         }
 32         else
 33         {
 34             printf("read error!\n");
 35             break;
 36         }
 37     }
 38     close(fd);
 39     return 0;
 40 }
//client
 1 #include<iostream>
  2 #include<unistd.h>
  3 #include<stdio.h>
  4 #include<sys/types.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 #include<string.h>
  8 int main()
  9 {
 10     int fd = open("fifo",O_WRONLY);
 11     if(fd < 0)
 12     {
 13         printf("open error\n");
 14         return 1;
 15     }
 16 
 17     char buf[1024];
 18     while(1)
 19     {
 20         printf("Please enter your message: ");
 21         fflush(stdout);
 22         ssize_t s = read(0,buf,sizeof(buf) - 1);
 23         if(s > 0)
 24         {
 25             buf[s - 1] = 0;
 26             write(fd,buf,strlen(buf));
 27         }
 28         else if(s == 0)
 29         {
 30             printf("client quit\n");
 31             break;
 32         }
 33         else
 34         {
 35             printf("read error!\n");
 36             break;
 37         }
 38     }
 39     close(fd);
 40     return 0;
 41 }

//用do 。。。while(0)包含的代码是一段代码,如果没有这句话的话,代码就会分离。
#define ERR_EXIT(m) \    
do \    
{ \        
	perror(m); \        
	exit(EXIT_FAILURE); \    
} while(0)
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值