Linux程序开发(八):操作系统进程通信编程

news2025/1/10 23:32:31

Tips:"分享是快乐的源泉💧,在我的博客里,不仅有知识的海洋🌊,还有满满的正能量加持💪,快来和我一起分享这份快乐吧😊!

喜欢我的博客的话,记得点个红心❤️和小关小注哦!您的支持是我创作的动力!数据源存放在我的资源下载区啦!

Linux程序开发(八):操作系统进程通信编程

目录

  • Linux程序开发(八):操作系统进程通信编程
    • 1. 问答题
      • 1.1. 操作系统中进程通信的作用?
      • 1.2. Linux进程间通信有哪几种方式?这几种方式之间的特点是什么?
      • 1.3. 查看以下代码:
    • 2. 编程题
      • 2.1. 利用dup/dup2实现往文件中写入数据。要求:在代码中执行两次以下语句:
      • 2.2. 编写程序实现如下功能:创建父子进程,父子进程之间通过管道进行通信,父程向子进程发送英文字符串,子进程接收到该字符串后,将该字符串倒序,并附加上自己的进程pid传回给父进程。
      • 2.3. 利用无名管道pipe()函数、创建进程fork()函数,实现ps -uax | grep root |wc -l命令。
      • 2.4. 用popen实现无名管道,完成ps -uax|grep root。

1. 问答题

1.1. 操作系统中进程通信的作用?

操作系统中进程通信的作用是允许不同的进程之间进行数据交换和协同工作,以实现共享资源、并发执行和协作完成任务。以下是进程通信的几个主要作用:

1.数据共享:进程通信允许不同的进程之间共享数据。通过共享内存、文件、管道、套接字等通信机制,进程可以将数据传递给其他进程,从而实现信息共享和协同处理。

2.资源共享:多个进程可以通过进程通信来共享系统资源,如文件、设备、数据库等。例如,一个进程可以将其打开的文件描述符传递给另一个进程,使其可以读取或写入同一个文件。

3.进程协作:进程通信使得不同的进程能够协同工作,共同完成复杂的任务。例如,一个进程可以将任务分解为多个子任务,并将这些子任务分配给其他进程进行处理,最后再将结果合并。

4.进程同步:当多个进程同时访问共享资源时,需要进行进程同步以避免竞态条件和数据不一致问题。进程通信提供了各种同步机制,如信号量、互斥锁、条件变量等,用于控制进程的访问顺序和临界区的互斥访问。

5.进程间通信:进程通信还可以用于不同计算机之间的通信,实现分布式系统和网络应用。通过网络协议和套接字等通信方式,进程可以在不同主机上进行远程通信和数据交换。

总而言之,进程通信在操作系统中起着重要的作用,它使得不同的进程能够相互协作、共享资源和信息,并实现并发执行和分布式计算。

1.2. Linux进程间通信有哪几种方式?这几种方式之间的特点是什么?

在Linux中,常见的进程间通信方式包括以下几种:

1.管道(Pipe):管道是一种半双工的通信方式,用于具有亲缘关系的父子进程之间或者兄弟进程之间的通信。管道通过创建一个字节流来实现进程间的数据传输,数据在管道中按先进先出(FIFO)的顺序传递。
特点:
(1)只能用于具有亲缘关系的进程间通信。
(2)只能实现单向通信,需要建立两个管道才能实现双向通信。
(3)数据传输效率高,但容量有限。

2.命名管道(Named Pipe):命名管道也是一种半双工的通信方式,但不同于管道,它允许无亲缘关系的进程之间进行通信。命名管道在文件系统中有对应的文件节点,可以通过文件路径进行访问。
特点:
(1)适用于无亲缘关系的进程间通信。
(2)可以实现单向或双向通信。
(3)数据传输效率高,但容量有限。

3.共享内存(Shared Memory):共享内存是一种高效的进程间通信方式,它允许多个进程直接访问同一块物理内存区域,从而实现数据共享。进程通过将共享内存映射到各自的虚拟地址空间中,来进行读写操作。
特点:
(1)数据传输效率高,因为进程直接访问内存而无需复制数据。
(2)需要进程间进行同步和互斥操作,以避免竞态条件和数据一致性问题。
(3)适用于大量数据交换和频繁访问的场景。

