linux基础编程:进程通信之管道

linux管道编程(实验4) linux管道编程 1.实验目的 ① 了解与掌握linux操作系统的基本操作 ② 了解并掌握管道的工作原理 2.实验平台 操作系统:Linux 3.实验内容和要求 (一)操作实验任务 ① 熟悉采用命令ln在文件与目录之间创建链接 ② 掌握硬链接和软链接的区别 ③ 首先在/opt目录下建立一个文件夹test_ln,然后将/tmp/file_temp/file1.txt 建立硬链接到/opt/test_ln ④ 将/tmp/file_temp/file1.txt 建立软链接到/opt/test_ln ⑤ 阅读附 阅读详情

在文章《linux基础编程:进程通信之信号》中,我们看到信号作为进程之间的通信方式。但是传送的信息之限于一个信号值。而本文将会介绍Linux支持的最初Unix IPC之一:管道和命名管道,它允许进程之间交换更多的数据,管道指的是从一个进程连接数据流到另一个进程。它具有以下特点:

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
  • 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

本文详细介绍管道相关信息。


管道


基于标准库popen和pclose的进程管道

#include <stdio.h>

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen允许一个程序将另一个程序作为新进程启动。并可以传递数据给它或者通过它接受数据。type仅能为"r"和“w”两种。当type="r"时,调用程序可以通过FILE文件流指针从被调用程序的输出获得数据;当type="w"时,调用程序可以通过fwrite函数向被调用程序发送数据。popen函数不支持任何其他选项。当popen启动的进程结束后,需要利用pclose关闭与之关联的文件流。如果在调用pclose时候,进程还在运行,那么pclose将会阻塞等待进程结束再返回。

static void check_image(char * imagename)
{
    FILE * fp;
    char command[120];
    char buf[200];
    bool ret;

    sprintf(command, "cksum %s", imagename);
    fp = popen(command, "r");
    if(fp == NULL) return;

    fgets(buf, 200, fp);
    fprintf(stdout, "Check Image:  %s... Done\n", buf);
    fclose(fp);
    return;
}


基于系统调用pipe的进程管道

#include <unistd.h>

int pipe(int pipefd[2]);

利用pipe创建的管道包含两个文件描述符fd[0]以及fd[1],需要注意的是,该处是文件描述符而不是文件流,对该文件描述符进行读写必须采用read和write系统调用,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。结构如下图:


从管道中读取数据:

  • 如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
  • 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red hat 7.2中为4096)。
向管道中写入数据:
  • 向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。 
  • 注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。
管道的主要局限性正体现在它的特点上:
  • 只支持单向数据流;
  • 只能用于具有亲缘关系的进程之间;
  • 没有名字;
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
int main()
{
    pid_t pid = 0;
    int fds[2], nwr = 0;
    char buf[128];

    pipe(fds);

    pid = fork();
    if(pid < 0)
    { 
        printf("Fork error.\n");
        return -1; 
    }else if(pid == 0)
    { 
        printf("This is child process, pid = %d\n", getpid());

        //part A
        printf("Child:waiting for message...\n");
        nwr = read(fds[0], buf, sizeof(buf))
        printf("Child:received\"%s\"\n", buf); 

        //part B
        printf("Child:send reply\n");
        strcpy(buf, "Reply from child");
        nwr = write(fds[1], buf, sizeof(buf)); 
        printf("Child:send %d bytes to parent.\n", nwr);

    }else{
        printf("This is parent process, pid = %d\n", getpid());

        printf("Parent:sending message...\n");
        strcpy(buf, "Message from parent");
        nwr = write(fds[1], buf, sizeof(buf));
        printf("Parent:send %d bytes to child.\n", nwr);

        //part C
        printf("Parent:waiting for reply from child...\n");
        nwr = read(fds[0], buf, sizeof(buf));
        printf("Parent:received \"%s\" from child\n", buf);
    }
    return 0;
}


命名管道


管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

对命名管道的操作和对文件操作相似,包括创建,打开,读写,和关闭操作。


命名管道的创建

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了吗,程序如下:

if(access(FIFO_NAME,F_OK)==-1){
       res=mkfifo(FIFO_NAME,0777);
       if(res!=0){
               fprintf(stderr,"Could not create fifo %s\n",FIFO_NAME);
               exit(EXIT_FAILURE);
      }
}
res=open(FIFO_NAME,open_mode);


命名管道的打开

打开的FIFO的限制是:由于FIFO是单向数据传输,程序不能以O_RDWR方式打开FIFO同时进行读写操作,只能是O_RDONLY或者O_WRONLY方式,打开函数如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
打开FIFO文件和普通文件的另外一个差别是:O_NONBLOCK选项对open的阻塞的影响,主要分为下面几种情况:

