游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

RustforLinux源码导读Ref引用计数容器

  引子
  2022年,我们很可能会看到Linux内核中的实验性Rust编程语言支持成为主流。2021。12。6早上发出了更新的补丁,介绍了在内核中处理Rust的初始支持和基础设施。
  这次更新的内容包括:升级到了最新Stable编译器和Rust2021edition。因此可以摆脱了constfntransmute,constpanic、constunreachableunchecked、corepanic和tryreserve这几个之前未稳定的特性。自定义core和alloc。为alloc添加了更加模块化的选项,以便禁用一些他们不需要的功能:norc和nosync,主要是为上游Rust项目添加。更严格的代码、文档和新的lint。抽象和驱动程序更新。添加了序列锁、电源管理回调的抽象,io内存(readXwriteX)、irq芯片和高级流处理程序,gpio芯片(包括irq芯片)、设备、amba设备和驱动程序以及证书。此外,也改进并简化了Ref(refcountt支持)对象并用它替换了Rust的Arc的所有实例。完全地从alloccrate中删除了Arc和Rc。
  从现在开始,Rustforlinux团队将开始定期提交补丁,每两周左右。
  除了来自Arm、Google和Microsoft的支持外,这次该团队又收到一封来自红帽的信:红帽对Rust用于内核的工作也非常感兴趣(ThereisinterestinusingRustforkernelworkthatRedHatisconsidering)。v2补丁:https:lore。kernel。orglkml20211206140313。56531ojedakernel。orgwww。phoronix。comscan。php?pakernelcrate文档为什么需要引入Ref来代替Arc
  RustforLinux中这个kernelcrate中之前使用的是Arc,但是现在换成了Ref。通过查看相关PRrust:updateReftousethekernelsrefcountt,可以了解其中主要有两点原因:最大化利用现有的C代码和消除恐慌(Panic)。内核中已经有了引用计数的实现refcountt,而且它超过引用计数的阈值时,不是Panic(abort)而是返回最大值(饱和加法)。因为这个原因,也使用RBTree(红黑树)替代了BTreeMap。不需要弱引用。
  Arc有一个MAXREFCOUNT的限制,是isize::MAXasusize大小,引用计数加法超过该大小就会溢出然后发生Panic(abort)。
  所以最终实现的Ref与Arc的区别在于:Ref是基于内核的refcountt来支持的它不支持弱引用,所以大小减少了一半当它超过阈值时,它使得引用计数饱和(saturating)而非中止(abort)它不提供getmut方法,所以引用计数对象是Pin的。Ref源码分析
  接下来分析一下Ref的实现。Ref结构体
  Ref结构体定义如下:AreferencecountedpointertoaninstanceofT。Thereferencecountisincrementedwhennewinstancesof〔Ref〕arecreated,anddecrementedwhentheyaredropped。Whenthecountreacheszero,theunderlyingTisalsodropped。InvariantsThereferencecountonaninstanceof〔Ref〕isalwaysnonzero。Theobjectpointedtoby〔Ref〕isalwayspinned。pubstructRefT:?Sized{ptr:NonNullRefInnerT,p:PhantomDataRefInnerT,}复制代码
  它维护一个不变量(Invariants):引用计数Ref总是一个非零的一个实例,并且被Ref引用的对象总是Pin的(不可移动)。
  该结构体中使用NonNull,而非mutT,这里需要协变(covariant),而非不变(invariant)。可以参考下面示例:usestd::ptr::NonNull;structRefT:?Sized{x:NonNullT,x:mutT,如果换成mutT,编译将不会通过}fntakea(r:Refau32,y:au32){}fngive()Refstaticu32{todo!()}fntest(){lety1;协变,能传入Refau32的函数take,也能接收Refstaticu32类型的参数,因为static:a,能接受子类型,也能接受父类型take(give(),y);}复制代码
  NonNull是mutT的协变版本,并且也代表了非空指针,代表了引用计数对象总是非空的,因为当计数为零就会释放。
  而这里使用PhatomData则是为了Drop检查,此处表示Ref类型拥有RefInner,当Ref被Drop的时候,RefInner也能跟着被Drop。RefInner结构体
  再来看RefInner结构体:〔repr(C)〕structRefInnerT:?Sized{refcount:Opaquebindings::refcountt,data:T,}复制代码
  RefInner内部包含了内核中C语言实现的引用计数结构体refcountt,这里就是为了复用C代码。
  其中Opaque类型是kernelcrate内置的专门为了和C打交道提供的一个包装类型,定义如下:pubstructOpaqueT(MaybeUninitUnsafeCellT);implTOpaqueT{Createsanewopaquevalue。pubfnnew(value:T)Self{Self(MaybeUninit::new(UnsafeCell::new(value)))}Createsanuninitialisedvalue。pubfnuninit()Self{Self(MaybeUninit::uninit())}Returnsarawpointertotheopaquedata。pubfnget(self)mutT{UnsafeCell::rawget(self。0。asptr())}}复制代码
  Opaque类型意味着永远都不需要Rust代码来解释的FFi对象。所以,为了使用内核中已经存在的引用计数结构体,这里用Opaque类型。关于refcountt
  Linux内核中定义的refcountt结构体定义如下:from:https:github。comtorvaldslinuxblobmastertoolsincludelinuxrefcount。htypedefstructrefcountstruct{atomictrefs;}refcountt;复制代码
  refcounttAPI的目标是为实现对象的引用计数器提供一个最小的API。虽然内部使用了原子操作,但一些refcount()和atomic()函数在内存顺序保证方面有很多不同。
  refcountt在2018年曾经发生过引用计数溢出的安全漏洞,即,当引用计数达到最大值时,如果再加一,则引用计数就会归零。所以,此时引用的对象就会被错误释放。这样就变成了一个UAF(useafterfree)漏洞,容易被人利用。
  所以现在refcountt被增加了引用计数检测:from:https:github。comtorvaldslinuxblobmastertoolsincludelinuxrefcount。hL69staticinlinerefcountcheckboolrefcountincnotzero(refcounttr){unsignedintold,new,valatomicread(rrefs);for(;;){newval1;if(!val)returnfalse;if(unlikely(!new))returntrue;oldatomiccmpxchgrelaxed(rrefs,val,new);if(oldval)break;valold;}REFCOUNTWARN(newUINTMAX,refcountt:saturated;leakingmemory。);returntrue;}复制代码
  在达到引用计数最大时采用饱和(saturated)加法,即,返回最大的值,而非零。注意,这里使用了compareandswap原子操作且并未提供内存顺序(使用relaxed)。为Ref实现的一些trait
  为了让Ref拥有一些类似于Arc的行为,所以为其实现一些内置trait。Thisistoallow〔Ref〕(andvariants)tobeusedasthetypeofself。implT:?Sizedcore::ops::ReceiverforRefT{}Thisistoallow〔RefBorrow〕(andvariants)tobeusedasthetypeofself。implT:?Sizedcore::ops::ReceiverforRefBorrow,T{}ThisistoallowcoercionfromRefTtoRefUifTcanbeconvertedtothedynamicallysizedtype(DST)U。implT:?SizedUnsizeU,U:?Sizedcore::ops::CoerceUnsizedRefUforRefT{}ThisistoallowRefUtobedispatchedonwhenRefTcanbecoercedintoRefU。implT:?SizedUnsizeU,U:?Sizedcore::ops::DispatchFromDynRefUforRefT{}SAFETY:ItissafetosendRefTtoanotherthreadwhentheunderlyingTisSyncbecauseiteffectivelymeanssharingT(whichissafebecauseTisSync);additionally,itneedsTtobeSendbecauseanythreadthathasaRefTmayultimatelyaccessTdirectly,forexample,whenthereferencecountreacheszeroandTisdropped。unsafeimplT:?SizedSyncSendSendforRefT{}SAFETY:ItissafetosendRefTtoanotherthreadwhentheunderlyingTisSyncforthesamereasonasabove。TneedstobeSendaswellbecauseathreadcancloneaRefTintoaRefT,whichmayleadtoTbeingaccessedbythesamereasoningasabove。unsafeimplT:?SizedSyncSendSyncforRefT{}复制代码
  从上面代码里看得出来,用到的trait有:core::ops::Receiver:是一个未稳定特性(receivertraitfeatures),它表示一个结构体可以作为方法接收者,不需要arbitraryselftypes特性。标准库中一些智能指针实现了该trait,比如BoxArcRcTPin
  等。core::ops::CoerceUnsized:也是一个未稳定特性(coerceunsizedfeatures),它表示将Size类型转换为DST类型。core::ops::DispatchFromDyn:同样是一个未稳定的特性(dispatchfromdynfeatures),它用于对象安全(动态安全dynsafe)的检查。实现DispatchFromDyn的类型可以安全地用作对象安全方法中的self类型。SendSync,是Rust中稳定的特性,用于标记线程间可安全传递和共享的类型。
  现在为Ref实现了这些trait,那么Ref也就拥有了相应的行为。基本上Ref的行为和Arc类似了,除了上面所说的那些区别。引用计数管理
  因为Ref是复用内核C代码,所以对于引用计数的管理,只需要实现相应的trait即可。
  比如,Clone时应该自增引用计数,而Drop时应该自减引用计数。所以,分别来看一下这两个实现。实现ClonetraitimplT:?SizedCloneforRefT{fnclone(self)Self{INVARIANT:Crefcountincsaturatestherefcount,soitcannotoverflowtozero。SAFETY:Bythetypeinvariant,thereisnecessarilyareferencetotheobject,soitissafetoincrementtherefcount。unsafe{bindings::refcountinc(self。ptr。asref()。refcount。get())};SAFETY:Wejustincrementedtherefcount。ThisincrementisnowownedbythenewRef。unsafe{Self::frominner(self。ptr)}}}复制代码
  实现Clonetrait很简单,直接通过bindings::refcountinc来调用内核中refcountt的自增方法refcountinc即可。
  因为refcountinc已经是有了引用计数溢出检测,使用饱和加法,所以不用担心归零。实现DroptraitimplT:?SizedDropforRefT{fndrop(mutself){SAFETY:Bythetypeinvariant,thereisnecessarilyareferencetotheobject。WecannottouchrefcountafteritsdecrementedtoanonzerovaluebecauseanotherthreadCPUmayconcurrentlydecrementittozeroandfreeit。Itisoktohavearawpointertofreedinvalidmemoryaslongasitisneverdereferenced。letrefcountunsafe{self。ptr。asref()}。refcount。get();INVARIANT:Iftherefcountreacheszero,therearenootherinstancesofRef,andthisinstanceisbeingdropped,sothebrokeninvariantisnotobservable。SAFETY:Alsobythetypeinvariant,weareallowedtodecrementtherefcount。letiszerounsafe{bindings::refcountdecandtest(refcount)};ifiszero{Thecountreachedzero,wemustfreethememory。SAFETY:Thisthreadholdstheonlyremainingreferencetoself,soitissafetogetamutablereferencetoit。letinnerunsafe{self。ptr。asmut()};letlayoutLayout::forvalue(inner);SAFETY:Thevaluestoredininnerisvalid。unsafe{core::ptr::dropinplace(inner)};SAFETY:Thepointerwasinitialisedfromtheresultofacalltoalloc。unsafe{dealloc(self。ptr。cast()。asptr(),layout)};}}}复制代码
  实现Droptrait,同样直接通过bindings::refcountdecandtest调用内核refcountdecandtest函数即可,该函数也包含了引用计数溢出检查。但是在引用计数归零的时候,需要释放内存。
  注意上面Clone和Drop这两个trait的实现,是UnsafeRust抽象为SafeRust的一个经典范例,主要是其中的Safety注释,考虑了安全边界,并且加以说明。创建新的引用计数对象
  接下来需要关注Ref如何创建新的引用计数对象。implTRefT{ConstructsanewreferencecountedinstanceofT。pubfntrynew(contents:T)ResultSelf{letlayoutLayout::new::RefInnerT();SAFETY:ThelayoutsizeisguaranteedtobenonzerobecauseRefInnercontainsthereferencecount。letinnerNonNull::new(unsafe{alloc(layout)})。okor(Error::ENOMEM)?。cast::RefInnerT();INVARIANT:Therefcountisinitialisedtoanonzerovalue。letvalueRefInner{SAFETY:JustanFFIcallthatreturnsarefcounttinitialisedto1。refcount:Opaque::new(unsafe{bindings::REFCOUNTINIT(1)}),data:contents,};SAFETY:inneriswritableandproperlyaligned。unsafe{inner。asptr()。write(value)};SAFETY:Wejustcreatedinnerwithareferencecountof1,whichisownedbythenewRefobject。Ok(unsafe{Self::frominner(inner)})}}复制代码
  该trynew方法中使用core::alloc::Layout结构体来定义内存布局。
  通过NonNull::new和自定义的core::alloc::alloc函数来分配新的内存,并转换为RefInner类型,并通过bindings::REFCOUNTINIT调用内核C函数对其初始化为1。其中自定义的core::alloc模块将来都会同步到rustcore中。
  其中Error::ENOMEM代表OOM错误。在kernelerror。rs中定义了很多内核错误码对应的错误。
  Linux内核中使用整数定义了很多错误码,在kernelcrate中,使用了NewType模式对其进行封装,而非直接使用整数错误码:macrorules!declareerr{(err:tt){pubconsterr:SelfError((bindings::errasi32));};(err:tt,(doc:expr),){(〔docdoc〕)pubconsterr:SelfError((bindings::errasi32));};}〔derive(Clone,Copy,PartialEq,Eq)〕pubstructError(ctypes::cint);implError{declareerr!(EPERM,Operationnotpermitted。);declareerr!(ENOENT,Nosuchfileordirectory。);declareerr!(ESRCH,Nosuchprocess。);declareerr!(ENOMEM,Outofmemory。);。。。}复制代码从已经存在的RefInner构造Refh1
  在上面的trynew方法中看到,最后一步使用frominner方法将一个裸指针构造为最终的Ref。并且它是一个内部方法,不是公开的API。
  注意,它是一个unsafe的方法,因为需要调用者来确保inner的指针是有效且非空的,对于这一点其文档注释也写的比较清楚。implT:?SizedRefT{Constructsanew〔Ref〕fromanexisting〔RefInner〕。SafetyThecallermustensurethatinnerpointstoavalidlocationandhasanonzeroreferencecount,oneofwhichwillbeownedbythenew〔Ref〕instance。unsafefnfrominner(inner:NonNullRefInnerT)Self{INVARIANT:Bythesafetyrequirements,theinvariantshold。Ref{ptr:inner,p:PhantomData,}}}复制代码RefBorrow
  不存在对底层引用计数结构体的可变借用,但是存在一个不可变的借用,并且需要手动维护生命周期。Aborrowed〔Ref〕withmanuallymanagedlifetime。InvariantsTherearenomutablereferencestotheunderlying〔Ref〕,anditremainsvalidforthelifetimeofthe〔RefBorrow〕instance。pubstructRefBorrowa,T:?Sizeda{inner:NonNullRefInnerT,p:PhantomDataa(),}implT:?SizedCloneforRefBorrow,T{fnclone(self)Self{self}}implT:?SizedCopyforRefBorrow,T{}复制代码
  RefBorrow结构体使用PhantomDataa()来持有生命周期参数,并为其实现Copytrait,其行为和普通的不可变引用类似。
  然后为Ref实现一个asrefborrow方法即可从Ref得到RefBorrow。implTRefT{Returnsa〔RefBorrow〕fromthegiven〔Ref〕。Thisisusefulwhentheargumentofafunctioncallisa〔RefBorrow〕(e。g。,inamethodreceiver),butwehavea〔Ref〕instead。Gettinga〔RefBorrow〕isfreewhenoptimised。〔inline〕pubfnasrefborrow(self)RefBorrow,T{SAFETY:TheconstraintthatlifetimeofthesharedreferencemustoutlivethatofthereturnedRefBorrowensuresthattheobjectremainsalive。unsafe{RefBorrow::new(self。ptr)}}}复制代码
  其实按Rust命名规范,此处asrefborrow改为asref更好。但是这里其实asref另有用处:implT:?SizedAsRefTforRefT{fnasref(self)T{SAFETY:Bythetypeinvariant,thereisnecessarilyareferencetotheobject,soitissafetodereferenceit。unsafe{self。ptr。asref()。data}}}复制代码
  要通过asref方法从Ref得到T。
  然后为RefBorrow实现Dereftrait,也可以从RefBorrow拿到T。implT:?SizedDerefforRefBorrow,T{typeTargetT;fnderef(self)Self::Target{SAFETY:Bythetypeinvariant,theunderlyingobjectisstillalivewithnomutablereferencestoit,soitissafetocreateasharedreference。unsafe{self。inner。asref()。data}}}复制代码唯一引用类型UniqueRef
  除了Ref之外,还实现了一个UniqueRef类型。顾名思义,该类型表示只有唯一一个引用计数的情况。pubstructUniqueRefT:?Sized{inner:RefT,}implTUniqueRefT{Triestoallocateanew〔UniqueRef〕instance。pubfntrynew(value:T)ResultSelf{Ok(Self{INVARIANT:Thenewlycreatedobjecthasarefcountof1。inner:Ref::trynew(value)?,})}Triestoallocateanew〔UniqueRef〕instancewhosecontentsarenotinitialisedyet。pubfntrynewuninit()ResultUniqueRefMaybeUninitT{Ok(UniqueRef::MaybeUninitT{INVARIANT:Thenewlycreatedobjecthasarefcountof1。inner:Ref::trynew(MaybeUninit::uninit())?,})}}复制代码
  没有为其实现Clone和Drop这两个trait,所以它只能持有唯一一个引用。引入该类型也许可以为内核开发提供更多便利。其他
  Ref还实现了其他trait,比如FromTryFrom,可以从裸指针和Ref之间相互转换。
  一个值得注意的地方是:implTRefT{Deconstructsa〔Ref〕objectintoarawpointer。Itcanbereconstructedoncevia〔Ref::fromraw〕。pubfnintoraw(obj:Self)constT{letretobjasconstT;core::mem::forget(obj);ret}}复制代码
  将Ref转换为裸指针时,注意使用core::mem::forget(obj)避免调用obj的Drop,否则会让引用计数减少而引起问题。小结
  从RustforLinux源码中可以学习很多UnsafeRust的相关技巧,尤其是和C语言打交道的一些比较好的实践。如果你感兴趣,还能学习Linux内核相关的一些内容,为将来是要Rust编写Linux内核驱动做一些准备。最后
  如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑
  如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http:github。crmeb。netudefu不胜感激!
  PHP学习手册:https:doc。crmeb。com
  技术交流论坛:https:q。crmeb。com

