1. 共享内存
共享内存是一种高效的Linux进程间通信机制,它允许不同进程之间共享同一块内存区域,从而实现数据共享。共享内存的使用需要借助于系统调用shmget、shmat等。下面我们将详细介绍共享内存的实现以及相关代码示例。
1.1 共享内存的创建和销毁
在Linux系统中,创建共享内存需要使用shmget函数。下面是一个创建共享内存的示例代码:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int create_shared_memory() {
int shmid;
// 创建共享内存
shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget failed");
return -1;
}
// 启动共享内存
void* shared_memory = shmat(shmid, NULL, 0);
if (shared_memory == (void*) -1) {
perror("shmat failed");
return -1;
}
// 注销共享内存
if (shmdt(shared_memory) == -1) {
perror("shmdt failed");
return -1;
}
// 销毁共享内存
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl failed");
return -1;
}
return 0;
}
在上述代码中,通过shmget函数创建了一个大小为1024字节的共享内存,然后通过shmat函数将共享内存连接到当前进程的地址空间,之后又通过shmdt函数将共享内存与当前进程断开连接。最后,通过shmctl函数销毁了共享内存。
1.2 共享内存的读写操作
创建共享内存后,不同的进程可以通过指针访问共享内存中的数据。下面是一个可以实现简单的共享内存读写操作的示例代码:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdio.h>
int read_shared_memory() {
int shmid;
// 创建共享内存
shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget failed");
return -1;
}
// 启动共享内存
void* shared_memory = shmat(shmid, NULL, 0);
if (shared_memory == (void*) -1) {
perror("shmat failed");
return -1;
}
// 写入数据到共享内存
strcpy((char*) shared_memory, "Hello, shared memory!");
// 读取共享内存中的数据
printf("Shared memory content: %s\n", (char*) shared_memory);
// 注销共享内存
if (shmdt(shared_memory) == -1) {
perror("shmdt failed");
return -1;
}
// 销毁共享内存
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl failed");
return -1;
}
return 0;
}
在上述代码中,我们首先创建了一个大小为1024字节的共享内存,并将其连接到当前进程的地址空间中。接着,我们使用strcpy函数将字符串"Hello, shared memory!"写入共享内存。最后,我们通过printf函数将共享内存中的内容打印出来。
2. 管道
管道是一种常用的Linux进程间通信方式,它允许不同进程之间通过管道进行数据传输。管道分为匿名管道和有名管道两种形式。
2.1 匿名管道的创建和使用
匿名管道是最简单的管道形式,它只能用于具有亲缘关系的父子进程之间的通信。下面是一个使用匿名管道进行进程间通信的示例代码:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int anonymous_pipe() {
int fd[2];
pid_t pid;
char buf[1024];
// 创建管道
if (pipe(fd) == -1) {
perror("pipe failed");
return -1;
}
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork failed");
return -1;
}
if (pid == 0) {
// 子进程从管道读取数据
close(fd[1]);
read(fd[0], buf, sizeof(buf));
printf("Child process read from pipe: %s\n", buf);
close(fd[0]);
} else {
// 父进程向管道写入数据
close(fd[0]);
strcpy(buf, "Hello, pipe!");
write(fd[1], buf, sizeof(buf));
close(fd[1]);
}
return 0;
}
在上述代码中,我们首先使用pipe函数创建了一个管道,并通过fork函数创建了一个子进程。子进程通过read函数从管道中读取数据,并通过printf函数将其打印出来。父进程则通过write函数向管道中写入数据。
2.2 有名管道的创建和使用
有名管道允许不具有亲缘关系的进程之间进行通信。有名管道可以通过mkfifo函数创建,并通过open函数打开和关闭。下面是一个使用有名管道进行进程间通信的示例代码:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int named_pipe() {
int fd;
char buf[1024];
// 创建有名管道
mkfifo("myfifo", 0666);
// 打开管道
fd = open("myfifo", O_WRONLY);
if (fd == -1) {
perror("open failed");
return -1;
}
// 向管道写入数据
strcpy(buf, "Hello, named pipe!");
write(fd, buf, sizeof(buf));
close(fd);
// 打开管道
fd = open("myfifo", O_RDONLY);
if (fd == -1) {
perror("open failed");
return -1;
}
// 从管道读取数据
read(fd, buf, sizeof(buf));
printf("Read from named pipe: %s\n", buf);
close(fd);
// 删除管道
unlink("myfifo");
return 0;
}
在上述代码中,我们首先使用mkfifo函数创建了一个名为"myfifo"的有名管道,并分别使用open函数打开了读端和写端。父进程通过write函数向管道中写入数据,子进程通过read函数从管道中读取数据,并通过printf函数将其打印出来。最后,我们使用unlink函数删除了有名管道。
3. 套接字
套接字(Socket)是一种常用的Linux进程间通信方式,它允许不同主机或同一主机上的不同进程之间进行通信。套接字可以用于实现不同层次的协议,如TCP、UDP等。下面是一个使用套接字进行进程间通信的示例代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
int socket_communication() {
int sockfd;
char buf[1024];
struct sockaddr_in servaddr;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket failed");
return -1;
}
// 设置服务器地址
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(1234);
servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
// 连接到服务器
if (connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
perror("connect failed");
return -1;
}
// 向服务器发送数据
strcpy(buf, "Hello, socket!");
write(sockfd, buf, sizeof(buf));
// 从服务器接收数据
read(sockfd, buf, sizeof(buf));
printf("Received from server: %s\n", buf);
// 关闭套接字
close(sockfd);
return 0;
}
在上述代码中,我们首先使用socket函数创建了一个套接字,并将其连接到服务器地址(此处为本地回环地址),然后通过write函数向服务器发送数据。服务器接收到数据后,通过write函数将其返回给客户端。最后,我们使用close函数关闭了套接字。
4. 信号
信号是一种用于进程间通信的机制,它允许一个进程向另一个进程发送信号。Linux系统中的信号可以通过kill函数发送,也可以通过信号处理函数接收和处理。下面是一个使用信号进行进程间通信的示例代码:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signal_handler(int sig) {
printf("Received signal: %d\n", sig);
}
int signal_communication() {
pid_t pid;
// 注册信号处理函数
signal(SIGUSR1, signal_handler);
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork failed");
return -1;
}
if (pid == 0) {
// 子进程向父进程发送信号
sleep(1);
kill(getppid(), SIGUSR1);
} else {
// 父进程等待子进程发送信号
pause();
}
return 0;
}
在上述代码中,我们首先通过signal函数注册了一个信号处理函数signal_handler,然后通过fork函数创建了一个子进程。子进程通过kill函数向父进程发送一个自定义信号SIGUSR1。父进程通过pause函数暂停自己的执行,直到收到信号后再继续执行,并由信号处理函数打印收到的信号编号。
总之,Linux提供了多种进程间通信方式,包括共享内存、管道、套接字和信号等。不同的通信方式适用于不同的场景,根据实际需求选择合适的通信方式。