进程间通信的几种方式

进程间通信方式,常见的有以下几种:

管道(pipe):管道是一种半双工通信方式,只能在具有亲缘关系的进程之间使用。管道可以通过fork()系统调用创建,其读端和写端分别在父进程和子进程中使用。管道具有较高的效率和简单易用的特点,但只适用于单向通信。

命名管道(named pipe):命名管道是一种FIFO(先进先出)的消息传递机制,它提供了一个无亲缘关系的进程间通信方式。命名管道可以在文件系统中创建并命名,任何进程都可以打开它进行读写操作。命名管道可以用于多个进程之间的通信,但由于是基于文件的通信方式,效率相对较低。

信号量(semaphore):信号量是一种计数器,用于控制多个进程对共享资源的访问。通过对信号量的P操作和V操作可以实现对资源的加锁和解锁,从而避免了多个进程同时访问共享资源的问题。信号量的优点在于可以在不同进程之间进行通信,但其使用较为复杂,需要考虑到各种竞态条件和同步问题。

共享内存(shared memory):共享内存是一种高效的进程间通信方式,可以将同一块物理内存映射到多个进程的虚拟地址空间中,实现多个进程之间的数据共享。共享内存的优点在于效率高,但同时也存在数据同步和竞态条件等问题,需要采用锁机制进行保护。

消息队列(message queue):消息队列是一种基于消息的进程间通信方式,它允许多个进程通过一个中介(消息队列)进行异步通信。消息队列具有缓存机制,可以减少进程之间的频繁通信,同时也可以避免数据丢失的问题。但消息队列的数据传输是有限制的,需要对消息的长度进行限制。

不同的进程间通信方式各有优缺点,适用于不同的场景。比如:

管道适用于父子进程之间的通信,或者只需要单向通信的场景。
命名管道适用于需要多个进程之间进行通信,但是数据传输量不大的场景。
信号量适用于需要对共享资源进行控制和同步的场景,但使用复杂。
共享内存适用于需要高效地进行大量数据共享的场景。

在下面的例子中,首先通过pipe()系统调用创建了一个管道,其中pipe_fd数组中下标为0的元素表示读端,下标为1的元素表示写端。然后通过fork()系统调用创建了一个子进程,并在父进程中向管道写入一条消息,子进程中从管道中读取消息并打印。最后关闭了相应的文件描述符并结束进程。

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

#define BUFFER_SIZE 1024

int main() {
    int pipe_fd[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];

    if (pipe(pipe_fd) < 0) {
        perror("pipe error");
        exit(EXIT_FAILURE);
    }

    if ((pid = fork()) < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        close(pipe_fd[0]); // 关闭管道读端
        char *msg = "hello, child process!";
        if (write(pipe_fd[1], msg, strlen(msg)) < 0) {
            perror("write error");
            exit(EXIT_FAILURE);
        }
        close(pipe_fd[1]); // 关闭管道写端
        printf("parent process finished writing to pipe.\n");
    } else {
        close(pipe_fd[1]); // 关闭管道写端
        if (read(pipe_fd[0], buffer, BUFFER_SIZE) < 0) {
            perror("read error");
            exit(EXIT_FAILURE);
        }
        printf("child process received message: %s\n", buffer);
        close(pipe_fd[0]); // 关闭管道读端
    }

    return 0;
}

在下面的例子中,首先通过mkfifo()系统调用创建了一个命名管道。然后通过fork()系统调用创建了一个子进程,并在父进程中向命名管道写入一条消息,子进程中从命名管道中读取消息并打印。最后关闭了相应的文件描述符并结束进程。

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

#define BUFFER_SIZE 1024
#define FIFO_FILE "/tmp/myfifo"

int main() {
    int fd;
    char buffer[BUFFER_SIZE];

    mkfifo(FIFO_FILE, 0666); // 创建命名管道

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        fd = open(FIFO_FILE, O_WRONLY); // 父进程以写方式打开管道
        if (fd < 0) {
            perror("open error");
            exit(EXIT_FAILURE);
        }
        char *msg = "hello, child process!";
        if (write(fd, msg, strlen(msg)) < 0) {
            perror("write error");
            exit(EXIT_FAILURE);
        }
        close(fd);
        printf("parent process finished writing to pipe.\n");
    } else {
        fd = open(FIFO_FILE, O_RDONLY); // 子进程以读方式打开管道
        if (fd < 0) {
            perror("open error");
            exit(EXIT_FAILURE);
        }
        if (read(fd, buffer, BUFFER_SIZE) < 0) {
            perror("read error");
            exit(EXIT_FAILURE);
        }
        printf("child process received message: %s\n", buffer);
        close(fd);
    }

    return 0;
}