鸿蒙首批华为荣耀机型升级,其中有你吗?鸿蒙系统2。0已确认有42款机型可以首批升级,比安卓快60,那么鸿蒙系统2。0已确认的升级机型都有哪些?不少小伙伴们还不是很了解,小编为大家分享一下关于可首批升级的42款机型推……听不清别人小声说话,配助听器有帮助吗?你好,建议先到专业的助听器验配中心检测一下听力,看是否是听力损失引起的听不清说话,因为如果声音足够小正常人也是听不清的。根据检查结果看助听器是否会有帮助,希望我的回答对您有所帮……野牛王浅谈互联网营销获客的新模式导读:互联网营销获客,是通过应用科技信息和互联网技术进行营销获客的方法。与传统营销获客方式相比,互联网营销获客具有效率高、传播快、覆盖广的特点。近几年,随着传统业务数字化转型,……不愧是大国,台积电联合美国麻省理工在1NM制程工艺上有重大突IT之家5月17日消息台积电今日联合台大、麻省理工宣布,在1nm以下芯片方面取得重大进展,研究成果已发表于Nature。该研究发现,利用半金属铋Bi作为二维材料的接触电极,可以……什么样的单反拍风景最好?感谢邀请!拍风景什么样的单反拍出的效果最好,无论是资深大V、摄影达人、老驴友都怕难也给出肯定的答案,因为每个人的要求、切入点、视觉审美标准等不样!我想我从下面几个方面来分析一下……iPhone12更新iOS14。5。1后感受分享,性能真的下这几天浩南的iPhone12ProMax收到了苹果官方iOS14。5。1系统的更新,怀着好奇的心情就更新了。从更新日志上看主要是修复一些BUG,还有提供重要的安全性更新,……微软发布十月份大补丸,赛博昆仑再获感谢北京时间10月13日凌晨,微软发布了10月份系统和产品补丁。微软发布包含修复多达71个漏洞的补丁集合大礼包,被修补漏洞的软件,包括了对微软Windows操作系统、微软Ed……抖音新私域是个什么平台?谢邀,我们是字节跳动旗下的巨量算数官方账号,我们在今年7月发布了《抖音私域经营白皮书》详细的内容可以关注一下我们的头条号,定期会有各行业的免费干货内容推送说到私域,不免会……三星市场跌落,为什么不着急三星在国内的市场份额已经完全跌到了其他项。国内被国产打的跌到谷底,国外印度市场,越南市场,欧洲市场,也被小米和ov打的似乎毫无招架之力。网友欢欣鼓舞,认为三星不过如……2457亿参数的巨量模型,意味着什么?源1。0模型参数规模为2457亿,训练采用的中文数据集达5000GB,相比GPT3模型1750亿参数量和570GB训练数据集,源1。0参数规模领先40,训练数据集规模领先近10……异地怎么办理新社保卡更方便一些?网上办理社保卡方法步骤申报条件及资料:第一,办理社保卡的人员必须已购买社保,首次办理,即新办理用户;第二,需提供申办人身份证复印件,没有身份证的,可提供户口簿……LED灯真省电吗?对于我这个干了4O多年电工来说,还是讲一讲吧,本人早在10前刚刚有LED产生时已开始留意它的耗电量和购买价格,就拿1支1。2米的LED管和1支40w的镇流器比一比,LED管全套……
更高性价比的新一代投影泰捷WeboxT1体验在新时代的家庭里,看电视这个词的词义早已发生了变化,它从单纯的被动接收电视节目,发展到可以由用户自主点播任何自己想看的内容,而电视这个载体,也从传统的平板电视,发展到激光电视,……在一夜之间,智能眼镜进入交互2。0时代,智能手机过时了?颠覆性产品的每次到来都是猝不及防的,想当年乔布斯发布第一代iPhone时,谁也没想到诺基亚手机业务会在短短的几年内凋落。昨天小米仅仅是简单预告了下,今天会有新品发布,然而万万没……人才最重要!华为给鸿蒙OS开发者定了职称无论是学习政治或者历史,里面都会提到有关人才的概念。无论古今,无论国家,无论行业,人才始终都是进步浪潮中最关键的角色,只有人才储备到位,一个领域、一个行业、一个国家才能发展壮大……好吃的冷饭炒炒也依然美味,经典的鼠标复刻也仍然典范记得刚接触外设的时候,玩过一只不知道几手,并且换过微动和脚贴的掉漆大师Sensei。后来鼠标坏掉了朋友都跟我要,说等他那只外壳坏了来换壳续命。正是因为有太多用户都钟爱着这样的外……伊顿用HoloLens2Dynamics365助力节省运营成查看引用信息源请点击:映维网不仅实现了最初的目标,同时看到了影响其经营方式的意外结果(映维网2021年10月21日)多元化动力管理公司伊顿(Eaton)致力于通过高……OriginOS首批招募体验开启!快来看看有没有你的机型OriginOS系统的发布,让vivo在安卓定制系统领域风光无限,大刀阔斧的UI设计也让OriginOS系统在vivo开发者大会上大放异彩,可以说,对于OriginOS的诞生,……这是一台28年前的古董掌机GameBoy在1989年4月6日问世,距今已经有28年历史了。S君有幸收藏了一部1989年生产的初版GameBoy,来看看来自1989年的古董是怎么样的。正面是经典的十……好看的数据可视化的图片是怎么样做的?可视化死磕excel是不行的,作为数据分析行业的偷懒大户,分享一些我在可视化工具上的使用心得,总结了三大类:快速出图类、专业图表类、高端大屏类。个人经验,大家按需采纳:一、快速……三星GalaxyZFlip3入手测评折痕比较明显,重量很轻,这次听说支持5g就直接下单买了。屏幕质量不错,折痕存在但是不影响使用,出厂时候自带了一个保护膜,所以屏幕就不用贴膜了,下一代要是支持双卡就更好了。第一次使用filp,不得……你手机上有什么很良心的APP?微信走路。好用的APP,浏览器就是UC和QQ浏览器。新闻资讯就是今日头条。购物就是淘宝京东唯品会。支付类就是支付宝,当然了还有微信。微信的功能强大到手机……你每天都会打开的手机软件有哪些?为什么?早上醒来,首先打开微信,QQ看看有没有留言!然后打开火山小视频,看看昨晚睡着后赚了多少钱!接着打开同花顺,看看资讯,挂好股票委托买卖单!然后打开QQ音乐,听听……想买无线吸尘器,却在纠结牌子?多维度选购指南对于当代的年轻人和时尚宝妈们来说,无线吸尘器作为一种非常优秀的小家电,能够极大程度地提升自己在家中清洁卫生的效率。相比较传统的扫把来说,无线吸尘器能够以任意的角度和姿势去清洁平……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网