flags=O_RDONLY:open将会调用阻塞,除非有另外一个进程以写的方式打开同一个FIFO,否则一直等待。

flags=O_WRONLY:open将会调用阻塞,除非有另外一个进程以读的方式打开同一个FIFO,否则一直等待。

flags=O_RDONLY|O_NONBLOCK:如果此时没有其他进程以写的方式打开FIFO,此时open也会成功返回,此时FIFO被读打开,而不会返回错误。

flags=O_WRONLY|O_NONBLOCK:立即返回,如果此时没有其他进程以读的方式打开,open会失败打开,此时FIFO没有被打开,返回-1。

命名管道创建和打开测试程序:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc,char *argv[])
{
        int res;
        int open_mode=0;
        if(argc < 2){
                fprintf(stderr,"Usage:%s<some combination of \
                        O_RDONLY,O_WRONLY,O_NONBLOCK\n",*argv);
                exit(EXIT_FAILURE);
        }
        argv++;
        if(strncmp(*argv,"O_RDONLY",8)==0)open_mode|=O_RDONLY;
        if(strncmp(*argv,"O_WRONLY",8)==0)open_mode|=O_WRONLY;
        if(strncmp(*argv,"O_NONBLOCK",10)==0)open_mode|=O_NONBLOCK;
        argv++;
        if(*argv){
                if(strncmp(*argv,"O_RDONLY",8)==0)open_mode|=O_RDONLY;
                if(strncmp(*argv,"O_WRONLY",8)==0)open_mode|=O_WRONLY;
                if(strncmp(*argv,"O_NONBLOCK",10)==0)open_mode|=O_NONBLOCK;
        }
        if(access(FIFO_NAME,F_OK)==-1){
                res=mkfifo(FIFO_NAME,0777);
                if(res!=0){
                        fprintf(stderr,"Could not create fifo %s\n",FIFO_NAME);
                        exit(EXIT_FAILURE);
                }
        }
        printf("process %d open FIFO with %d\n",getpid(),open_mode);
        res=open(FIFO_NAME,open_mode);
        printf("process %d result %d\n",getpid(),res);
        sleep(5);
        if(res!=-1)close(res);
        printf("process %d finished\n",getpid());
        exit(EXIT_SUCCESS);
}


命名管道的读写

对命名管道的读写需要利用系统调用read函数:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

在读取管道时,是否采用O_NONBLOCK非阻塞标志对管道的读有影响:

  • 对一个空的,阻塞的FIFO文件的read调用将会等待,直到有数据可以读时才继续执行。
  • 对一个空的,非阻塞的FIFO的read系统调用将会立即返回0字节。

在写管道时,需要考虑FIFO可以存在的数据长度是有限制的,在limits.h文件中由#definde PIPE_BUF语句定义,通常是4096字节。

对于设置了阻塞标志的写操作:

  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
对于没有设置阻塞标志的写操作:
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;

命名管道的关闭

#include <unistd.h>

int close(int fd);


总结


管道常用于两个方面:在shell中时常会用到管道(作为输入输入的重定向),在这种应用方式下,管道的创建对于用户来说是透明的;用于具有亲缘关系的进程间通信,用户自己创建管道,并完成读写操作。FIFO可以说是管道的推广,克服了管道无名字的限制,使得无亲缘关系的进程同样可以采用先进先出的通信机制进行通信。管道和FIFO的数据是字节流,应用程序之间必须事先确定特定的传输"协议",采用传播具有特定意义的消息。要灵活应用管道及FIFO,理解它们的读写规则是关键。

【并发编程四】windows进程通信和Linux进程通信 原本写完上一篇[【并发编程三】C++进程通信(管道pipe)],就准备写消息队列的c++demo了,但是真正查找资料才发现网上都是linux下的消息队列的c++demo,就没有windows下的消息队列的进程通信的demwindows没有信号这个概念,但是会提供类似功能的api,比如,杀死进程:linux下的kill信号,windows下提供TerminateProcess的api接口。 阅读详情

相关推荐

VMvare 桥接模式无法联网、没有未桥接的主机网络适配器,无法将网络更改为桥接状态。

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!战项目源码讲义》点击传送门即可获取!**

q16974058的博客 4330

学习笔记——进程间通信之管道详解

重要概念!!! 进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。 IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。 管道 管道,顾名思义,是进程之间数据流输入输出的通道,我们称pipe管道,通常是把一个进程的输出通过管道连接到另一个进程的输入。管道又分为两种:无名管道(也就是我们说的管道)和FIFO(又称命名管道)

打不倒的程序猿 2103

