进程间通信方式

参考和学习视频:
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端口的服务器。客户端向服务器发送一条消息,并等待服务器的响应。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
error: Content is protected !!内容保护!!