进程间通信方式,常见的有以下几种:
管道(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;
}