Linux系统:InterProcessCommunication进程间通信

进程间通信(Inter-Process Communication,IPC)是指两个或多个进程之间进行数据交换和共享信息的机制。在操作系统中,不同的进程可能运行在不同的地址空间,因此需要一些特殊的机制来使它们能够协同工作。

以下是一些常见的进程间通信的方式:

  1. 管道(Pipes):

    • 单向通信,用于父子进程或者兄弟进程之间的通信。
    • 可以是匿名管道(使用pipe函数)或命名管道(使用mkfifo函数)。
  2. 消息队列(Message Queues):

    • 允许进程通过消息进行通信,可以实现多对多的通信。
    • 使用msggetmsgsndmsgrcv等系统调用。
  3. 共享内存(Shared Memory):

    • 两个或多个进程可以访问相同的内存区域,实现高效的数据交换。
    • 使用shmgetshmatshmdt等系统调用。
  4. 信号量(Semaphores):

    • 用于控制多个进程对共享资源的访问,防止竞争条件。
    • 使用semgetsemop等系统调用。
  5. 套接字(Sockets):

    • 在网络编程中使用,也可以用于本地进程通信。
    • 使用socketbindconnect等系统调用。
  6. 文件锁(File Locking):

    • 使用文件锁机制来实现进程间的同步。
    • 使用fcntl系统调用。
  7. 信号(Signals):

    • 用于通知进程发生了某个事件。
    • 使用killsignal等系统调用。
  8. 命名管道(Named Pipes):

    • 类似于匿名管道,但允许无关的进程之间进行通信。
    • 使用mkfifo函数。

下面看几个具体的例子:

eg.1.创建一个子进程,子进程向管道写入一段字符串,而父进程从管道读取该字符串并打印出来
#include <sys/wait.h>

void main()
{
    int x, fd[2];//定义整型变量和一个包含两个元素的整型数组,用于存储管道的文件描述符。
    char buf[30], s[30];//定义两个字符数组,用于存储要传输的数。

    // 创建管道
    if (pipe(fd) == -1) {//创建管道,fd[0]用于读取,fd[1]用于写入。
        perror("pipe");
        return;
    }

    // 创建子进程
    while ((x = fork()) == -1);//使用fork创建子进程,如果创建失败则一直尝试。

    if (x == 0)/*在Linux系统中,fork 函数调用成功后,它会在父进程和子进程中分别返回不同的值:在父进程中,fork 返回子进程的进程ID(PID),这是一个正整数。在子进程中,fork 返回0。因此,if (x == 0) 这样的判断语句实际上是在检查当前代码是否正在子进程中执行。如果条件成立(x 等于 0),那么就执行对应于子进程的代码块。*/
    {
        // 子进程向管道写入数据
        sprintf(buf, "This is an example \n");//将字符串格式化并存储到buf数组中。
        if (write(fd[1], buf, 30) == -1) {//将数据写入管道。
            perror("write");
            return;
        }
    }
    else//在父进程中执行以下代码块。
    {
        // 父进程等待子进程执行完毕
        wait(NULL);//等待子进程执行完毕

        // 父进程从管道读取数据
        if (read(fd[0], s, 30) == -1) {//从管道读取数据到s数组。
            perror("read");
            return;
        }

        // 打印从管道读取的数据
        printf("%s", s);
    }
}

编译运行:

终端输出:

eg.2.一个父进程创建两个子进程的示例,通过无名管道进行通信
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/wait.h>

void main() {
    int i, r, p1, p2, fd[2];
    char buf[50], s[50];

    // 创建管道
    pipe(fd);

    // 创建第一个子进程 (p1)
    while ((p1 = fork()) == -1);
    if (p1 == 0) {
        // 在子进程 p1 中锁定写入管道的文件描述符
        lockf(fd[1], 1, 0);//锁定整个文件,以确保在写入数据到管道的时候,不会被其他进程同时写入。

        // 向管道中写入消息
        sprintf(buf, "child process p1 is sending messages!\n");
        printf("child process p1!\n");
        write(fd[1], buf, 50);

        // 等待5秒
        sleep(5);

        // 解锁文件描述符
        lockf(fd[1], 0, 0);

        // 子进程 p1 退出
        exit(0);
    } else {
        waitpid(p1,NULL,0);
        // 创建第二个子进程 (p2)
        while ((p2 = fork()) == -1);
        if (p2 == 0) {
            // 在子进程 p2 中锁定写入管道的文件描述符
            lockf(fd[1], 1, 0);

            // 向管道中写入消息
            sprintf(buf, "child process p2 is sending messages\n");
            printf("child process p2!\n");
            write(fd[1], buf, 50);

            // 等待5秒
            sleep(5);

            // 解锁文件描述符
            lockf(fd[1], 0, 0);

            // 子进程 p2 退出
            exit(0);

        } else {
            // 等待第一个子进程 p1 结束
            wait(0);

            // 从管道中读取数据到缓冲区 s
            if ((r = read(fd[0], s, 50)) == -1)
                printf("can't read pipe\n");
            else
                printf("%s\n", s);

            // 等待第二个子进程 p2 结束
            wait(0);

            // 再次从管道中读取数据到缓冲区 s
            if ((r = read(fd[0], s, 50)) == -1)
                printf("can't read pipe\n");
            else
                printf("%s\n", s);

            // 父进程退出
            exit(0);
        }
    }
}

~                                                                                                                                                                              ~                                                                                                                                                                              ~                                        

--------------------------------------------------------------------------------------------------------------------------------

sprintf()和printf()的区别:

`sprintf` 和 `printf` 都是 C 语言中用于格式化输出的函数,但它们有一些关键的区别:

1. 输出位置:
   - `printf` 将格式化的文本输出到标准输出流(通常是终端或控制台)。
   - `sprintf` 将格式化的文本输出到字符串中,而不是标准输出。你需要提供一个目标字符串的缓冲区,`sprintf` 将格式化的内容写入这个缓冲区。

2. 返回值:
   - `printf` 的返回值是输出的字符数(成功时),如果发生错误,则返回负值。
   - `sprintf` 的返回值是写入字符串的字符数,不包括字符串结尾的空字符 `\0`。

3. 用途:
   - `printf` 主要用于在屏幕上输出信息,方便用户查看。
   - `sprintf` 主要用于将格式化的字符串保存到内存中,以后可以进一步使用或处理。

简单的一个示例:


#include <stdio.h>

int main() {
    char buffer[100];
    int value = 42;

    // 使用 sprintf 将格式化的内容写入缓冲区
    sprintf(buffer, "The value is: %d", value);

    // 使用 printf 将格式化的内容输出到屏幕
    printf("The value is: %d\n", value);

    // 输出 sprintf 的结果
    printf("sprintf result: %s\n", buffer);

    return 0;
}
```

在这个例子中,`sprintf` 将格式化的字符串写入了 `buffer`,而 `printf` 直接将格式化的内容输出到屏幕。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值