参考和学习视频:
B站up主【拥有6块腹肌的程序猿的个人空间-哔哩哔哩】 https://b23.tv/rPiVDqX
视频地址:https://www.bilibili.com/video/BV1kN411E7dp?vd_source=f8a6ae1f67ceef69d4edbdd3820288ec
进程间通信方式:
1. 管道--pipe
匿名管道
- 头文件:
#include <unistd.h> - 函数原型:
int pipe(int pipefd[2]); //pipefd[0]为读端 //pipefd[1]为写端 //返回值0为成功,-1为失败并设置errno。 - 文件为/proc/[pid]/fd
- 示例代码:
#include <stdio.h> #include <unistd.h> int main() { int fd[2]; if(pipe(fd) != 0) { printf("pipe faield\n"); return 0; } printf("pipe[0] is %d\n", fd[0]); printf("pipe[1] is %d\n", fd[1]); write(fd[1], "hello", 5); char buff[16] = {0}; read(fd[0], buff, sizeof(buff) - 1); printf("read [%s]\n", buff); return 0; } - 编译、运行和输出:
-
父子进程和管道应用
#include <stdio.h> #include <unistd.h> int main() { int fd[2]; if(pipe(fd) != 0) { printf("pipe faield\n"); return 0; } printf("pipe[0] is %d\n", fd[0]); printf("pipe[1] is %d\n", fd[1]); int ret = fork(); if(ret < 0) { perror("fork"); return 0; } else if(ret == 0) { //child write(fd[1], "hello", 5); } else { //parent char buff[32] = {0}; read(fd[0], buff, sizeof(buff) - 1); printf("read buffer: [%s]\n", buff); } return 0; } -
编译运行结果:
有名管道
以下是一个使用有名管道进行进程间通信的C语言示例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define FIFO_NAME "/tmp/myfifo" // 定义有名管道的路径 int main() { int fd; char message[100]; // 创建有名管道 mkfifo(FIFO_NAME, 0666); // 打开有名管道以进行读取和写入操作 fd = open(FIFO_NAME, O_RDWR); // 从标准输入读取用户输入的消息,并将其写入到有名管道中 printf("Enter a message: "); fgets(message, sizeof(message), stdin); write(fd, message, strlen(message) + 1); // 从有名管道中读取另一个进程发送的消息,并打印出来 read(fd, message, sizeof(message)); printf("Received message: %s", message); // 关闭并删除有名管道 close(fd); unlink(FIFO_NAME); return 0; }
在上述示例中,我们首先定义了一个路径为/tmp/myfifo的有名管道。然后,我们使用mkfifo()函数创建了这个有名管道。
接下来,我们通过调用open()函数以读写模式打开这个有名管道,并将返回的文件描述符保存在变量fd中。
然后,我们从标准输入中读取用户输入的消息,并使用write()函数将该消息写入到有名管道中。
最后,我们使用read()函数从有名管道中读取另一个进程发送的消息,并使用printf()函数打印出来。
最后,我们关闭并删除有名管道,通过调用close()和unlink()函数完成这个操作。
请注意,在运行此程序之前,您需要先编译它。您可以使用以下命令进行编译:
gcc named_pipe.c -o named_pipe
然后,您可以运行生成的可执行文件:
./named_pipe
2. 共享内存--shm
下面是一个使用共享内存进行进程间通信的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#define MAX_SIZE 1024
int main()
{
int segment_id;
char* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
const int shared_segment_size = MAX_SIZE;
// 创建共享内存段
segment_id = shmget(IPC_PRIVATE, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
// 连接到共享内存段
shared_memory = (char*)shmat(segment_id, 0, 0);
printf("共享内存段连接到地址:%p", shared_memory);
// 写入数据到共享内存段
printf("请输入要写入的字符串:");
fgets(shared_memory, MAX_SIZE, stdin);
// 断开与共享内存段的连接
shmdt(shared_memory);
// 重新连接到共享内存段,以查看写入的数据
shared_memory = (char*)shmat(segment_id, (void*)0x5000000, 0);
printf("从共享内存读取的字符串:%s", shared_memory);
// 删除共享内存段
shmctl(segment_id, IPC_RMID, &shmbuffer);
return 0;
}
在这个例子中,我们首先创建了一个大小为MAX_SIZE的共享内存段。然后我们将该共享内存段连接到当前进程的地址空间,并通过标准输入获取一个字符串,将其写入共享内存段中。然后我们断开与共享内存段的连接,并重新连接到共享内存段,以读取刚才写入的数据。最后,我们删除共享内存段。
3. 消息队列--msgqueue
下面是一个使用C语言实现进程间使用消息队列通信的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_MSG_SIZE 1024
struct msg_buffer {
long msg_type;
char msg_text[MAX_MSG_SIZE];
};
int main() {
key_t key;
int msg_id;
struct msg_buffer message;
// 创建唯一的key
key = ftok("msg_queue_example", 65);
// 创建消息队列
msg_id = msgget(key, 0666 | IPC_CREAT);
if (msg_id == -1) {
printf("无法创建消息队列");
exit(1);
}
printf("消息队列已创建,ID:%d", msg_id);
// 发送消息到消息队列
message.msg_type = 1;
strcpy(message.msg_text, "Hello from process A!");
if (msgsnd(msg_id, &message, sizeof(message), 0) == -1) {
printf("无法发送消息到消息队列");
exit(1);
}
printf("Process A已发送消息:%s", message.msg_text);
// 接收来自进程B的消息
if (msgrcv(msg_id, &message, sizeof(message), 2, 0) == -1) {
printf("无法接收来自进程B的消息");
exit(1);
}
printf("Process A接收到来自进程B的消息:%s", message.msg_text);
// 删除消息队列
if (msgctl(msg_id, IPC_RMID, NULL) == -1) {
printf("无法删除消息队列");
exit(1);
}
printf("消息队列已成功删除");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_MSG_SIZE 1024
struct msg_buffer {
long msg_type;
char msg_text[MAX_MSG_SIZE];
};
int main() {
key_t key;
int msg_id;
struct msg_buffer message;
// 获取与进程A相同的key
key = ftok("msg_queue_example", 65);
// 连接到消息队列
msg_id = msgget(key, 0666 | IPC_CREAT);
if (msg_id == -1) {
printf("无法连接到消息队列");
exit(1);
}
printf("Process B已连接到消息队列,ID:%d", msg_id);
// 接收来自进程A的消息
if (msgrcv(msg_id, &message, sizeof(message), 1, 0) == -1) {
printf("无法接收来自进程A的消息");
exit(1);
}
printf("Process B接收到来自进程A的消息:%s", message.msg_text);
// 发送回复消息到消息队列
message.msg_type = 2;
strcpy(message.msg_text, "Hello from process B!");
if (msgsnd(msg_id, &message, sizeof(message), 0) == -1) {
printf("无法发送回复消息到消息队列");
exit(1);
}
printf("Process B已发送回复消息:%s", message.msg_text);
return 0;
}
这个例子中,进程A和进程B通过共享一个唯一的key来连接到同一个消息队列。进程A首先向消息队列发送一条消息,然后等待来自进程B的回复。进程B接收到来自进程A的消息后,发送一条回复消息给进程A。最后,两个进程分别从消息队列断开连接,并删除该消息队列。
请注意,此示例仅用于演示目的,并未处理错误情况和边界情况。在实际使用中,应该添加适当的错误处理和边界检查。
4. 信号量--sem
下面是一个使用信号量进行进程间通信的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SEM_KEY 1234
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
void P(int sem_id) {
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1; // P操作,信号量减1
sb.sem_flg = SEM_UNDO;
if (semop(sem_id, &sb, 1) == -1) {
perror("P operation failed");
exit(1);
}
}
void V(int sem_id) {
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1; // V操作,信号量加1
sb.sem_flg = SEM_UNDO;
if (semop(sem_id, &sb, 1) == -1) {
perror("V operation failed");
exit(1);
}
}
int main() {
int pid;
// 创建一个信号量集合,包含一个信号量
int sem_id = semget(SEM_KEY, 1, IPC_CREAT | 0666);
if (sem_id == -1) {
perror("Semaphore creation failed");
exit(1);
}
// 初始化信号量为0
union semun arg;
arg.val = 0;
if (semctl(sem_id, 0, SETVAL, arg) == -1) {
perror("Semaphore initialization failed");
exit(1);
}
pid = fork();
if(pid < 0) {
perror("Fork failed");
exit(1);
}
else if(pid == 0) { // 子进程
P(sem_id); // 等待信号量为0
printf("Child process received signal.");
V(sem_id); // 发送信号量加1
}
else { // 父进程
sleep(2); // 等待子进程初始化完毕
printf("Parent process sending signal.");
V(sem_id); // 发送信号量加1
P(sem_id); // 等待信号量为0
printf("Parent process received signal.");
}
return 0;
}
这个例子创建了一个信号量集合,其中包含一个信号量。父进程和子进程通过该信号量进行通信。父进程首先发送一个V操作,将信号量加1,然后等待子进程发送一个P操作,使得信号量变为0。子进程在初始化完成后,等待父进程发送一个V操作,然后发送一个P操作给父进程。
5. 信号-signal
以下是一个使用信号进行进程间通信的C语言示例:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void signal_handler(int signum) {
printf("Received signal %d", signum);
}
int main() {
pid_t child_pid;
// 创建子进程
child_pid = fork();
if (child_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
// 子进程
// 注册信号处理函数
signal(SIGUSR1, signal_handler);
printf("Child process waiting for signal...");
// 等待信号
while(1) {
sleep(1);
}
} else {
// 父进程
printf("Sending signal to child process...");
// 发送信号给子进程
kill(child_pid, SIGUSR1);
sleep(2);
printf("Signal sent successfully.");
}
return 0;
}
在上面的示例中,父进程创建了一个子进程,并向子进程发送了 SIGUSR1 信号。子进程注册了 signal_handler 函数作为 SIGUSR1 信号的处理函数。当子进程收到该信号时,会打印一条消息。
运行程序后,父进程会发送一个 SIGUSR1 信号给子进程,然后等待2秒钟。在这段时间内,子进程会不断地等待接收信号并打印消息。当子进程收到信号后,会打印一条消息。
6. 套接字--socket
以下是一个使用C语言实现进程间使用套接字通信的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
int main() {
int sockfd, newsockfd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len;
char buffer[256];
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error opening socket");
exit(1);
}
// 设置服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
// 绑定套接字到服务器地址
if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
perror("Error binding");
exit(1);
}
// 监听连接请求
listen(sockfd, 5);
printf("Server listening on port %d...", PORT);
while (1) {
// 接受连接请求
client_len = sizeof(client_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &client_addr, &client_len);
if (newsockfd < 0) {
perror("Error accepting connection");
exit(1);
}
printf("Connection accepted from %s:%d", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 读取客户端发送的数据
bzero(buffer, sizeof(buffer));
read(newsockfd, buffer, sizeof(buffer));
printf("Received message: %s", buffer);
// 向客户端发送响应
write(newsockfd, "Server received your message", 27);
// 关闭连接
close(newsockfd);
}
// 关闭套接字
close(sockfd);
return 0;
}
上述代码创建了一个服务器进程,监听8080端口的连接请求。当有客户端连接到服务器时,服务器会接收客户端发送的消息,并向客户端发送响应消息。
以下是一个使用C语言实现的客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 8080
int main() {
int sockfd;
struct sockaddr_in server_addr;
struct hostent *server;
char buffer[256];
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error opening socket");
exit(1);
}
// 获取服务器地址信息
server = gethostbyname("localhost");
if (server == NULL) {
perror("Error finding host");
exit(1);
}
// 设置服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
bcopy((char *)server->h_addr, (char *)&server_addr.sin_addr.s_addr, server->h_length);
// 连接到服务器
if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
perror("Error connecting");
exit(1);
}
// 向服务器发送消息
write(sockfd, "Hello from client", 18);
// 读取服务器的响应
bzero(buffer, sizeof(buffer));
read(sockfd, buffer, sizeof(buffer));
printf("Received message: %s", buffer);
// 关闭套接字
close(sockfd);
return 0;
}
上述代码创建了一个客户端进程,连接到本地8080端口的服务器。客户端向服务器发送一条消息,并等待服务器的响应。