4.消息队列(Message Queue):消息队列是一种按消息进行通信的方式,消息被放入队列中并按照一定的优先级被取出。进程可以通过消息队列发送和接收消息,实现进程间的异步通信。
特点:
(1)适用于多个进程之间的数据传输和同步。
(2)支持点对点和发布/订阅模式。
(3)可以设置消息的优先级,确保高优先级消息先被处理。
(4)适用于多种类型和大小的数据传输。

5.信号量(Semaphore):信号量是一种计数器,用于控制多个进程对共享资源的访问。进程可以通过对信号量进行P(等待)和V(释放)操作来实现进程间的同步和互斥。
特点:
(1)主要用于进程间的同步和互斥。
(2)可以用于限制对共享资源的访问数量。
(3)适用于多进程并发控制。

6.套接字(Socket):套接字是一种网络编程接口,可以用于不同主机之间的进程通信。通过套接字,进程可以在网络上发送和接收数据,实现远程通信。
特点:
(1)适用于不同主机之间的进程通信。
(2)支持多种传输协议和通信方式,如TCP、UDP等。
(3)能够实现分布式计算和网络应用。

1.3. 查看以下代码:

int fd1, fd2, fd3, fd4;
fd1 = open("a.txt", O_RDONLY);
fd2 = open("b.txt", O_WRONLY);
fd3 = dup(fd1);
fd4 = dup2(fd2, 0);
![image-6.png](attachment:image-6.png)

假设当前终端没有打开任何正常文件,请问:最后fd1、fd2、fd3和fd4的值为多少?并解释原因。

提示:

  • 系统调用dup()和dup2()都能够复制文件描述符。
  • dup返回新的文件描述符,成功时返回最小的尚未被使用的文件描述符。
  • dup2可以让用户指定返回的文件描述符的值,它通常用来重新打开或者重定向一个文件描述符。
# 根据给定的代码片段和提示,可以得出以下结论:

fd1:调用open("a.txt", O_RDONLY)打开文件"a.txt",以只读方式打开,返回一个新的文件描述符。因为当前终端没有打开任何正常文件,所以open()函数返回的文件描述符应该是最小的尚未被使用的文件描述符。因此,fd1的值应为一个大于或等于3的整数。

fd2:调用open("b.txt", O_WRONLY)打开文件"b.txt",以只写方式打开,返回一个新的文件描述符。同样地,因为当前终端没有打开任何正常文件,所以fd2的值应该是一个大于或等于3的整数,且与fd1不同。

fd3:调用dup(fd1)复制文件描述符fd1,返回一个新的文件描述符。由于dup函数复制的是最小的尚未被使用的文件描述符,所以fd3的值应该等于fd1的值。

fd4:调用dup2(fd2, 0)将文件描述符fd2复制到标准输入文件描述符0,并返回0。因为dup2函数可以让用户指定返回的文件描述符的值,这里将fd2复制到了标准输入文件描述符0,所以fd4的值应为0。

综上所述:

fd1的值应为大于或等于3的整数。
fd2的值应为大于或等于3的整数,且与fd1不同。
fd3的值应与fd1相同。
fd4的值应为0。

2. 编程题

2.1. 利用dup/dup2实现往文件中写入数据。要求:在代码中执行两次以下语句:

printf(“Hello Linux\n”);

前一次输出到文件c.txt中,后一次输出到屏幕上。

提示:printf语句后增加fflush(NULL);语句,刷新打开的流。

(1)编写C语言程序dup_test.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("c.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("Failed to open file");
        return 1;
    }

    int stdout_copy = dup(STDOUT_FILENO); // 备份stdout文件描述符

    // 将标准输出重定向到文件
    if (dup2(fd, STDOUT_FILENO) == -1) {
        perror("Failed to redirect stdout to file");
        close(fd);
        return 1;
    }

    printf("Hello Linux\n");
    fflush(NULL); // 刷新输出流

    // 恢复标准输出到屏幕
    if (dup2(stdout_copy, STDOUT_FILENO) == -1) {
        perror("Failed to restore stdout");
        close(fd);
        return 1;
    }

    printf("Hello Linux\n"); // 输出到屏幕

    close(fd);
    return 0;
}

(2)用gcc编译器进行程序编译

gcc -o dup_test dup_test.c

在这里插入图片描述

(3)运行程序

./dup_test

在这里插入图片描述

