0xlavon 发表于 2024-5-22 12:48:21

Linux进程间通信

本帖最后由 0xlavon 于 2024-5-22 12:52 编辑

在Linux操作系统中,进程间通信(Inter-Process Communication, IPC)是进程之间传递数据或信号的机制。IPC在多进程编程中至关重要,适用于资源共享、数据同步、事件通知等场景。本文将介绍几种常见的Linux进程间通信方式,并给出相应的代码示例。

1. 管道(Pipes)

管道是一种最简单的进程间通信方式,主要用于父子进程之间的数据传输。

1.1 匿名管道(Unnamed Pipes)

匿名管道只能用于具有亲缘关系的进程之间,即父进程与子进程之间。

1.1.1 创建与使用

匿名管道通过`pipe()`系统调用创建:


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

int main() {
    int pipefd;
    char buffer;
    pid_t cpid;

    if (pipe(pipefd) == -1) {
      perror("pipe");
      exit(EXIT_FAILURE);
    }

    cpid = fork();
    if (cpid == -1) {
      perror("fork");
      exit(EXIT_FAILURE);
    }

    if (cpid == 0) {                /* 子进程 */
      close(pipefd);          /* 关闭写端 */
      read(pipefd, buffer, sizeof(buffer));
      printf("子进程接收到的数据: %s\n", buffer);
      close(pipefd);
    } else {                     /* 父进程 */
      close(pipefd);          /* 关闭读端 */
      write(pipefd, "Hello, child process", 21);
      close(pipefd);
    }

    return 0;
}



1.2 命名管道(Named Pipes, FIFO)

命名管道可以用于任何两个进程之间的通信,具有路径名,与文件类似。

1.2.1 创建与使用

使用`mkfifo()`创建命名管道:


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

int main() {
    char *fifo = "/tmp/my_fifo";
    char buffer;

    if (mkfifo(fifo, 0666) == -1) {
      perror("mkfifo");
      exit(EXIT_FAILURE);
    }

    pid_t cpid = fork();
    if (cpid == 0) {    /* 子进程 */
      int fd = open(fifo, O_RDONLY);
      read(fd, buffer, sizeof(buffer));
      printf("子进程接收到的数据: %s\n", buffer);
      close(fd);
    } else {            /* 父进程 */
      int fd = open(fifo, O_WRONLY);
      write(fd, "Hello, child process", 21);
      close(fd);
    }

    return 0;
}



2. 消息队列(Message Queues)

消息队列允许进程以消息的形式进行通信,支持异步操作,且能够实现数据的结构化传输。

2.1 创建与使用

消息队列通过`msgget()`、`msgsnd()`、`msgrcv()`等系统调用进行操作:


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct msg_buffer {
    long msg_type;
    char msg_text;
};

int main() {
    key_t key;
    int msgid;
    struct msg_buffer message;

    key = ftok("progfile", 65);
    msgid = msgget(key, 0666 | IPC_CREAT);
    message.msg_type = 1;

    strcpy(message.msg_text, "Hello, message queue");
    msgsnd(msgid, &message, sizeof(message), 0);

    msgrcv(msgid, &message, sizeof(message), 1, 0);
    printf("接收到的消息: %s\n", message.msg_text);

    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}



3. 共享内存(Shared Memory)

共享内存是最快的IPC方式,允许多个进程直接访问同一块内存区域。

3.1 创建与使用

共享内存通过`shmget()`、`shmat()`、`shmdt()`等系统调用进行操作:


#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    key_t key = ftok("shmfile", 65);
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
    char *str = (char*) shmat(shmid, (void*)0, 0);

    strcpy(str, "Hello, shared memory");
    printf("写入数据: %s\n", str);

    shmdt(str);
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}



4. 信号量(Semaphores)

信号量主要用于进程同步,但也可以用于进程间通信。

4.1 创建与使用

信号量通过`semget()`、`semop()`、`semctl()`等系统调用进行操作:


#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>

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 u;
    u.val = 1;
    semctl(semid, 0, SETVAL, u);

    struct sembuf sb = {0, -1, 0};// P操作
    semop(semid, &sb, 1);

    printf("进入临界区\n");
    sleep(2);// 模拟临界区操作
    printf("退出临界区\n");

    sb.sem_op = 1;// V操作
    semop(semid, &sb, 1);

    semctl(semid, 0, IPC_RMID);

    return 0;
}



5. 套接字(Sockets)

套接字不仅用于网络通信,也可以用于本地进程间通信(Unix Domain Sockets)。

5.1 创建与使用

本地套接字通过`socket()`、`bind()`、`listen()`、`accept()`等系统调用进行操作:


#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define SOCKET_PATH "/tmp/socket"

int main() {
    int server_fd, client_fd;
    struct sockaddr_un addr;
    char buffer;
    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
    bind(server_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un));
    listen(server_fd, 5);

    client_fd = accept(server_fd, NULL, NULL);
    read(client_fd, buffer, sizeof(buffer));
    printf("接收到的数据: %s\n", buffer);

    close(client_fd);
    close(server_fd);
    unlink(SOCKET_PATH);

    return 0;
}



6. 总结

Linux提供了多种进程间通信方式,每种方式都有其适用的场景和优缺点:

(1) 管道(匿名管道和命名管道)简单易用,适用于父子进程或相关进程之间的通信。

(2) 消息队列适用于需要结构化数据传输且通信双方相对独立的场景。

(3) 共享内存是最快的通信方式,但需要进程间同步机制确保数据一致性。

(4) 信号量主要用于进程同步,也可以结合其他IPC方式使用。

(5) 套接字适用于本地和网络通信,具有很强的灵活性和扩展性。

选择合适的IPC方式,能够有效提高系统的性能和稳定性。希望本文能帮助读者更好地理解和应用Linux进程间通信技术。Happy coding with Linux IPC!
页: [1]
查看完整版本: Linux进程间通信