linux进程通信方式及其详细实例
概述
进程通信是现代操作系统中非常重要的概念之一,它使得不同进程之间能够协同工作,实现更加复杂的应用程序。进程通信可以通过多种方式实现,其中一些常见的方式包括管道、消息队列、共享内存和信号量等。
进程通信方式
在Linux系统中,进程通信的方式有多种,以下是其中几种常见的方式:
管道(Pipe):是一种半双工的通信方式,它只能用于具有亲缘关系的进程间的通信。一个进程将数据写入管道,而另一个进程则从管道中读取数据。
命名管道(Named Pipe):也是一种半双工的通信方式,不同之处在于,它可以用于任意进程之间的通信。
信号(Signal):是一种比较简单的通信方式,用于通知接收进程某个事件已经发生。信号可以由内核、进程或者用户发送。
共享内存(Shared Memory):是一种高效的通信方式,它允许多个进程共享同一块物理内存区域。多个进程可以在该内存区域中读写数据,从而实现进程间的通信。
信号量(Semaphore):是一种用于进程同步的机制,可以用于控制多个进程对共享资源的访问。信号量可以用来实现进程间的互斥和同步。
消息队列(Message Queue):是一种基于消息的通信方式,进程可以向消息队列发送消息,并从队列中读取消息。消息队列可以用于不同进程之间的通信。
套接字(Socket):是一种通用的进程通信方式,可以用于不同机器之间的进程通信,也可以用于同一机器上的进程通信。套接字可以支持不同的网络协议,例如TCP/IP和UDP。
管道(Pipe)
管道是一种半双工的通信方式,只能用于具有亲缘关系的进程间的通信。一个进程将数据写入管道,另一个进程则从管道中读取数据。管道有两种类型,分别为无名管道和命名管道。
无名管道的创建需要调用pipe函数,函数原型为:
int pipe(int pipefd[2]);
其中pipefd[0]和pipefd[1]是两个文件描述符,分别用于读和写管道。下面是一个简单的示例,创建一个无名管道并在父进程和子进程之间进行通信:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int fd[2], pid;
char buf[20];
if (pipe(fd) == -1) { // 创建管道
perror("pipe");
exit(1);
}
if ((pid = fork()) == -1) { // 创建子进程
perror("fork");
exit(1);
}
if (pid == 0) { // 子进程写入管道
close(fd[0]); // 关闭读端
write(fd[1], "Hello, parent", 14);
close(fd[1]);
} else { // 父进程读取管道
close(fd[1]); // 关闭写端
read(fd[0], buf, 20);
printf("Received message: %s\n", buf);
close(fd[0]);
}
return 0;
}
命名管道也叫FIFO,它是一种特殊的文件类型,可以用于任意进程之间的通信。命名管道的创建需要调用mkfifo函数,函数原型为:
int mkfifo(const char *pathname, mode_t mode);
其中pathname是管道文件的路径名,mode是文件权限。下面是一个简单的示例,创建一个命名管道并在父进程和子进程之间进行通信:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/myfifo"
int main() {
int fd, pid;
char buf[20];
if (mkfifo(FIFO_PATH, 0666) == -1) { // 创建命名管道
perror("mkfifo");
exit(1);
}
if ((pid = fork()) == -1) { // 创建子进程
perror("fork");
exit(1);
}
if (pid == 0) { // 子进程写入管道
fd = open(FIFO_PATH, O_WRONLY);
write(fd, "Hello, parent", 14);
close(fd);
} else { // 父进程读取管道
fd = open(FIFO_PATH, O_RDONLY);
read(fd, buf, 20);
printf("Received message: %s\n", buf);
close(fd);
}
return 0;
}
消息队列
消息队列是一种进程间通信的方式,允许一个进程向另一个进程发送消息。它是一种异步通信方式,发送方发送消息后即可继续执行,不必等待接收方的响应。
在C语言中,可以通过消息队列函数来创建消息队列,具体代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSGSIZE 16
struct msgbuf {
long mtype;
char mtext[MSGSIZE];
};
int main()
{
int msqid;
key_t key;
struct msgbuf buf;
if ((key = ftok(".", 'a')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
buf.mtype = 1;
sprintf(buf.mtext, "Hello, world!");
if (msgsnd(msqid, &buf, MSGSIZE, 0) == -1) {
perror("msgsnd");
if (msgrcv(msqid, &buf, MSGSIZE, 1, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("接收到的消息:%s\n", buf.mtext);
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
return 0;
}
这段代码中,通过ftok函数创建一个唯一的key,然后通过msgget函数创建一个消息队列。发送方向消息队列中发送了一条消息,接收方从消息队列中读取并输出该消息。最后通过msgctl函数删除消息队列。
共享内存(Shared Memory)
共享内存是一种高效的IPC机制,它允许多个进程共享同一个物理内存区域,从而避免了数据拷贝和进程切换的开销。共享内存的创建需要调用shmget函数和shmat函数,函数原型如下:
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
其中key是共享内存的键值,size是共享内存的大小,shmflg是标志位。下面是一个简单的示例,创建一个共享内存区域并在父进程和子进程之间进行通信:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#define SHM_KEY 1234
struct shared_memory {
int count;
char data[20];
};
int main() {
int shmid, pid;
struct shared_memory *shm;
shmid = shmget(SHM_KEY, sizeof(struct shared_memory), 0666 | IPC_CREAT); // 创建共享内存
if (shmid == -1) {
perror("shmget");
exit(1);
}
shm = shmat(shmid, NULL, 0); // 将共享内存映射到进程的地址空间
if (shm == (void *)-1) {
perror("shmat");
exit(1);
}
shm->count = 0; // 初始化共享内存中的数据
if ((pid = fork()) == -1) { // 创建子进程
perror("fork");
exit(1);
}
if (pid == 0) { // 子进程访问共享内存
while (1) {
shm->count++;
sprintf(shm->data, "Count: %d", shm->count);
sleep(1);
}
} else { // 父进程访问共享内存
while (1) {
printf("%s\n", shm->data);
sleep(1);
}
}
return 0;
}
套接字通信(Socket Communication)
套接字通信是指使用套接字进行进程间通信的方式。套接字通信可以在不同主机之间进行,同时也可以使用不同的网络协议进行通信。套接字通信一般包括创建套接字、连接套接字、发送数据和接收数据等步骤。以下是一个使用套接字进行进程通信的c语言示例:
信号(Signal)
信号是一种异步通信方式,当进程接收到一个信号时,会打断当前的执行,转而执行与该信号相关联的信号处理函数。Linux提供了许多信号,如SIGINT、SIGTERM、SIGKILL等。可以使用signal函数或sigaction函数来注册信号处理函数,下面是一个简单的示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void handler(int sig) {
printf("Received signal %d\n", sig);
}
int main() {
signal(SIGINT, handler); // 注册信号处理函数
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}
该程序会不断输出"Running…“,当用户按下CTRL+C时,会打印出"Received signal 2”,然后继续运行。
信号量(Semaphore)
信号量是一种计数器,用于控制对共享资源的访问。每次进程访问共享资源时,都要先获取一个信号量,如果信号量的值大于0,则进程可以继续访问,否则进程需要等待。访问完成后,进程会释放信号量,使其值加1,以便其他进程访问。Linux提供了semget函数、semop函数和semctl函数来创建和操作信号量,下面是一个简单的示例:链接