2.2. 编写程序实现如下功能:创建父子进程,父子进程之间通过管道进行通信,父程向子进程发送英文字符串,子进程接收到该字符串后,将该字符串倒序,并附加上自己的进程pid传回给父进程。

例如:父进程发出Hello World!;子进程处理返回!dlroW olleH2709,2709是子进程号。

(1)在linux上编写c语言程序pipe_communication.c

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

#define BUFFER_SIZE 100

int main() {
    int fd[2]; // 管道文件描述符数组
    pid_t pid;
    char input_string[] = "Hello World!";
    char read_buffer[BUFFER_SIZE];

    if (pipe(fd) < 0) {
        perror("Pipe creation failed");
        exit(EXIT_FAILURE);
    }

    pid = fork();
    if (pid < 0) {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }

    if (pid > 0) {  // 父进程
        close(fd[0]); // 关闭读端

        // 向子进程发送字符串
        write(fd[1], input_string, strlen(input_string)+1);
        close(fd[1]); // 关闭写端

        // 从子进程读取处理后的字符串
        read(fd[0], read_buffer, BUFFER_SIZE);
        printf("Received from child process: %s\n", read_buffer);

        close(fd[0]); // 关闭读端
    } else {  // 子进程
        close(fd[1]); // 关闭写端

        // 从父进程接收字符串
        read(fd[0], read_buffer, BUFFER_SIZE);
        printf("Received from parent process: %s\n", read_buffer);

        // 处理字符串,倒序并附加进程id
        int pid = getpid();
        int length = strlen(read_buffer);
        for (int i = 0; i < length / 2; i++) {
            char temp = read_buffer[i];
            read_buffer[i] = read_buffer[length - i - 1];
            read_buffer[length - i - 1] = temp;
        }
        char pid_str[20];
        sprintf(pid_str, "%d", pid);
        strcat(read_buffer, pid_str);

        // 将处理后的字符串发送给父进程
        write(fd[1], read_buffer, strlen(read_buffer)+1);
        close(fd[0]); // 关闭读端
        close(fd[1]); // 关闭写端
    }

    return 0;
}

(2)用gcc编译器进行程序编译

gcc -std=gnu99 -o pipe_communication pipe_communication.c

在这里插入图片描述

(3)运行程序

./pipe_communication

在这里插入图片描述

2.3. 利用无名管道pipe()函数、创建进程fork()函数,实现ps -uax | grep root |wc -l命令。

(1)在linux上编写c语言程序pipe_test.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define READ_END 0
#define WRITE_END 1

