1. 管道(Pipes)

    • 单向通信channel
    • 分为匿名管道(用于相关进程)和命名管道(用于无关进程)
  2. 信号(Signals)

    • 用于发送简单消息
    • 例如:SIGTERM, SIGKILL
  3. 消息队列(Message Queues)

    • 允许进程通过发送和接收消息来交换数据
    • 消息存储在队列中,直到接收进程检索它们
  4. 共享内存(Shared Memory)

    • 多个进程共享同一块物理内存
    • 最快的IPC方法,但需要同步机制
  5. 信号量(Semaphores)

    • 用于同步进程操作和访问共享资源
    • 可以防止竞态条件
  6. 套接字(Sockets)

    • 可用于同一机器上的进程间通信,也可用于网络通信
    • 支持全双工通信
  7. 文件(File)

    • 进程可以通过读写同一个文件来交换数据
    • 需要适当的文件锁定机制
  8. 内存映射文件(Memory-mapped Files)

    • 将文件映射到进程的地址空间
    • 可以像访问内存一样访问文件内容
  9. 远程过程调用(RPC)

    • 允许一个进程调用另一个进程(可能在不同的机器上)的过程
    • 常用于分布式系统

背景

为什么需要进程间通信

虚拟内存机制导致了进程只能访问自己的内存,如果想要其他进程的数据,就需要通信。

通信的目的有:同步、互斥、消息传递、资源共享

案例

  1. redis rdb、aof持久化,父子进程之间通信
  2. nginx master和worker/worker之间通信
  3. 界面与插件
  4. 连接不同主机

管道

cat file | grep "main"

信号

$ <Ctrl+c> # 发送SIGINT信号

消息队列

代码来自:https://developer.aliyun.com/article/297029

// sender.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
 
#define MAX_TEXT 512
struct msg_st
{
    long int msg_type;
    char text[MAX_TEXT];
};
 
int main()
{
    int running = 1;
    struct msg_st data;
    char buffer[BUFSIZ];
    int msgid = -1;
 
    //建立消息队列
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(msgid == -1)
    {
        fprintf(stderr, "msgget failed with error: %d\n", errno);
        exit(EXIT_FAILURE);
    }
 
    //向消息队列中写消息,直到写入end
    while(running)
    {
        //输入数据
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        data.msg_type = 1;    //注意2
        strcpy(data.text, buffer);
        //向队列发送数据
        if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
        {
            fprintf(stderr, "msgsnd failed\n");
            exit(EXIT_FAILURE);
        }
        //输入end结束输入
        if(strncmp(buffer, "end", 3) == 0)
            running = 0;
        sleep(1);
    }
    exit(EXIT_SUCCESS);
}
// receiver.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
 
struct msg_st
{
    long int msg_type;
    char text[BUFSIZ];
};
 
int main()
{
    int running = 1;
    int msgid = -1;
    struct msg_st data;
    long int msgtype = 0; //注意1
 
    //建立消息队列
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(msgid == -1)
    {
        fprintf(stderr, "msgget failed with error: %d\n", errno);
        exit(EXIT_FAILURE);
    }
    //从队列中获取消息,直到遇到end消息为止
    while(running)
    {
        if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
        {
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
            exit(EXIT_FAILURE);
        }
        printf("You wrote: %s\n",data.text);
        //遇到end结束
        if(strncmp(data.text, "end", 3) == 0)
            running = 0;
    }
    //删除消息队列
    if(msgctl(msgid, IPC_RMID, 0) == -1)
    {
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

信号量

共享内存

文件

MMAP

RPC

//
// example function to show how to make an RPC call to the coordinator.
//
// the RPC argument and reply types are defined in rpc.go.
//
func CallExample() {
 
	// declare an argument structure.
	args := ExampleArgs{}
 
	// fill in the argument(s).
	args.X = 99
 
	// declare a reply structure.
	reply := ExampleReply{}
 
	// send the RPC request, wait for the reply.
	// the "Coordinator.Example" tells the
	// receiving server that we'd like to call
	// the Example() method of struct Coordinator.
	ok := call("Coordinator.Example", &args, &reply)
	if ok {
		// reply.Y should be 100.
		fmt.Printf("reply.Y %v\n", reply.Y)
	} else {
		fmt.Printf("call failed!\n")
	}
}