一种用于实现在进程间进行数据交互访问的方法及其装置的制作方法

文档序号:6362527阅读:182来源:国知局
专利名称:一种用于实现在进程间进行数据交互访问的方法及其装置的制作方法
技术领域
本申请涉及进程间数据访问,尤其涉及一种用于实现在进程间进行数据交互访问的方法及其装置。
背景技术
在实际应用中,为了提高设备处理复杂数据的能力和速度,经常需要进程间相互协作,共同完成任务,例如,完成特定的数据传输、数据共享、事件通知、资源共享、进程控制等任务。在已知技术中,由于各进程的用户空间(即各进程私有的虚拟内存地址空间)是相互独立的,不能直接相互访问,因此,进程间进行数据交换的常用方式只有通过共享文件、匿名管道、信号量、消息队列和共享内存等技术手段来进行,它们所使用的数据交换方式都是由一个进程向特定的区域写入数据,然后,另一个进程从这些区域读出上一个进程所写入的数据,从而达到进程间相互协作的目的。然而上述已有的实现方案仅仅是实现了进程间数据的共享,而不能提供进程间数据之间关系的共享,即不能实现进程间数据的交互访问。例如专利申请编号200610067532.2的专利中就描述了进程间数据共享的一种实现,但不能提供进程间数据的交互式访问,尤其不能提供进程间数据的直接的实时交互式访问。其次,在目前的实现进程间相互协作的方案中,对共享内存的管理大多采用简单的链式管理,即在共享内存中简单地创建一个链式存储结构的链表来存储进程在运行中产生的一些结果数据。因此目前的实现进程间相互协作的方案既不能提供进程间数据的直接的实时交互式访问,也不能对共享内存进行高效的利用。

发明内容
本申请提供了一种用于实现在进程间进行数据交互访问的方法,所述方法包括:步骤1,将需要交互访问的多个进程连接到同一个共享内存段;以及步骤2,将所述多个进程中需要共享的数据结构分配到所述共享内存段,以使所述多个进程之间共享所述数据结构包含的成员变量的状态关系,和/或所述数据结构产生的结果数据之间的关系。本申请还提供了一种用于实现在进程间进行数据交互访问的装置,所述装置包括:连接模块,用于将需要交互访问的多个进程连接到同一个共享内存段;以及分配模块,用于将所述多个进程中需要共享的数据结构分配到所述共享内存段,以使所述多个进程之间共享所述数据结构包含的成员变量的状态关系,和/或所述数据结构产生的结果数据之间的关系。根据本申请的用于实现在进程间进行数据交互访问的方法和装置,不但为进程间数据的直接的实时交互式访问提供了一种可能的实现,而且,本申请可以是跨操作系统平台的。另外,如前所述,由于本申请所采用的共享内存管理方式类似于Linux内核的slab管理方式,因此,本申请还能够实现对共享内存的高效管理。