int main() {
    int pipe1[2], pipe2[2];
    pid_t pid1, pid2;

    if (pipe(pipe1) == -1 || pipe(pipe2) == -1) {
        perror("Pipe creation failed");
        exit(EXIT_FAILURE);
    }

    pid1 = fork();
    if (pid1 < 0) {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    } else if (pid1 == 0) { // 子进程1
        close(pipe1[READ_END]);
        dup2(pipe1[WRITE_END], STDOUT_FILENO);
        execlp("ps", "ps", "-uax", NULL);
        perror("Exec failed");
        exit(EXIT_FAILURE);
    } else { // 父进程
        pid2 = fork();
        if (pid2 < 0) {
            perror("Fork failed");
            exit(EXIT_FAILURE);
        } else if (pid2 == 0) { // 子进程2
            close(pipe1[WRITE_END]);
            close(pipe2[READ_END]);
            dup2(pipe1[READ_END], STDIN_FILENO);
            dup2(pipe2[WRITE_END], STDOUT_FILENO);
            execlp("grep", "grep", "root", NULL);
            perror("Exec failed");
            exit(EXIT_FAILURE);
        } else { // 父进程
            close(pipe1[READ_END]);
            close(pipe1[WRITE_END]);
            close(pipe2[WRITE_END]);
            dup2(pipe2[READ_END], STDIN_FILENO);
            execlp("wc", "wc", "-l", NULL);
            perror("Exec failed");
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}

(2)用gcc编译器进行程序编译

gcc -o pipe_test pipe_test.c

在这里插入图片描述

(3)运行程序

./pipe_test
ps -uax | grep root |wc -l

在这里插入图片描述

2.4. 用popen实现无名管道,完成ps -uax|grep root。

(1)在linux上编写c语言程序popen.c

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

int main() {
    FILE* fp1, *fp2;
    char cmd1[] = "ps -uax";
    char cmd2[] = "grep root";

    fp1 = popen(cmd1, "r");
    if (fp1 == NULL) {
        perror("Popen failed");
        exit(EXIT_FAILURE);
    }

    fp2 = popen(cmd2, "w");
    if (fp2 == NULL) {
        perror("Popen failed");
        exit(EXIT_FAILURE);
    }

    int c;
    while ((c = fgetc(fp1)) != EOF) {
        fputc(c, fp2);
    }

    pclose(fp1);
    pclose(fp2);

    return 0;
}

(2)用gcc编译器进行程序编译

gcc -o popen popen.c

在这里插入图片描述

(3)运行程序

./popen
ps -uax|grep root

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1684101.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java面向对象-常用类 (包装类)

常用类 – 包装类 基本数据类型的包装类 理解&#xff1a;包装类是8种基本数据类型对应的类 出现原因&#xff1a;Java是一种纯面向对象语言&#xff0c;但是java中有8种基本数据类型&#xff0c;破坏了java为纯面向对象的特征。为了承诺在java中一切皆对象&#xff0c;java…

【Hive SQL 每日一题】分析电商平台的用户行为和订单数据

需求描述 假设你是一位数据分析师&#xff0c;负责分析某电商平台的用户行为和订单数据&#xff0c;平台上有多个用户&#xff0c;用户可以在不同的日期下单&#xff0c;每个订单包含多个商品。请你完成相关业务分析&#xff0c;帮助平台优化运营策略和用户体验。 数据准备 …

音视频-常用的分析工具介绍-连续补充

目录 1&#xff1a;Audacity 2&#xff1a;MediaInfo 3&#xff1a;MP4Box 4&#xff1a;hexinator 5&#xff1a;Adobe Audition 6&#xff1a;VideoEye 7&#xff1a;YUVplayer &#xff08;YUV/RGB播放器&#xff09; 在做音视频分析时&#xff0c;经常用到各种分析工…

Sentinel的隔离和降级

文章目录 1、概念简介2、FeignClient整合Sentinel2.1、修改配置&#xff0c;开启sentinel功能2.2、编写失败降级逻辑2.3、总结 3、线程隔离&#xff08;舱壁模式&#xff09;3.1、线程隔离的实现方式3.2、sentinel的线程隔离1&#xff09;配置隔离规则2&#xff09;Jmeter测试 …

flink程序本地运行报: A JNI error has occurred和java.lang.NoClassDefFoundError

1.问题描述 在idea中运行flink job程序出现如下错误&#xff1a; Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/flink/api/common/io/FileInputFormat …

Excel模板计算得出表格看板

背景 表格看板及导出&#xff0c;单元格时间年是根据筛选器时间变化的 较往年和往年是计算单元格 思路 1.通过excel模板来把数据填入excel再数据清洗得到数据返回前端 2.数据填充&#xff0c;通过行列作为key 列如&#xff1a;key整体20241月&#xff0c;根据key匹配数据填…

资料防拷贝该如何实现?数据防拷贝的方法有哪些

数据安全和隐私保护成为企业和个人关注的重点。电脑中存储的资料往往包含了重要的商业机密、个人隐私或其他敏感信息。 因此&#xff0c;如何有效防止他人非法拷贝电脑资料&#xff0c;成为了一个亟待解决的问题。 本文将探讨数据防拷贝的方法&#xff0c;以帮助企业和个人保护…

linux查看硬盘信息

1、查看挂接的分区状态 [rootMaster ~]# fdisk -l |grep Disk 2、查看硬盘和分区分布 [rootMaster ~]# lsblk 3、查看硬盘和分区的详细信息 [rootMaster ~]# fdisk -l 4、查看挂接的分区状态 [rootMaster ~]# swapon -s 5、查看硬盘使用情况 [rootMaster ~]# df -hT 6、硬…

Mysql总结1

Mysql常见日志 &#xff08;1&#xff09;错误日志&#xff1a;记录数据库服务器启动、停止、运行时存在的问题&#xff1b; &#xff08;2&#xff09;慢查询日志&#xff1a;记录查询时间超过long_query_time的sql语句&#xff0c;其中long_query_time可配置&#xff0c;且…

docker所在磁盘空间不足 迁移数据

1.查看原始目录docker info | grep "Docker Root Dir" 一般在/var/lib/docker 2.停止docker service docekr stop 3.移动数据 注意 移动前不要创建docker目录&#xff01; mv /var/lib/docker /home/docker 4.进入目录查看是否与原始目录相同&#xff0c;确认一…

精准键位提示,键盘盲打轻松入门

在说明精准键位提示之前&#xff0c;我们先来看一张图&#xff1a; 这是一张标准的基准键位图&#xff0c;也就是打字时我们双手的8个手指放在基准键位上&#xff0c;在打不同的字母时&#xff0c;我们的手指以基准键位为中心&#xff0c;或上、或下、或左、或右&#xff0c;在…

全域运营是本地生活的下半场?新的创业风口来了?

随着全域概念的兴起&#xff0c;全域运营赛道也逐渐进入人们的视野之中&#xff0c;甚至有业内人士预测&#xff0c;全域运营将会是本地生活下半场的大趋势。 之所以这么说&#xff0c;是因为全域运营作为包含了公域和私域内所有运营业务的新模式&#xff0c;不仅能同时做所有本…

楼道堆积物视觉识别监控系统

楼道堆积物视觉识别监控系统采用了AI神经网络和深度学习算法&#xff0c;楼道堆积物视觉识别监控系统通过摄像头实时监测楼道的情况&#xff0c;通过图像处理、物体识别和目标跟踪算法&#xff0c;系统能够精确地识别楼道通道是否被堆积物阻塞。楼道堆积物视觉识别监控系统检测…

RA-RISK ANALYSIS

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、咨询 一、期刊简介 Risk Analysis代表风险分析学会出版&#xff0c;在ISI期刊引文报告中的社会科学、数学方法类别中排名前10位&#xff0c;为风险分析领域的新发展提供了焦点。这本国际同行评审期刊致力于发表…

面试准备【面试准备】

面试准备【面试准备】 前言面试准备自我介绍&#xff1a;项目介绍&#xff1a; 论坛项目功能总结数据库表设计注册功能登录功能显示登录信息功能发布帖子评论私信点赞功能关注功能通知搜索网站数据统计热帖排行缓存 论坛项目技术总结Http的无状态cookie和session的区别为什么要…

Python TCP编程简单实例

客户端&#xff1a;创建TCP链接时&#xff0c;主动发起连接的叫做客户端 服务端&#xff1a;接收客户端的连接 连接其他服务器 可以通过tcp连接其他服务器。 示例&#xff1a; import socket# 1.创建一个socket # 参数1&#xff1a;指定协议 AF_INET&#xff08;ipv4&#…

TSMaster发送CAN报文

打开TSMaster工程 从菜单栏打开CAN报文发送窗口&#xff1a;【分析】->【报文发送】->【添加CAN/CAN FD发送】 可以选择【从CAN数据库添加报文】或者是【添加新的原始报文】 方法一 添加新的原始报文 可以配置报文发送的触发方式&#xff0c;有【手动】和【周期】两种。…

Linux多线程系列三: 生产者消费者模型,信号量使用,基于阻塞队列和环形队列的这两种生产者消费者代码的实现

Linux多线程系列三: 生产者消费者模型,信号量,基于阻塞队列和环形队列的这两种生产者消费者代码的实现 一.生产者消费者模型的理论1.现实生活中的生产者消费者模型2.多线程当中的生产者消费者模型3.理论 二.基于阻塞队列的生产者消费者模型的基础代码1.阻塞队列的介绍2.大致框架…

力扣刷题--2733. 既不是最小值也不是最大值【简单】

题目描述 给你一个整数数组 nums &#xff0c;数组由 不同正整数 组成&#xff0c;请你找出并返回数组中 任一 既不是 最小值 也不是 最大值 的数字&#xff0c;如果不存在这样的数字&#xff0c;返回 -1 返回所选整数。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,1,…

docker三种自定义网络(虚拟网络) overlay实现原理

docker提供了三种自定义网络驱动&#xff1a;bridge、overlay、macvlan。 bridge驱动类似默认的bridge网络模式。 overlay和macvlan是用于创建跨主机网络。 支持自定义网段、网关&#xff0c;docker network create --subnet 172.77.0.0/24 --gateway 172.77.0.1 my_n…