在下面的例子中,首先通过semget()系统调用创建了一个信号量,然后通过semctl()系统调用初始化了该信号量的值为0。然后通过fork()系统调用创建了一个子进程,并在父进程中等待信号量,子进程中从父进程中读取消息并打印。最后通过semctl()系统调用删除了信号量并结束进程。

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

#define SEM_KEY 0x1234 // 信号量键值

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

void wait_sem(int semid, int sem_num) {
    struct sembuf sem_op;
    sem_op.sem_num = sem_num;
    sem_op.sem_op = -1;
    sem_op.sem_flg = 0;
    semop(semid, &sem_op, 1);
}

void signal_sem(int semid, int sem_num) {
    struct sembuf sem_op;
    sem_op.sem_num = sem_num;
    sem_op.sem_op = 1;
    sem_op.sem_flg = 0;
    semop(semid, &sem_op, 1);
}

int main() {
    int semid;
    union semun sem_val;
    char buffer[1024];

    if ((semid = semget(SEM_KEY, 1, IPC_CREAT|0666)) < 0) {
        perror("semget error");
        exit(EXIT_FAILURE);
    }

    sem_val.val = 0;
    if (semctl(semid, 0, SETVAL, sem_val) < 0) {
        perror("semctl error");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        wait_sem(semid, 0);
        char *msg = "hello, child process!";
        printf("parent process finished waiting for semaphore.\n");
        strncpy(buffer, msg, sizeof(buffer) - 1);
        signal_sem(semid, 0);
    } else {
        wait_sem(semid, 0);
        printf("child process received message: %s\n", buffer);
        signal_sem(semid, 0);
    }

    if (semctl(semid, 0, IPC_RMID, sem_val) < 0) {
        perror("semctl error");
        exit(EXIT_FAILURE);
    }

    return 0;
}

在下面的例子中,首先通过shmget()系统调用创建了一个共享内存,然后通过shmat()系统调用将该共享内存附加到当前进程的地址空间中。然后通过fork()系统调用创建了一个子进程,并在父进程中向共享内存中写入了一条消息,子进程中从共享内存中读取消息并打印。最后通过shmdt()和shmctl()系统调用分离共享内存并删除共享内存,并结束进程。

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

#define SHM_KEY 0x1234 // 共享内存键值

int main() {
    int shmid;
    char *shared_mem;
    char buffer[1024];

    if ((shmid = shmget(SHM_KEY, 1024, IPC_CREAT|0666)) < 0) {
        perror("shmget error");
        exit(EXIT_FAILURE);
    }

    if ((shared_mem = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("shmat error");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        char *msg = "hello, child process!";
        printf("parent process writes message to shared memory.\n");
        strncpy(shared_mem, msg, strlen(msg));
    } else {
        sleep(1);
        printf("child process reads message from shared memory: %s\n", shared_mem);
    }

    if (shmdt(shared_mem) < 0) {
        perror("shmdt error");
        exit(EXIT_FAILURE);
    }

    if (shmctl(shmid, IPC_RMID, NULL) < 0) {
        perror("shmctl error");
        exit(EXIT_FAILURE);
    }

    return 0;
}

在下面的例子中,首先通过msgget()系统调用创建了一个消息队列,然后通过fork()系统调用创建了一个子进程,并在父进程中向消息队列中写入了一条消息,子进程中从消息队列中读取消息并打印。最后通过msgctl()系统调用删除消息队列,并结束进程。

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

#define MSG_TYPE 1 // 消息类型
#define MSG_KEY 0x1234 // 消息队列键值

struct msgbuf {
    long mtype;
    char mtext[1024];
};

int main() {
    int msqid;
    struct msgbuf msg;
    key_t key = MSG_KEY;

    if ((msqid = msgget(key, IPC_CREAT|0666)) < 0) {
        perror("msgget error");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        char *msg_text = "hello, child process!";
        printf("parent process writes message to message queue.\n");
        msg.mtype = MSG_TYPE;
        strncpy(msg.mtext, msg_text, strlen(msg_text));
        if (msgsnd(msqid, &msg, strlen(msg.mtext)+1, 0) < 0) {
            perror("msgsnd error");
            exit(EXIT_FAILURE);
        }
    } else {
        sleep(1);
        printf("child process reads message from message queue: ");
        if (msgrcv(msqid, &msg, sizeof(msg), MSG_TYPE, 0) < 0) {
            perror("msgrcv error");
            exit(EXIT_FAILURE);
        }
        printf("%s\n", msg.mtext);
    }

    if (msgctl(msqid, IPC_RMID, NULL) < 0) {
        perror("msgctl error");
        exit(EXIT_FAILURE);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值