下面将参照所附附图来描述本申请的实施例,其中:图1示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中进程获取共享内存的一个实施例的示意图;图2示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中共享内存初始化后的内存池结构的一个实施例的示意图;图3示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中使用一段时间后的slab结构的一个实施例的示意图;图4示例性示出了在现有技术的用于实现进程间相互协作的方法中进程共享数据的示意图;图5示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中进程共享数据与数据之间的关系的示意图;以及图6示例性示出了本申请的用于实现在进程间进行数据交互访问的装置的示意图。
具体实施例方式下面将结合图1-图5详细描述本申请,其中,结合图1-图3将描述本申请如何对共享内存进行高效管理,结合图4-图5将描述本申请如何实现在进程间进行数据交互访问。图1-图3所描述的对共享内存的高效管理是基于如Linux操作系统平台等在一定程度上采用了 buddy内存分配原则的任何操作系统平台。以Linux操作系统平台上运行的任何一个用户进程为例,图1示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中进程获取共享内存的一个实施例的示意图。首先,对应于所述用户进程的用户类(User Class) 100向一个公用的或为管理内存而编写的内存管理器类(MEM_manager类)110注册所要使用的共享内存的名称与大小。然后,MEMjnanager类110初始化一个内存池类(MEM_pool类)120,由所述MEM_pool类120来初始化一个共享内存块(或称为内存段或内存池(pool))以供所述用户进程使用。图2示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中共享内存初始化后的内存池结构(或pool结构)的一个实施例的示意图。首先,假设所初始化的共享内存段的大小为N (N为正整数)字节,且共享内存段中的每一个页面(page)的大小为M(M为正整数)字节,那么,根据buddy内存分配原则,即仅将内存划分为2的幂次方个内存分区来使用,在本申请中,内存分区为一个页面,用于存储一个最大不超过一个页面大小的数据(可以理解,将1(1为正整数)个内存分区一起使用就可以存储一个最大不超过I个页面大小的数据),因此,假设该块共享内存段的所有N字节全部都用于存储用户进程的数据(这是一种假设的理想情况),那么该块共享内存段(即内存块或内存池)可分配的不同大小类型的数据的种类数L(也即该块共享内存段可分配的不同大小类型的数据的级别数L,为正整数)为:
L = 1g2N-1og2M(I)例如N = M,则L = O,即只有I个级别(O级),表示该块共享内存段只能存储一种大小类型的数据(即最大不超过I个页面大小的数据);N = SM,则L = 3,即有4个级别(O级、I级、2级、3级),表示该块共享内存段能存储4种大小类型的数据,即能够存储最多8个M字节的数据、或最多4个2M字节的数据、或最多2个4M字节的数据、或最多I个8M字节的数据。另外,仿照Linux操作系统中对slab结构的使用,如图2所示,在本申请中,每个slab结构都是共享内存中的一个小内存块,这样的一个slab结构(即一个小内存块)包含一个或多个页面,这样的一个slab结构(即这样的一个小内存块,即这样的一个或多个页面)被划分成多个相同大小类型的对象(用于分别存储相应的相同大小类型的数据,这里相同大小类型的数据的含义是指数据的尺寸大小在一个级别上,而并非指数据的尺寸大小绝对相同),即每个slab结构分配有多个这样的对象,也就是说每个slab结构对应一种大小类型的数据,因此,一个共享内存段可分配的不同大小类型的数据的级别数(种类数)L意味着该块共享内存段可以分配的slab结构的种类数最多为L,为便于描述,这里假设正好有所有L种slab结构(包括暂时为空的slab结构),然而,由于共享内存段在实际中并非是将所有N字节全部都用于存储用户进程的数据,而是需要消耗一部分字节来对该块共享内存段自身的结构及其所包含的slab的结构进行描述,因此,在实际中,一个可分配的slab结构的种类数最多为L的共享内存段(即内存池结构)实际可用于分配所述用户进程数据的页面的个数K(K为正整数)为:K= (N-该内存池结构属性描述字所占用的字节数-L* 一个slab结构属性描述字所占用的字节数)/M(2)如图2所示,在所述共享内存段(内存池)中,该内存池结构属性描述字200占用了一部分字节数,其中内存池结构描述字200描述该共享内存段的属性,主要的属性包括该共享内存段的地址、名 称和大小;L个slab结构的描述字(用于描述slab结构O至slab结构L-1的属性,每个slab结构的描述字的长度相等)占用了一部分字节数,其中每个slab结构根据需要可以指向m(m为正整数)个不同的页面(这m个页面组成一个小内存块,用来存储相同大小类型的数据,并且每个slab结构所对应的m数的确定值可以不同);其余的共享内存段属于多个页面结构,这里的每个页面结构即代表一个页面(页面用于被各slab结构瓜分和使用,即,如前所述,每个slab结构根据需要会指向这些页面中的一个或多个),那么,根据前述的公式(2),该共享内存段实际可用于分配所述用户进程数据的页面结构(即页面)的个数为K个,其中,页面是slab结构所指向的结构,一个页面是对一个数据页面的描述,主要包含该数据页面的起始地址等信息。图2中所示的数据页面结构O至数据页面K-1才是实际用于存储所述用户进程的数据的区域。然后,MEM_manager类110向所述用户类100返回一个对应于所述共享内存段的MEM_pool类120,对于Linux操作系统平台,这个MEM_pool类120中包含一个slab类130,从而实现了所述用户进程对所述共享内存的获取。其中在实现所述用户进程对所述共享内存的获取的过程中,如果用户类100使用的是C语言,则调用slab类130的slab_alloc()函数和slab_free()函数来分配和释放共享内存;若使用C++的STL(标准模板库)的话,则可以通过一个自定义的(即编写的)allocator类140来替换STL容器(一些以模板类的方法提供的数据结构)中的内存分配模型,图1中的粗实线箭头表示当前使用中,而细虚线箭头表示可以作为候选方式。然后,所述用户进程就可以开始使用所获取的共享内存了,图3示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中使用一段时间后的slab结构的一个实施例的示意图。如前所述,每个slab结构可以指向分配有相同大小类型数据的m个页面,应当理解,图3中所示的slab结构的页面分配只是一个逻辑上的示意图,在真正的物理内存分配上,一个slab结构放的是一个页面的地址,这个页面又放有指向下一个页面的地址,如此形成一个分配页面的链表。另外,由于每个slab结构根据所述用户进程当前存在的实际数据的类型和数量的变化也可以产生和消亡,所以,为了容易理解,这里假设一种所述用户进程运行一段时间后的状态,slab结构O包含页面O、页面3、页面11、页面12等4个页面形成的链表;slab结构I已经为空,如果长时间不再有相应大小类型的数据使用slab结构l,slab结构I将会被删除;slab结构2包含页面10、页面31、页面2等3个页面形成的链表;slab结构3包含页面15、页面7、页面9、页面I等4个页面形成的链表;以及slab结构L-1包含页面K-1、页面4等2个页面形成的链表。可以看到,本申请的对共享内存采用基于slab结构的链式管理方式,与传统的没有采用slab结构的简单的链式共享内存管理方式相比,本申请所采用的slab结构通过对相同大小级别的对象(即数据)进行缓存,从而避免了常见的碎片问题,slab结构还支持通用对象的初始化,从而避免了为同一目的而对一个对象重复进行初始化,因此在空间上和时间上都充分提高了共享内存的存取效率。上面以Linux操作系统平台上运行的任何一个用户进程为例讲述了一个用户进程对共享内存的获取和使用。一个进程对共享内存的获取和使用是下面将要讲述的多个进程间进行数据交互访问的前提。需要交互访问的不同进程分别将同一段共享内存连接到各自的用户空间(即各进程私有的虚拟内存地址空间)。其中,如果需要交互访问的各进程中的至少两个进程之间的关系是父子进程关系,就在父进程连接共享内存段并初始化该共享内存段后再创建(fork)子进程,然后父子进程间就可以使用该共享内存段进行数据共享了。如果各进程之间的关系是非父子进程关系,则各进程在连接相同的共享内存段时,需要在各自的进程空间中指定相同的映射地址,以便映射到相同的共享内存段。例如,这种共享内存段的共享方式在Linux系统中一般由void*shmat(int shmid, constvoid*shmaddr, int shmflg)函数调用中的shmaddr参数来指定。到目前为止,讲述了在本申请中各进程如何连接到同一段共享内存段,以及如何对共享内存采用基于slab结构的链式管理以实现共享内存的高效利用。下面将结合图4和图5来讲述本申请如何实现进程间数据的交互式访问,即,具体地,如何实现进程间数据的直接的实时交互访问。图4示例性示出了在现有技术的用于实现进程间相互协作的方法中进程共享数据的示意图。如图4中所述,在现有的标准共享数据模型中,进程所使用的容器、结构、函数、数组、数据链等数据结构320分配在用户进程自身的地址空间(即各进程私有的虚拟内存地址空间)300中。在父用户进程创建(fork)子用户进程后分别连接到共享内存段后,或在各非父子用户进程连接到共享内存段后,各用户进程所使用的容器、结构、函数、数组、数据链等数据结构320所包含的成员变量如start与end等仅仅分配在各个用户进程自身的地址空间300中,而仅仅将所使用的容器、结构、函数、数组、数据链等数据结构320的结果数据存储在位于共享内存段的地址空间310中的结果数据空间(用于存放这些数据结构的结果数据)330中,因此各用户进程所使用的容器、结构、函数、数组、数据链等数据结构320自身不能被共享,各进程能够共享的仅仅是这些容器、结构、函数、数组、数据链等数据结构自身的结果数据空间330,即仅仅能够共享这些容器、结构、函数、数组、数据链等数据结构所产生的结果数据,而不能共享这些容器、结构、函数、数组、数据链等数据结构320所包含的各成员变量的状态关系,也就是说不能共享它们所产生的结果数据之间的关系(即数据的地址映射关系)。例如,如果用户进程A调用insert函数改变了成员变量start与end的状态,由于成员变量start与end不在共享内存空间310中,因此用户进程B不能“看”到用户进程A调用insert函数改变了成员变量start与end的状态的这种变化,因此也就无法实现进程A与进程B间直接的实时交互式访问或协作,或者说进程A与进程B只能在传统技术的水平上进行协作。为了实现进程间直接的实时交互式访问的目的,本申请将各用户进程所使用的容器、结构、函数、数组、数据链等数据结构中的一个或多个直接分配在共享内存段的地址空间中。图5示例性示出了在本申请的用于实现在进程间进行数据交互访问的方法中进程共享数据与数据之间的关系的示意图。如图5中所示,在本申请的用于实现在进程间进行数据交互访问的方法中,各用户进程所使用的容器、结构、函数、数组、数据链等数据结构320中的一个或多个直接分配在共享内存段的地址空间310中,用户进程通过设置在该用户进程的地址空间300中的指针340来操作和使用容器、结构、函数、数组、数据链等数据结构320。这样一来,由于各用户进程所使用的容器、结构、函数、数组、数据链等数据结构320自身直接被共享,因此各进程不但能够直接共享这些容器、结构、函数、数组、数据链等数据结构320自身的结果数据空间(用于存放各进程所使用的容器、结构、函数、数组、数据链等数据结构320的结果数据)330,即能够直接共享这些容器、结构、函数、数组、数据链等数据结构320所产生的结果数据,还能够直接共享这些容器、结构、函数、数组、数据链等数据结构320自身所包含的各成员变量的状态关系,还能够直接共享这些容器、结构、函数、数组、数据链等数据结构320所产生的结果数据之间的关系(即数据的地址映射关系)。例如,如果用户进程A调用insert函数改变了成员变量start与end的状态,用户进程B立刻就能“看”到insert函数改变了成员变量start与end的状态的这种变化,因此就实现了进程A与进程B间直接的实时交互式访问。更广泛地说,对于任意两个进程A和进程B,进程A增加、更改或删除了某个数据的值,进程B立刻就可以“感知”到这种改变,而不必对所有数据进行重新解释,也就是说进程A和进程B这两个进程间不仅共享了数据,而且还共享了这些数据之间的相互关系等一切中间过程。另外,可以理解,本申请的将各用户进程所使用的容器、结构、函数、数组、数据链等数据结构中的一个或多个直接分配在共享内存段的地址空间中以共享数据和数据之间的相互关系并不局限于在Linux操作系统平台上的使用,而是可以推广发挥到Windows等任何能够使用共享内存的操作系统平台上。作为另一个实施例,例如,如果进程使用STL容器,就将STL容器本身也分配到共享内存段中。例如,如果使用STL容器中的map容器,示例性的代码如下:typedef basic_string〈char, char_traits〈char>, memp ο ο I _allocator〈char>>xstring ;typedef map〈xstring, int,less〈xstring>, mempool_allocator〈pair〈xstring,int>>>xmap ;xmap*ptrmap ;ptrmap = new(slab_alloc(sizeof(xmap))xmap ;可以看到,通过将各用户进程所使用的map容器直接分配在共享内存段的地址空间中,能够使得map容器的数据关系(即容器自身的数据结构或容器内各变量之间的关系)也被存储在共享内存段的地址空间中。例如,其中mempool allocator可以是一种自定义的(即编写的)基于共享内存的内存分配模型。图6示例性示出了本申请的用于实现在进程间进行数据交互访问的装置的示意图。如图6所示,本申请的一种用于实现在进程间进行数据交互访问的装置400包括:连接模块410,用于将需要交互访问的多个进程连接到同一个共享内存段310;以及分配模块420,用于将所述多个进程中需要共享的数据结构320分配到所述共享内存段310。图6中仅示出了一个进程的操作,可以理解,参与数据共享的其它进程的操作与此相同。图6中所示的装置用于执行附图5所示的用于实现在进程间进行数据交互访问的方法,参考附图5可以理解,图6中所示的装置用以使所述多个进程之间共享所述数据结构320包含的成员变量的状态关系,和/或所述数据结构320产生的结果数据之间的关系。其中,在所述连接模块中,如果所述需要交互访问的多个进程中的至少两个进程之间的关系是父子进程关系,就在父进程连接所述共享内存段310并初始化所述共享内存段310后再创建子进程。其中,在所述连接模块中,如果所述多个进程之间的关系是非父子进程关系,则所述多个进程在连接所述共享内存段310时,需要在各自的进程空间300中指定相同的映射地址,以便映射到相同的所述共享内存段。其中,在所述连接模块中,如果所述共享内存段310的大小为N,N为正整数,所述共享内存段310中的每一个页面的大小为M,M为正整数,那么仅将所述共享内存段310划分为2的幂次方个内存分区来使用,其中一个所述内存分区为一个所述页面,所述共享内存段310可分配的不同大小类型的数据的种类数L为:L= log2N-log2M,L为正整数,并且通过指向一个或多个所述页面来形成一个slab结构,其中一个所述slab结构被划分成多个相同大小类型的对象,并且所述多个相同大小类型的对象用于分别存储相应的相同大小类型的数据,以及并且所述共享内存段310最多具有L个所述slab结构,每个所述slab结构对应一种大小类型的数据。其中,在所述分配模块中,所述多个进程中需要共享的数据结构320包括容器、结构、函数、数组、数据链中的一个或多个。其中,在所述分配模块中,所述多个进程中需要共享的数据结构320是标准模板
库容器。其中,在所述分配模块中,通过设置在所述多个进程各自的地址空间300中的指针340来使用所述数据结构320。
通过上述结合具体实施例对本申请的详细描述,可以看到,本申请的用于实现在进程间进行数据交互访问的方法及其装置不但为进程间数据的直接的实时交互式访问提供了一种可能的实现,而且,本申请可以是跨操作系统平台的。另外,如前所述,由于本申请所采用的共享内存管理方式类似于Linux内核的slab管理方式,因此,本申请还能够实现对共享内存的高效管理。根据本申请的用于实现在进程间进行数据交互访问的方法可以由具有运算处理能力的单个或多个处理设备,如单个或多个计算机,运行计算机可执行指令来实现。根据本申请的用于实现在进程间进行数据交互访问的装置可以为单个或多个处理设备,如单个或多个计算机,其中的各个模块或单元可以为该处理设备运行计算机可执行指令时具有相应功能的设备组件。根据本申请的一个实施例,可以使用C/C++等语言在Linux、Windows等系统下来实现上述用于实现在进程间进行数据交互访问的方法及其装置。虽然已参照典型实施例描述了本申请,但应当理解,所用的术语是说明和示例性、而非限制性的术语。由于本申请能够以多种形式具体实施而不脱离申请的精神或实质,所以应当理解,上述实施例不限于任何前述的细节,而应在随附权利要求所限定的范围内广泛地解释,因此落入权利要求或其等同范围内的全部变化和改型都应为随附权利要求所涵
至JHL ο
权利要求
1.一种用于实现在进程间进行数据交互访问的方法,所述方法包括: 步骤1,将需要交互访问的多个进程连接到同一个共享内存段;以及 步骤2,将所述多个进程中需要共享的数据结构分配到所述共享内存段,以使所述多个进程之间共享所述数据结构包含的成员变量的状态关系,和/或所述数据结构产生的结果数据之间的关系。
2.根据权利要求1所述的方法,其中, 在所述步骤I中,如果所述多个进程中的至少两个进程之间的关系是父子进程关系,就在父进程连接所述共享内存段并初始化所述共享内存段后再创建子进程。
3.根据权利要求1所述的方法,其中, 在所述步骤I中,如果所述多个进程之间的关系是非父子进程关系,则所述多个进程在连接所述共享内存段时,需要在各自的进程空间中指定相同的映射地址,以便映射到相同的所述共享内存段。
4.根据权利要求1所述的方法,其中, 在所述步骤I中,如果所述共享内存段的大小为N,N为正整数,所述共享内存段中的每一个页面的大小为M,M为正整数,那么仅将所述共享内存段划分为2的幂次方个内存分区来使用,其中一个所述内存分区为一个所述页面, 并且所述共享内存段可分配的不同大小类型的数据的种类数L为:L = 1g2N-1og2M, L为正整数, 并且通过指向一个或多个所述页面来形成一个slab结构,其中一个所述slab结构被划分成多个相同大小类型的对象,并且所述多个相同大小类型的对象用于分别存储相应的相同大小类型的数据,以及 并且所述共享内存段最多具有L个所述slab结构,每个所述slab结构对应一种大小类型的数据。
5.根据权利要求1所述的方法,其中, 在所述步骤2中,所述多个进程中需要共享的数据结构包括容器、结构、函数、数组、数据链中的一个或多个。
6.根据权利要求1所述的方法,其中, 在所述步骤2中,所述多个进程中需要共享的数据结构是标准模板库容器。
7.根据权利要求1所述的方法,其中, 在所述步骤2中,通过设置在所述多个进程的地址空间中的指针来使用所述数据结构。
8.一种用于实现在进程间进行数据交互访问的装置,所述装置包括: 连接模块,用于将需要交互访问的多个进程连接到同一个共享内存段;以及 分配模块,用于将所述多个进程中需要共享的数据结构分配到所述共享内存段,以使所述多个进程之间共享所述数据结构包含的成员变量的状态关系和/或所述数据结构产生的结果数据之间的关系。
9.根据权利要求8所述的装置,其中, 在所述连接模块中,如果所述需要交互访问的多个进程中的至少两个进程之间的关系是父子进程关系,就在父进程连接所述共享内存段并初始化所述共享内存段后再创建子进程。
10.根据权利要求8所述的装置,其中, 在所述连接模块中,如果所述多个进程之间的关系是非父子进程关系,则所述多个进程在连接所述共享内存段时,需要在各自的进程空间中指定相同的映射地址,以便映射到相同的所述共享内存段。
11.根据权利要求8所述的装置,其中, 在所述连接模块中,如果所述共享内存段的大小为N,N为正整数,所述共享内存段中的每一个页面的大小为M,M为正整数,那么仅将所述共享内存段划分为2的幂次方个内存分区来使用,其中一个所述内存分区为一个所述页面, 并且所述共享内存段可分配的不同大小类型的数据的种类数L为:L = log2N-log2M,L为正整数, 并且通过指向一个或多个所述页面来形成一个slab结构,其中一个所述slab结构被划分成多个相同大小类型 的对象,并且所述多个相同大小类型的对象用于分别存储相应的相同大小类型的数据,以及 并且所述共享内存段最多具有L个所述slab结构,每个所述slab结构对应一种大小类型的数据。
12.根据权利要求8所述的装置,其中, 在所述分配模块中,所述多个进程中需要共享的数据结构包括容器、结构、函数、数组、数据链中的一个或多个。
13.根据权利要求8所述的装置,其中, 在所述分配模块中,所述多个进程中需要共享的数据结构是标准模板库容器。
14.根据权利要求8所述的装置,其中, 在所述分配模块中,通过设置在所述多个进程的地址空间中的指针来使用所述数据结构。
全文摘要
一种用于实现在进程间进行数据交互访问的方法包括步骤1,将需要交互访问的多个进程连接到同一个共享内存段;以及步骤2,将所述多个进程中需要共享的数据结构分配到所述共享内存段,以使所述多个进程之间共享所述数据结构包含的成员变量的状态关系,和/或所述数据结构产生的结果数据之间的关系。一种用于实现在进程间进行数据交互访问的装置包括连接模块,用于将需要交互访问的多个进程连接到同一个共享内存段;以及分配模块,用于将所述多个进程中需要共享的数据结构分配到所述共享内存段,以使所述多个进程之间共享所述数据结构包含的成员变量的状态关系,和/或所述数据结构产生的结果数据之间的关系。
文档编号G06F9/54GK103197979SQ20121000125
公开日2013年7月10日 申请日期2012年1月4日 优先权日2012年1月4日
发明者李晓波, 李永亮 申请人:阿里巴巴集团控股有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1