保姆级教程:用Ansible 2.9自动化配置华为ENSP路由器(附Cloud连接避坑指南)

本文提供了一份详细的Ansible 2.9与华为ENSP路由器自动化配置教程,涵盖环境搭建、网络连通性配置、Playbook开发及典型问题排查。特别针对Cloud连接常见问题提供避坑指南,帮助运维工程师快速掌握网络自动化技能,提升工作效率。

weixin_42561476的博客 376

【Linux】进程间通信——管道

Linux进程间通信篇——匿名管道的学习

weixin_63449996的博客 2379

Apollo control模块横向控制原理及核心代码逐行解析

看完这篇文章,你会对apollo横向控制有一个整体的概念

weixin_39199083的博客 2万+

STM32外部中断EXTI原理与飞控实时应用实战

外部中断(EXTI)是嵌入式系统中实现事件驱动和实时响应的核心机制,其本质是将外部电平变化转化为可调度的CPU中断事件。基于ARM Cortex-M NVIC架构,EXTI通过GPIO-EXTI-NVIC三级硬件通路实现低延迟信号捕获,具备边沿触发、优先级管理与挂起控制等关键能力。在四轴飞控、工业传感器、人机交互等对毫秒级响应有强需求的场景中,EXTI显著优于轮询方式,兼顾实时性与CPU资源效率。实际开发需严格遵循SYSCFG映射→EXTI配置→NVIC使能的时序逻辑,并规避常见陷阱如中断号误配、伪触发、低

weixin_36382073的博客 65

【Linux】进程间通信——管道(1w5字图文详解,附代码和运行动图)

本篇文章围绕Linux中的管道展开讲解,包含进程间通信的概念和手段、匿名管道的概念、用法、特征、读写规则、应用场景和命名管道的概念、创建方式、用法和读写规则等内容

Eristic0618的博客 4530

把外部系统接到 SAP Build Process Automation 的两条路:前台直连与后台编排的完整范式

想把外部系统接入到你的【销售订单管理】审批流程,最稳妥的路径是:在流程里开一个,在 BTP 配好以为核心的 Destination,让前端经由approuter代理、后台通过 Cloud SDK 直连,任务处理用My Inbox或Task APIs,异步对接用。这套组合拳,既满足安全边界与运维规范,又能让前台与后台两个世界各取所需、自然衔接。SAP。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1660

操作系统:进程间通信 | 管道

进程间通信(Inter-Process Communication,简称IPC)是指在不同进程之间传播或交换信息我们知道:进程之间是独立的,所以进程之间的进程间通信一定不是两个进程直接通信的,为了保证进程间的独立性和实现进程间通信,操作系统就设计了若干种进程间通信方式,来实现多进程之间的协同工作。

weixin_73234157的博客 1960

sap中途启用物料账

spro->控制->产品成本控制->实际成本核算/物料分类帐->实际成本核算->激活实际成本的在产品。spro->控制->产品成本控制->实际成本核算/物料分类帐->实际成本核算->激活实际成本组件划分。spro->控制->产品成本控制->实际成本核算/物料分类帐->实际成本核算->激活实际成本核算。spro->控制->产品成本控制->实际成本核算/物料分类帐->激活评估范围的物料分类账。2.对于之前存在物料移动,但是期初没有库存的物料,物料分类账里追踪不到相关物料信息。1.需要激活物料分类账。

Yeol827的博客 993

ORB-SLAM3算法2之EuRoc开源数据集运行ORB-SLAM3生成轨迹并用evo工具评估轨迹

ORB-SLAM3算法1 已成功编译安装ORB-SLAM3到本地,本篇目的是用EuRoc开源数据来运行ORB-SLAM3,并生成轨迹,最后用evo评估工具来评估ORB-SLAM3生成的轨迹和真值轨迹。

重点记录机器人和自动驾驶相关领域的技术,尤其是计算机视觉,视觉SLAM等,不仅有前言文献或必备算法的学习,也会有实际应用的点滴经验 3762

进程间通信之管道(初学者必备)

