书接上文:轻松掌握IPC技巧,读懂Linux进程间通信(一),今天继续探讨几种核心的IPC技术:信号量、套接字、内存映射和文件。
由于现代操作系统中,进程间都是地址空间隔离的,每一个进程都有独立的地址空间,而我们的业务软件为了良好的架构和运维都会进行模块化设计,IPC进程间通信是一个必须要掌握的技能。进程间通信(IPC)是指在不同进程之间传递数据或信号的一系列方法。进程间通信有很多种方式,每种方式都会有自己的特点。
信号量(Semaphore)

技术原理
信号量是一种用于提供进程间或线程间同步的机制。它是一个计数器,用于控制访问共享资源的进程数量。当信号量的值大于0时,表示可用资源的数量;当值为0时,表示没有可用资源,进程需要等待。进程可以执行两种操作:wait
(等待)操作会减少信号量的值,signal
(信号)操作会增加信号量的值。
特点
-
同步控制:信号量主要用于实现进程间的同步。 -
灵活性:支持多个进程同时访问共享资源。 -
死锁风险:不当使用可能导致死锁。
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
sem_t sem;
void* thread_function(void* arg) {
sem_wait(&sem); // 等待信号量
printf("Entered..n");
// critical section
sleep(4); // 模拟耗时操作
printf("Exiting..n");
sem_post(&sem); // 释放信号量
}
int main() {
sem_init(&sem, 0, 1); // 初始化信号量,初始值为1
pthread_t t1, t2;
pthread_create(&t1, NULL, thread_function, NULL);
sleep(2); // 确保线程t1先运行
pthread_create(&t2, NULL, thread_function, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
sem_destroy(&sem); // 销毁信号量
return 0;
}
套接字(Socket)

技术原理
套接字是用于不同计算机之间或同一台计算机上的不同进程之间网络通信的一种技术。它提供了一个端点到端点的通信机制。根据协议类型的不同,套接字分为流式套接字(TCP)和数据报套接字(UDP),以及本地UNIX套接字。
特点
-
灵活的通信机制:支持面向连接的(TCP)和无连接的(UDP)通信。 -
跨平台:套接字API在多种操作系统上得到支持。 -
网络通信:主要用于实现网络应用程序之间的数据交换。下面的代码示例展示了一个简单的基于TCP的客户端和服务器通信过程。
服务器端示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "Hello from server";
// 创建套接字文件描述符
server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 绑定套接字到端口8080
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
// 监听连接
listen(server_fd, 3);
// 接受连接
new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
// 读取和发送消息
read(new_socket, buffer, 1024);
printf("%sn", buffer);
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sentn");
return 0;
}
客户端示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
// 创建套接字
sock = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
// 连接到服务器
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
// 发送消息
send(sock, hello, strlen(hello), 0);
printf("Hello message sentn");
// 接收消息
read(sock, buffer, 1024);
printf("%sn", buffer);
return 0;
}
内存映射(Memory Mapped Files)

技术原理
内存映射文件允许将文件或设备的内容映射到进程的地址空间。通过这种方式,文件内容可以像访问普通内存一样被访问,这样可以提高文件操作的效率,并且允许多个进程共享内存空间的同一部分。
特点
-
高效的文件访问:通过内存访问文件,避免了传统的文件I/O操作,提高效率。 -
进程间共享:允许多个进程共享对同一文件内容的访问。 -
易于操作:可以使用普通的指针操作来读写文件内容。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main() {
const char *text = "This is a test";
int fd = open("test.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
size_t textsize = strlen(text) + 1;
// 调整文件大小
lseek(fd, textsize-1, SEEK_SET);
write(fd, "", 1);
// 创建内存映射
char *map = mmap(0, textsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(map, text, textsize);
// 同步内存映射
msync(map, textsize, MS_SYNC);
// 释放内存映射
munmap(map, textsize);
close(fd);
return 0;
}
文件

技术原理
文件是最基础的数据存储和交换手段。进程可以通过读写文件来交换信息。尽管这种方式相对简单,但它不需要特殊的IPC机制或API,可以在几乎所有操作系统和编程环境中使用。
特点
-
简单性:使用标准的文件I/O操作,易于理解和实现。 -
通用性:几乎所有的操作系统都支持文件操作。 -
非实时性:文件I/O操作相对较慢,可能不适合要求高实时性的应用。 -
同步处理:需要额外的机制(如锁)来处理多个进程同时访问同一文件的问题。
写入文件
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
char *text = "Hello, world!";
fp = fopen("example.txt", "w");
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
fprintf(fp, "%sn", text);
fclose(fp);
return 0;
}
读取文件
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
char buffer[255];
fp = fopen("example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
fgets(buffer, 255, fp);
printf("%sn", buffer);
fclose(fp);
return 0;
}
在实际应用中,选择哪种IPC机制取决于具体需求,如数据的大小、传输速度、同步与异步等。每种机制都有其适用场景和优缺点。在设计系统时,需要根据实际场景和需求进行合理选择。
推荐一本开发者必备的经典手册,Linux编程资深专家Michael Kerrisk在书中详细描述了Linux/UNIX系统编程所涉及的系统调用和库函数,并辅之以全面而清晰的代码示例。《Linux/UNIX系统编程手册(上、下册)》涵盖了逾500个系统调用及库函数,并给出逾200个程序示例,另含88张表格和115幅示意图。
公众号后台回复“linux编程手册”获取下载链接

点个在看你最好看

原文始发于微信公众号(计算机刨根问底):轻松掌握IPC技巧,读懂Linux进程间通信(二)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207770.html