您的位置:

Linux进程间通信

一、进程间通信简介

在Linux系统中,进程间通信(Inter Process Communication,IPC)是实现多个进程之间数据交换的重要手段。正如名称所示,IPC旨在实现不同进程之间的相互通信,使得进程之间可以进行数据交换,完成相应的任务。

常见的进程间通信方式包括进程信号、共享内存、消息队列、信号量和管道等。

二、共享内存

共享内存是指两个或者多个进程共享同一段物理内存的方式。

共享内存的优点在于速度快,因为共享内存的本质是一块内存空间,进程之间不需要进行数据拷贝和操作系统的干预,直接读取该内存空间中的数据,因此效率很高。

下面是使用共享内存进行进程间通信的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024
#define SHM_MODE 0666

int main(){
    int shmid;
    char *shmptr;
    char buff[SHM_SIZE];

    if((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0){
        printf("shmget error\n");
        exit(1);
    }
    if((shmptr = shmat(shmid, 0, 0)) == (void *)-1){
        printf("shmat error\n");
        exit(1);
    }

    while(1){
        fgets(buff, SHM_SIZE, stdin);
        if(strncmp(buff, "quit", 4) == 0)
            break;
        strncpy(shmptr, buff, SHM_SIZE);
    }

    if(shmdt(shmptr) < 0){
        printf("shmdt error\n");
        exit(1);
    }
    if(shmctl(shmid, IPC_RMID, 0) < 0){
        printf("shmctl error\n");
        exit(1);
    }

    exit(0);
}

上面的代码示例创建一个共享内存区域,并且通过fgets函数从标准输入中读取数据,将数据写入该共享内存空间。

下面是另一个进程访问该共享内存的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024
#define SHM_MODE 0666

int main(){
    int shmid;
    char *shmptr;

    if((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0){
        printf("shmget error\n");
        exit(1);
    }
    if((shmptr = shmat(shmid, 0, 0)) == (void *)-1){
        printf("shmat error\n");
        exit(1);
    }

    while(1){
        if(*shmptr != '\0'){
            printf("receive %s", shmptr);
            memset(shmptr, 0, SHM_SIZE);
        }
        sleep(1);
    }

    if(shmdt(shmptr) < 0){
        printf("shmdt error\n");
        exit(1);
    }
    exit(0);
}

上面的代码示例访问上面示例中创建的共享内存空间,并且每隔1秒钟输出访问到的数据。

三、消息队列

消息队列是多个进程之间传递数据的一种方式,类似于生产者-消费者模型。

在发送进程将数据写入消息队列中之后,接收进程从该消息队列中读取数据。

下面是使用消息队列进行进程间通信的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_SIZE 1024

struct msgbuf{
    long mtype;
    char mtext[MSG_SIZE];
};

int main(){
    key_t key;
    int msgid;
    struct msgbuf msg;

    if((key = ftok(".", 'a')) == -1){
        printf("ftok error\n");
        exit(1);
    }
    if((msgid = msgget(key, IPC_CREAT | 0666)) < 0){
        printf("msgget error\n");
        exit(1);
    }

    while(1){
        fgets(msg.mtext, MSG_SIZE, stdin);
        msg.mtype = 1;
        msgsnd(msgid, &msg, strlen(msg.mtext) + 1, 0);
        if(strncmp(msg.mtext, "quit", 4) == 0)
            break;
    }

    msgctl(msgid, IPC_RMID, 0);

    exit(0);
}

上面的代码示例创建一个消息队列,并且通过fgets函数从标准输入中读取数据,将数据写入该消息队列中。

下面是另一个进程访问该消息队列的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_SIZE 1024

struct msgbuf{
    long mtype;
    char mtext[MSG_SIZE];
};

int main(){
    key_t key;
    int msgid;
    struct msgbuf msg;

    if((key = ftok(".", 'a')) == -1){
        printf("ftok error\n");
        exit(1);
    }
    if((msgid = msgget(key, IPC_CREAT | 0666)) < 0){
        printf("msgget error\n");
        exit(1);
    }

    while(1){
        msgrcv(msgid, &msg, MSG_SIZE, 1, 0);
        if(strncmp(msg.mtext, "quit", 4) == 0)
            break;
        printf("receive %s", msg.mtext);
    }

    msgctl(msgid, IPC_RMID, 0);

    exit(0);
}

上面的代码示例访问上面示例中创建的消息队列,并且输出访问到的数据。

四、管道

管道是一种进程间通信方式,通过在不同的进程中使用文件描述符来实现进程间的通信。

管道分为匿名管道和有名管道两种方式。匿名管道只能在有父子关系的进程之间使用,有名管道可以在不同的进程之间使用。

下面是使用匿名管道进行进程间通信的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PIPE_SIZE 1024

int main(){
    int fd[2];
    pid_t pid;
    char buff[PIPE_SIZE];

    if(pipe(fd) < 0){
        printf("pipe error\n");
        exit(1);
    }
    if((pid = fork()) < 0){
        printf("fork error\n");
        exit(1);
    }else if(pid > 0){
        close(fd[0]);
        while(1){
            fgets(buff, PIPE_SIZE, stdin);
            if(strncmp(buff, "quit", 4) == 0)
                break;
            write(fd[1], buff, PIPE_SIZE);
        }
        close(fd[1]);
    }else{
        close(fd[1]);
        while(1){
            if(read(fd[0], buff, PIPE_SIZE) == 0)
                break;
            printf("receive %s", buff);
        }
        close(fd[0]);
        exit(0);
    }

    exit(0);
}

上面的代码示例创建一个匿名管道,并且通过fgets函数从标准输入中读取数据,将数据写入该管道中。

下面是另一个进程访问该管道的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PIPE_SIZE 1024

int main(){
    int fd[2];
    pid_t pid;
    char buff[PIPE_SIZE];

    if(pipe(fd) < 0){
        printf("pipe error\n");
        exit(1);
    }
    if((pid = fork()) < 0){
        printf("fork error\n");
        exit(1);
    }else if(pid > 0){
        close(fd[0]);
        while(1){
            if(read(fd[1], buff, PIPE_SIZE) == 0)
                break;
            printf("receive %s", buff);
        }
        close(fd[1]);
        wait(NULL);
    }else{
        close(fd[1]);
        while(1){
            fgets(buff, PIPE_SIZE, stdin);
            if(strncmp(buff, "quit", 4) == 0)
                break;
            write(fd[0], buff, PIPE_SIZE);
        }
        close(fd[0]);
        exit(0);
    }

    exit(0);
}

上面的代码示例访问上面示例中创建的匿名管道,并且输出访问到的数据。