进程间通信的介绍 进程间通信的概念 进程间通信 (Inter-Process Communication,IPC)则是多进程协作的基础。 一般而言,IPC至少需要两方 (如两个进程)参与。 根据信息流动的方向,这两方通常被称为发送者和接收者。 进程间通信的目的 数据传输:一个进程需要将它的数据交给另外一个进程 资源共享:多个进程之间共享同样的资源 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。 进程控制:有些进程希望完全控制另一个进程的执行(

qq_56999918的博客 2297

验证方法、符合性方法与设计保证的关系

未来,随着适航标准和工程实践的不断演进,验证与符合性方法将持续优化,以应对日益复杂的系统设计需求。设计保证过程中的验证活动具有双重作用,既用于设计过程的质量控制和风险管理,又作为适航符合性方法的重要支持材料。在未来的航空电子系统开发中,进一步优化设计保证与验证活动的协同作用,将有助于应对日益复杂的工程挑战,推动航空安全技术的发展。为了全面理解这二者之间的关系,本章将系统说明设计保证过程中的验证活动如何既服务于设计保证,又作为符合性方法中的核心证明材料。最终,通过功能测试和环境测试验证硬件的实际性能。

qq_41854911的博客 554

Linux:匿名管道的五大特性(进程间通信三)

元旦快乐,期末周悲伤,这两个星期没时间学习计算机相关内容啦,今天是开年的的一篇博客,延续上一篇博客内容,今天我们来谈谈匿名管道的五大特征~~先说结论,

hello world/linux 1288

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

进程间通信IPC(2)命名管道,system V 共享内存,mmap文件映射

本文介绍了Linux进程间通信的三种主要方式:命名管道、System V共享内存和mmap文件映射。命名管道通过mkfifo创建管道文件实现无血缘关系进程间的通信,并演示了客户端-服务器通信示例。System V共享内存是最高效的IPC方式,直接在物理内存创建共享区域映射到进程地址空间,避免了频繁系统调用开销。mmap文件映射则通过文件映射到内存实现数据共享。文章详细阐述了命名管道的实现原理和代码示例,包括管道创建、打开、读写操作及存在性检查等关键实现细节。同时解释了共享内存的管理机制和内核数据结构。

20 • backend dev • building things that just work • learning systems & low level stuff • love video games 649

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

嵌入式学习——Linux

操作系统本质上是一款软件,作用是屏蔽底层各式各样的硬件平台,向上层应用提供统一接口,以此实现通用软件代码。

koi77u的博客 216

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Linux系统编程:进程间通信之管道

在某些场景下我们需要不同进程之间进行(1)基本数据的交互。(2)发送命令。(3)实现某种协同。(4)通知某些信息……

weixin_51142926的博客 4342

【Linux】进程通信之管道通信详解

详细介绍管道通信的概念和原理、管道通信的实现和应用、管道通信的优化和问题

阿润菜菜的博客 8821

Linux编程:4、进程通信-管道(匿名管道)

【代码】Linux编程:4、进程通信-管道(匿名管道)

m0_75107602的博客 1020

【Linux操作系统】详解Linux系统编程中的管道进程通信

fifo函数:在C标准库中没有名为fifo的函数。命名管道(FIFO):命名管道是一种特殊的文件,可以在文件系统中创建,并且可以被不同的进程打开和读写。使用mkfifo函数可以创建命名管道。兄弟进程间通信:兄弟进程是指由同一个父进程创建的多个子进程。兄弟进程间通信可以使用命名管道实现,其中一个进程向命名管道写入数据,另一个进程从命名管道读取数据。非血缘关系进程间通信:非血缘关系的进程是指没有共同的父进程的进程。

Goforyouqp的博客 7604

【Linux杂货铺】进程通信

【Linux杂货铺】进程通信。 欢迎收看本期【Linux杂货铺】,本期内容将讲解进程是如何实现通信的,即资源共享。本篇文章主要聚焦于本地通信。重点讲解管道,共享内存实现进程通信,拓展了解消息队列,信号量等内容。

jupangMZ的博客 3787

【Linux系统编程】管道

Linux系统编程 管道

lyh030105的博客 1241

Linux编程:5、进程通信-命名管道

定义:命名管道是一种特殊的文件,不同于匿名管道,它有文件名,可在文件系统中看到。用途:解决匿名管道只能在有血缘关系进程(如父子进程或者有共同祖先的进程)间通信的限制,允许无血缘关系的进程间通信。

m0_75107602的博客 1067

【Linux进程通信 —— 管道】

在 Linux 中,进程间通信(IPC,Inter-Process Communication)是指不同进程之间交换数据和信息的一种机制。这种通信可以是在同一台计算机上的不同进程之间,也可以是在不同计算机之间的进程之间。管道(Pipe)是一种用于进程间通信的机制,允许一个进程的输出直接成为另一个进程的输入。它主要用于在父进程和子进程之间或者在同时运行的两个进程之间进行通信。管道可以分为匿名管道(Anonymous Pipe)和命名管道(Named Pipe)两种类型。

jokr_的博客 987

<Linux进程通信之管道>——《Linux》

<Linux进程通信之管道>——《Linux》

m0_57859086的博客 1365
上一篇: linux基础编程:进程通信之信号
下一篇: linux基础编程:进程通信之System V IPC:消息队列,信号量,共享内存
ColZer
博客等级 码龄14年 42粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值