Linux下进程间通信的方式


在操作系统中,多进程之间常见的通信方式有以下几种:

管道(Pipe):分为匿名管道和命名管道。匿名管道只能在具有亲缘关系(父子进程或兄弟进程)的进程间使用,而命名管道可以在不相关的进程间使用。

消息队列(Message Queue):消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存(Shared Memory):多个进程可以访问同一块物理内存区域,这是最快的一种进程间通信方式,但需要进程自己进行同步和互斥控制。

信号量(Semaphore):主要用于进程间以及同一进程内不同线程之间的同步和互斥。

套接字(Socket):可用于不同主机上的进程间通信,也可用于同一主机上的进程间通信。

🚀 匿名管道 (Linux/MacOS)

#include <unistd.h>
#include <iostream>

int main() {
    int fd;
    pipe(fd);  // 创建管道

    if (fork() == 0) {      // 子进程(读端)
        close(fd);       // 关闭写端
        char buf[100];
        read(fd, buf, sizeof(buf));
        std::cout << "Child read: " << buf << std::endl;
    } else {                // 父进程(写端)
        close(fd);       // 关闭读端
        const char* msg = "Hello via pipe!";
        write(fd, msg, strlen(msg)+1);
        wait(nullptr);      // 等待子进程结束
    }
    return 0;
}

🔍 命名管道跨进程通信

写入端 (writer.cpp)

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

int main() {
    mkfifo("/tmp/myfifo", 0666);  // 创建命名管道
    int fd = open("/tmp/myfifo", O_WRONLY);
    write(fd, "Data from writer", 16);
    close(fd);
}

读取端 (reader.cpp)

#include <iostream>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("/tmp/myfifo", O_RDONLY);
    char buf[100];
    read(fd, buf, sizeof(buf));
    std::cout << "Read from FIFO: " << buf << std::endl;
    close(fd);
}

📨 消息队列示例

#include <sys/ipc.h>
#include <sys/msg.h>
#include <iostream>

struct msg_buffer {
    long msg_type;
    char msg_text[100];
} message;

int main() {
    key_t key = ftok("msgqfile", 65);
    int msgid = msgget(key, 0666 | IPC_CREAT);
    
    if (fork() == 0) {  // 子进程发送
        message.msg_type = 1;
        strcpy(message.msg_text, "Queue Message");
        msgsnd(msgid, &message, sizeof(message), 0);
    } else {            // 父进程接收
        msgrcv(msgid, &message, sizeof(message), 1, 0);
        std::cout << "Received: " << message.msg_text << std::endl;
        msgctl(msgid, IPC_RMID, NULL);  // 清理队列
    }
    return 0;
}

💾 共享内存同步版

#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>

int main() {
    const int SIZE = 4096;
    int* shared = (int*)mmap(NULL, SIZE, PROT_READ | PROT_WRITE, 
                            MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    if (fork() == 0) {      // 子进程写入
        *shared = 2025;
        std::cout << "Child wrote: " << *shared << std::endl;
    } else {                // 父进程读取
        wait(nullptr);
        std::cout << "Parent read: " << *shared << std::endl;
        munmap(shared, SIZE);
    }
    return 0;
}

⚖️ 信号量同步示例

#include <sys/sem.h>
#include <unistd.h>
#include <iostream>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int main() {
    key_t key = ftok("semfile", 65);
    int semid = semget(key, 1, 0666 | IPC_CREAT);
    
    union semun arg;
    arg.val = 1;  // 初始值
    semctl(semid, 0, SETVAL, arg);

    struct sembuf op = {0, -1, 0};  // P操作
    
    if (fork() == 0) {  // 子进程
        semop(semid, &op, 1);
        std::cout << "Child entered critical section\n";
        sleep(2);
        op.sem_op = 1;  // V操作
        semop(semid, &op, 1);
    } else {            // 父进程
        semop(semid, &op, 1);
        std::cout << "Parent entered critical section\n";
        sleep(2);
        op.sem_op = 1;
        semop(semid, &op, 1);
        semctl(semid, 0, IPC_RMID);  // 删除信号量
    }
    return 0;
}

🌐 TCP 套接字通信

服务端 (server.cpp)

#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr{AF_INET, htons(8080), INADDR_ANY};
    
    bind(server_fd, (sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 5);
    
    int client_fd = accept(server_fd, nullptr, nullptr);
    char buf[100];
    recv(client_fd, buf, sizeof(buf), 0);
    std::cout << "Server received: " << buf << std::endl;
    send(client_fd, "ACK", 4, 0);
    close(server_fd);
}

客户端 (client.cpp)

#include <sys/socket.h>
#include <arpa/inet.h>

int main() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr{AF_INET, htons(8080), inet_addr("127.0.0.1")};
    
    connect(sock, (sockaddr*)&addr, sizeof(addr));
    send(sock, "Hello TCP!", 11, 0);
    char ack;
    recv(sock, ack, sizeof(ack), 0);
    close(sock);
}

🔧 编译运行说明

匿名管道/共享内存/信号量

g++ -std=c++11 demo.cpp -o demo &&./demo

命名管道(需分开编译)

g++ writer.cpp -o writer && g++ reader.cpp -o reader
./writer &./reader

TCP 套接字(需双终端)

g++ server.cpp -o server && g++ client.cpp -o client
./server  # 终端 1
./client  # 终端 2

进程间通信方式详解

匿名管道

匿名管道用于有亲缘关系的进程,比如父子进程。需要用 pipe() 函数创建管道,父进程写数据,子进程读数据。记得关闭不用的端,比如父进程关闭读端,子进程关闭写端。要注意错误处理和正确使用 fork()

命名管道

命名管道可以在不相关进程间使用,所以需要创建 FIFO 文件。使用 mkfifo() 函数,然后一个进程以写方式打开,另一个以读方式打开。要注意打开模式,比如 O_WRONLYO_RDONLY ,以及可能的阻塞问题。

消息队列

使用 msgget() 创建或获取队列,msgsnd() 发送消息,msgrcv() 接收。结构体需要包含长整型类型字段,消息内容可以是字符串。注意权限设置和错误检查。

共享内存

需要 shmget() 创建共享内存段,shmat() 附加到进程地址空间。写入和读取数据后要分离,最后删除共享内存。同时,可能需要用信号量来同步。

信号量

使用 sem_open() 创建或打开信号量,sem_wait()sem_post() 进行 P/V 操作。示例可以展示进程间的同步,比如父进程和子进程交替执行。

套接字

可以用于不同主机或同一主机。这里用本地套接字,即 AF_UNIX 。服务器端创建 socketbindlistenaccept ,客户端 connect 后发送数据。注意地址结构体和关闭套接字。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值