【Linux】进程间通信(IPC)

【Linux】进程间通信(IPC)

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

进程间通信的目的

数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止
时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态。
进程间通信分类
管道:匿名管道pipe,命名管道
System V IPC:System V 消息队列、System V 共享内存、System V 信号量
POSIX IPC:消息队列、共享内存、信号量、互斥量、条件变量、读写锁

管道

概念:管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
匿名管道

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

在这里插入图片描述
关闭管道只需将两个文件描述符关闭
用fork来共享管道
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
fork之后的半双工管道 从父进程到子进程的管道
在这里插入图片描述
若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。

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

int main()
{
    int fd[2];  // 两个文件描述符
    pid_t pid;
    char buff[20];

    if(pipe(fd) < 0)  // 创建管道
        printf("Create Pipe Error!\n");

    if((pid = fork()) < 0)  // 创建子进程
        printf("Fork Error!\n");
    else if(pid > 0)  // 父进程
    {
        close(fd[0]); // 关闭读端
        write(fd[1], "hello world\n", 12);
    }
    else
    {
        close(fd[1]); // 关闭写端
        read(fd[0], buff, 20);
        printf("%s", buff);
    }

    return 0;
}

在这里插入图片描述
管道读写规则
当没有数据可读时
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
当管道满的时候
O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程
退出
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性

命名管道的打开规则

命名管道的打开规则
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
例子-用命名管道实现server&client通信
客户端client:

 #include<stdio.h>
   #include<unistd.h>
   #include<sys/types.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #include<string.h>
   #define FIFO "fifo"
   int main()
  {
      int fd = open(FIFO, O_WRONLY);
      if(fd < 0)
      {
          perror("open error");
          return 1;
      }
  
      char buf[1024];
      while(1)
     {
          printf("eoch# ");
          fflush(stdout);
          ssize_t s = read(0, buf, sizeof(buf)-1);
          if(s>0)
          {
              buf[s] = 0;
              write(fd, buf, strlen(buf));
          }
      }
      close(fd);
      return 0;
  }

服务器端sreve:

 #include<stdio.h>
   #include<unistd.h>
   #include<fcntl.h>
   #include<sys/types.h>
  #include<sys/stat.h>
   #define FIFO "fifo"
   
   int main()
   {
      if(mkfifo(FIFO, 0644)<0)
      {
          perror("mkfifo error");
          return 1;
      }
  
      int fd = open(FIFO, O_RDONLY);
      if(fd < 0)
      {
          perror("open error");
          return 2;
      }
  
      char buf[1024];
      while(1)
      {
          ssize_t s = read(fd, buf, sizeof(buf)-1);
          if(s > 0)
          {
              buf[s] = 0;
              printf("proc_two: %s\n", buf);
          }
          else if(s==0)
          {
              printf("proc_two quit, me too...");
              break;
          }
          else
          {
              break;
          }
      }
      close(fd);
      return 0;
  }

makefile
all为所有目标
“.PHONY”为所有依赖关系

$@:表示目标文件;一般是在规则中这么用:gcc -o $@ $(object);

$^:表示所有依赖文件;一般是在规则中这么用:gcc -o $@ $^ ;用所有依赖文件链接成目的文件;

$<:表示第一个依赖文件;在规则中使用:gcc -o $@ -c $< ;其实这个时候就是每个依赖文件生成一个目的文件;
在这里插入图片描述
运行截图:
client:
在这里插入图片描述
server:
在这里插入图片描述

system V共享(共享内存)

我们了解其概念即可
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递

发布了15 篇原创文章 · 获赞 42 · 访问量 4486

猜你喜欢

转载自blog.csdn.net/qq_43676757/article/details/104964617