context_switch( )上下文切换.docx_第1页
context_switch( )上下文切换.docx_第2页
context_switch( )上下文切换.docx_第3页
context_switch( )上下文切换.docx_第4页
context_switch( )上下文切换.docx_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

context_switch( )上下文切换 分类: linux_system 2010-12-14 22:09 2091人阅读 评论(0) 收藏 举报 structthreadloadrunnerdescriptoriolinux(一)/kernel_details/archive/2007/01/11/1479940.aspxcontext_switch( )上下文切换:调用switch_mm(),把虚拟内存从一个进程映射切换到新进程中 调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息 The context_switch( ) function sets up the address space of next. the active_mm field of the process descriptor points to the memory descriptor that is used by the process, while the mm field points to the memory descriptor owned by the process. For normal processes, the two fields hold the same address; however, a kernel thread does not have its own address space and its mm field is always set to NULL. The context_switch( ) function ensures that if next is a kernel thread, it uses the address space used by prev. context_switch( )函数建立next的地址空间。进程描述符的active_mm字段指向进程所使用的内存描述符,而mm字段指向进程所拥有的内存描述符。对于一般的进程,这两个字段有相同的地址,但是,内核线程没有它自己的地址空间而且它的 mm字段总是被设置为 NULL。context_switch( )函数保证:如果next是一个内核线程,它使用prev所使用的地址空间:static inlinetask_t * context_switch(runqueue_t *rq, task_t *prev, task_t *next) struct mm_struct *mm = next-mm; struct mm_struct *oldmm = prev-active_mm;如果next是内核线程,则线程使用prev所使用的地址空;schedule( )函数把该线程设置为懒惰TLB模式事实上,每个内核线程并不拥有自己的页表集(task_struct-mm = NULL);更确切地说,它使用一个普通进程的页表集。不过,没有必要使一个用户态线性地址对应的TLB表项无效,因为内核线程不访问用户态地址空间。|-| if (unlikely(!mm) | next-active_mm = oldmm; | atomic_inc(&oldmm-mm_count); | enter_lazy_tlb(oldmm, next); |-|如果next是一个普通进程,schedule( )函数用next的地址空间替换prev的地址空间|-| else | switch_mm(oldmm, mm, next); |-|如果prev是内核线程或正在退出的进程,context_switch()函数就把指向prev内存描述符的指针保存到运行队列的prev_mm字段中,然后重新设置prev-active_mm|-| if (unlikely(!prev-mm) | prev-active_mm = NULL; | WARN_ON(rq-prev_mm); | | rq-prev_mm = oldmm; |-| context_switch()终于可以调用switch_to()执行prev和next之间的进程切换了|-| switch_to(prev, next, prev); |-| return prev;/* context_switch - switch to the new MM and the new* threads register state.*/static inline voidcontext_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)struct mm_struct *mm, *oldmm;prepare_task_switch(rq, prev, next);trace_sched_switch(rq, prev, next);mm = next-mm;oldmm = prev-active_mm;/* * For paravirt, this is coupled with an exit in switch_to to * combine the page table reload and the switch backend into * one hypercall. */arch_enter_lazy_cpu_mode();if (unlikely(!mm) next-active_mm = oldmm;atomic_inc(&oldmm-mm_count);enter_lazy_tlb(oldmm, next); elseswitch_mm(oldmm, mm, next);if (unlikely(!prev-mm) prev-active_mm = NULL;rq-prev_mm = oldmm;/* * Since the runqueue lock will be released by the next * task (which is an invalid locking op but in the case * of the scheduler its an obvious special-case), so we * do an early lockdep release here: */#ifndef _ARCH_WANT_UNLOCKED_CTXSWspin_release(&rq-lock.dep_map, 1, _THIS_IP_);#endif/* Here we just switch the register state and the stack. */switch_to(prev, next, prev);barrier();/* * this_rq must be evaluated again because prev may have moved * CPUs since it called schedule(), thus the rq on its stack * frame will be invalid. */finish_task_switch(this_rq(), prev);(二):工作效率与Context Switch最近读蔡学镛先生的博文無所不在的Context Switch,很有启发。以前没有好好考虑这个问题:究竟是什么让你工作效率低下?现在才清楚,Context Switch才是元凶。电脑应该是有史以来效率最高的工具了,而其效率的提高,最重要的技术就是多道程序并行处理,也就是同一时间电脑可以做很多事情。它是怎样做到的?Context Switch!每到一定时间片,操作系统会自动切换任务,而先前一个任务的Context都被保持下来,新任务的Context会被装载进来,并马上进入工作状态。关键在于电脑可以极快地切换Context并迅速进入工作流程。但人脑却很难做到这一点,这也就是人脑在工作效率上比不上电脑的主要原因之一。想想你曾经工作效率极高的时刻,那是怎样一种状态?是不是连续很长时间所有的思想都集中在一件事情上,忘记了外物的存在,甚至忘记了饥饿与劳累。心理学家把这种状态称之为Flow,中文翻译为福乐状态。在从事自己喜欢、并有一定挑战性的事情的时候,Flow最容易出现。提高工作效率,其根本在于增加Flow出现的频率和周期。这里有两个条件导致高效能:1. 所从事的事情可以诱发Flow,也就是难度不能太高也不太低,压力不太大也不太小,以刚刚超出你的能力范围一点点为宜;2. 减少Context Switch,因为作为正常人来说,一件事情一旦被中断(或者被迫更改),需要很长时间才能重新回到之前的状态,这个恢复时间跟被打断的时间和影响有关。电脑之所有效率高是因为它没有这个问题,因为它只要正常工作,任何时候都是Flow状态,不管事情难度和压力如何,并能以极快的速度Switch Context而无任何不良反应。人脑不是电脑,但可以借鉴其优点,以避免工作效能低下。我们能学习电脑的是,如何尽可能快的Switch Context。电脑和人脑有许多类比处,比如Cache好比当前正在正在考虑的事情,内存好比记忆,硬盘好比备忘录、文档资料等。造成Context Switch慢的关键在于,Context一旦丢失就需要很长时间才能恢复。而电脑把Context很好的组织在存储体系的某一处,以便可以迅速的Load进来,最频繁使用的就放在Cache,其次是内存,最后是硬盘。人脑的记忆容量有限,所以我们更需要外界媒体来记录Context,虽然比不上从记忆调出快,但可靠并且完备。现在有一些工具可以帮助这点,如Mylyn。其实GTD也包含了这个思想。另一个需要解决的问题就是,减少导致Context Switch的Interrupt。这里包括两个层面,一是减少外界的干扰,二是减少内在的干扰。减少外界干扰,需要你为自己创建一个干净的环境,比如隐藏各种可能分散你注意力的东西,关掉电脑里各种可能弹出提示窗口的软件,带上入耳式耳塞听轻音乐(但不能过量以损伤听力)。减少内在的干扰,也分为几个层面。首先你要有计划,知道有哪些事情要做,当前要做什么,需要做多长时间。其次你需要能尽可能地Focus,在当前这个时间段,在完成一个Task or subtask之前,除非不得已,不要做其他无关事情,更不要分心。最后,你需要释放或者忘记你的压力,以便为当前的任务创造一个轻松的环境。这个压力可能来自外部,也可能来自内部。你需要非常清楚你当前工作的意义和目标,清楚其他的事情该什么时候做或该不该做。只有这样才能分清主次,集中精力。(三):CPU Utilization 好理解,就是CPU的利用率,75%以上就比较高了(也有说法是80%或者更高)。除了这个指标外,还要结合Load Average和Context Switch Rate来看,有可能CPU高是因为后两个指标高导致的。 Load Average ,这个很难衡量。网上搜了一圈,还没见到几个合理的解释。我100个并发用户测试数来这两个值是:77.534%,6.108,CPU利用率比较高,Load Average也好像有点高。后来发现了如下两片博文: 理解Load Average做好压力测试 ,“Load Average是 CPU的 Load,它所包含的信息不是 CPU的使用率状况,而是在一段时间内 CPU正在处理以及等待 CPU处理的进程数之和的统计信息,也就是 CPU使用队列的长度的统计信息。 ”,基本解释了multi-process,multi-thread程序的原理。理解Linux处理器的负载均值(翻译) ,简单说起来就一句话: Load Average CPU个数 * 核数 *0.7 比如1个1核CPU,Load Average 1 * 1 * 0.7;1个4核的CPU,Load Average必须 = 4,则要好好检查检查。另外网上说的CSR5000,我认为标准不该如此单一。其他信息: 这三个指标在LoadRunner中可以监控到;另外,在linux中,也可以用vmstat查看r(Load Arerage),in(Interrupt)和cs(Context Switch)#vmstat 1 5procs -memory- -swap- -io- -system-cpu-r b swpd free buff cache si so bi bo in cs us sy id wa0 0 244644 29156 415720 2336484 0 0 1 49 2 1 1 0 98 00 0 244644 29140 415720 2336484 0 0 0 28 9 115 0 0 99 10 0 244644 29140 415720 2336484 0 0 0 24 62 256 0 0 100 00 0 244644 29140 415720 2336484 0 0 0 0 5 93 0 0 100 00 0 244644 29140 415720 2336484 0 0 0 0 58 255 0 0 100 0Interrupt Rate包括内核由于进程的时间片中断。(在 Linux 2.6 中,系统时钟每 1 毫秒中断一次时钟频率,用 HZ 宏表示,定义为 1000,即每秒中断 1000 次。系统不一样,内核不一样配置100、250的都有。)内核的时钟频率可以通过如下命令知道cat /boot/config-uname -r | grep CONFIG_HZ=CONFIG_HZ=100每秒总的时钟中断数就是 = cpu个数 * 核数 * CONFIG_HZcat /proc/interrupts CPU0 CPU1 CPU2 CPU3 LOC: 97574747 52361843 105207680 69447653 Local timer interruptsRES: 107368 257510 98635 186294 Rescheduling interruptsCAL: 14174 14206 14164 194 function call interruptsTLB: 1007949 853117 992546 591410 TLB shootdowns可以查看中断的类型以及次数 =性能相关的系列文章:LoadRunner利用ODBC编写MySql脚本 LoadRunner压力测试时监控服务器Linux的资源情况 压力测试衡量CPU的三个指标:CPU Utilization、Load Average和Context Switch Rate 高性能服务器架构(High-Performance Server Architecture) 网站性能测试PV到TPS的转换以及TPS的波动 用GTmetrix来优化你的网页(集成了YSlow、FireBug的功能) 本文来自CSDN博客,转载请标明出处:/marising/archive/2010/01/12/5182771.aspx(四):在进行负载测试(Load Test)是要监控服务器的CPU、内存、磁盘、网络的情况。如何监控Ubuntu的情况呢。 1、安装rstatd,sudo apt-get install rstatd,如果无法apt安装,可以下载安装。 2、启动rpc.rstatd 查看是否正常启动,用如下命令 rpcinfo -p rootlocalhost # rpcinfo -p program vers proto port 100000 2 tcp 111 portmapper 100000 2 udp 111 portmapper 100024 1 udp 676 status 100024 1 tcp 679 status 100001 3 udp 691 rstatd 100001 2 udp 691 rstatd 100001 1 udp 691 rstatd 3、在LoadRunner Controller的run界面中,添加System Resource Graphs下的Unix Resource,在Unix Resource图上右键Add Measurements,然后点击Add,填写ip如9,默认只有三个指标,在下面的Add中可以添加其他指标。 4、下面说一下各种指标的情况CPU指标Average load上一分钟同时处于“就绪”状态的平均进程数, perform.log这个命令3秒采样一次,共采样120次 360秒6分钟,可以根据自己的需要调整 3 和 120 这两个值。perform.log是保存的文件名磁盘Collision rate每秒钟在以太网上检测到的冲突数 Disk rate 磁盘传输速率 网络Incoming packets error rate 接收以太网数据包时每秒钟接收到的错误数 Incoming packets rate 每秒钟传入的以太网数据包数 Outgoing packets errors rate 发送以太网数据包时每秒钟发送的错误数 Outgoing packets rate 每秒钟传出的以太网数据包数 pps是以太网传输最小包长是64字节。包转发线速的衡量标准是以单位时间内发送64byte的数据包(最小包)的个数作为计算基准的。对于千兆以太网来说,计算方法如下:1000Mbps/(64B+8B+12B)8bit)=1.488095pps说明:当以太网帧为64Byte时,需考虑8Byte的前导符和12Byte的帧间隙的固定开销。在以太网中,每个帧头都要加上了8个字节的前导符,前导符的作用在于告诉监听设备数据将要到来。然后,以太网中的每个帧之间都要有帧间隙,即每发完一个帧之后要等待一段时间再发另外一个帧,在以太网标准中规定最小是12个字节,然而帧间隙在实际应用中有可能会比12个字节要大,在这里我用了最小值。每个帧都要有20个字节的固定开销。(另外这20字节的信息是不能通过抓包软件抓下来的)因此一个全双工线速的千兆以太网端口在转发64Byte包时的包转发率为1.488Mpps。以下是常用以太网端口的包转发率:1、万兆以太网:14.88Mpps2、千兆以太网:1.488Mpps3、百兆以太网:0.1488Mpps4、十兆以太网:0.01488Mpps(14.88Kpps)=本文来自CSDN博客,转载请标明出处:/marising/archive/2010/01/08/5160210.aspx(五):Linux学习总结进程切换和调度算法深入分析一、Linux进程切换深入分析#define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)创建内核线程时使用的CLONE标志。1#define unlikely(x) _builtin_expect(!(x), 0)编译器优化,实际返回值x是整型表达式,0表示并不预期该事件发生,也就是说x为0的可能性很小,这是为了让编译器对下面得语句进行优化。2进程内核态堆栈结构:进程是动态实体,进程描述符是存放在动态内存中的。在一块进程内存区上,Linux存放了两个数据结构:指向task_struct得thread_info和内核态的进程栈。大小一般2页8K,这要求页面帧对齐2的13次幂,在X86上编译时可以配置大小为4K。thread_info在内存区开始处,内核栈从内存尾向下增长。在C语言中可以用union结构表示:图1. 8K内核栈和进程描述符task_struct及thread_info的相互关系union thread_union struct thread_info thread_info; unsigned long stack2048; /* 1024 for 4KB stacks */ ;CPU的esp寄存器用于执行堆栈的顶部指针,当从用户态转向内核态时,进程内核栈总是空的,所以esp就会执行堆栈底部。使用alloc_thread_info 和free_thread_info用于分配和释放一个存放thread_info结构和内核堆栈的内存区。内核通过当前esp指针可以很方便的得到thread_info结构的地址。current_thread_info(void)的原理即如下:movl $0xffff2000,%ecx /* or 0xfffff000 for 4KB stacks */ andl %esp,%ecxmovl %ecx,pthread_info中task指针是第一个,所以current宏相当于current_thread_info( )-task,从而也就得到task指针。每个进程有自己独立得进程空间,所有进程共享CPU寄存器。进程继续执行时必须装入寄存器恢复得数据集称为硬件上下文环境。在Linux中部分硬件上下文存放在进程描述符中,部分存放到内核态堆栈里。3. 进程切换堆栈原理:每个进程有自己独立得进程空间,所有进程共享CPU寄存器。进程继续执行时必须装入寄存器恢复得数据集称为硬件上下文环境。在Linux中部分硬件上下文存放在进程描述符中,部分存放到内核态堆栈里。80x86体系支持在进程TSS段跳转时自动执行进程硬件上下文切换。Linux使用软件方法实现。软件方式效率差不多,当更灵活,可以控制流程,留下优化空间。80x86用TSS段保存硬件上下文内容,每个CPU有一个TSS段。从用户态到内核态切换时,从TSS中取出内核栈地址。用户态进程访问I/O端口时,TSS中的I/O访问位图可以验证权限。tss_struct描述了TSS格式,init_tss存放初始TSS内容,每次进程切换,内核更新TSS中的某些字段,以反映当前运行进程的权限等级。每个进程有个反映任务CPU状态的thread_struct结构变量thread,除eax、ecx等通用寄存器内容保存在内核态堆栈中,其他大部分寄存器都保存在次结构中。该结构一部分对应于tss_struct中的内容,进程切换时把thread中某些内容更新到tss_struct中就可以反映当前任务的运行CPU环境。struct tss_struct unsigned short back_link,_blh; unsigned long esp0; unsigned short ss0,_ss0h; unsigned long esp1; unsigned short ss1,_ss1h; /* ss1 is used to cache MSR_IA32_SYSENTER_CS */ unsigned long esp2; unsigned short ss2,_ss2h; unsigned long _cr3; unsigned long eip; unsigned long eflags; unsigned long eax,ecx,edx,ebx; unsigned long esp; unsigned long ebp; unsigned long esi; unsigned long edi; unsigned short es, _esh; unsigned short cs, _csh; unsigned short ss, _ssh; unsigned short ds, _dsh; unsigned short fs, _fsh; unsigned short gs, _gsh; unsigned short ldt, _ldth; unsigned short trace, io_bitmap_base; /* * The extra 1 is there because the CPU will access an * additional byte beyond the end of the IO permission * bitmap. The extra byte must be all 1 bits, and must * be within the limit. */ unsigned long io_bitmapIO_BITMAP_LONGS + 1; /* * Cache the current maximum and the last task that used the bitmap: */ unsigned long io_bitmap_max; struct thread_struct *io_bitmap_owner; /* * pads the TSS to be cacheline-aligned (size is 0x100) */ unsigned long _cacheline_filler35; /* * . and then another 0x100 bytes for emergency kernel stack */ unsigned long stack64; _attribute_(packed);struct thread_struct /* cached TLS descriptors. */struct desc_struct tls_arrayGDT_ENTRY_TLS_ENTRIES;unsigned long esp0;unsigned long sysenter_cs;unsigned long eip;unsigned long esp;unsigned long fs;unsigned long gs;/* Hardware debugging registers */unsigned long debugreg8; /* %db0-7 debug registers */* fault info */unsigned long cr2, trap_no, error_code;/* floating point info */union i387_union i387;/* virtual 86 mode info */struct vm86_struct _user * vm86_info;unsigned long screen_bitmap;unsigned long v86flags, v86mask, saved_esp0;unsigned int saved_fs, saved_gs;/* IO permissions */unsigned long *io_bitmap_ptr; unsigned long iopl;/* max allowed port in the bitmap, in bytes: */unsigned long io_bitmap_max;4进程切换流程解析switch_to进程切换本质上两步:1) 进程页表PGD切换;2) 内核态堆栈和硬件上下文切换(包括CPU寄存器); 上面两步通过context_switch()实现,它通过调用switch_mm()切换进程空间,switch_to切换内核上下文环境。首先看看context_switch()做了些什么:1) 进程描述符中active_mm执行进程使用的地址空间,mm执行进程拥有的地址空间,对于普通进程它们相同。对于内核线程,它的mm总为NULL。所以context_switch()首先判断if (!next-mm)即next为内核线程,则使用prev的进程地址空间:if (!next-mm) next-active_mm = prev-active_mm; atomic_inc(&prev-active_mm-mm_count); enter_lazy_tlb(prev-active_mm, next);2) 否则,如果next是普通进程,则用next进程空间替换prev的地址空间: switch_mm(oldmm, mm, next);3) 如果prev是内核线程或者正在退出,则设置prev-active_mm 和runqueue的 prev_mm为NULL:if (!prev-mm) prev-active_mm = NULL; WARN_ON(rq-prev_mm); rq-prev_mm = oldmm;下面看看switch_mm()如何切换进程空间:1) 获取cpu逻辑号。2) 清除cpu_vm_mask位标志。cpu_clear(cpu, prev-cpu_vm_mask)3) 设置cpu_tlbstate状态。per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK4) 设置cpu_tlbstate的active_mm为next。per_cpu(cpu_tlbstate, cpu).active_mm = next5) 设置next的cpu_vm_mask标志。cpu_set(cpu, next-cpu_vm_mask)6) 装载next的pgd页表到cr3寄存器。load_cr3(next-pgd)7) 如果next的LDT描述符改变,则加载next的LDT描述符。if (unlikely(prev-context.ldt != next-context.ldt) load_LDT_nolock(&next-context);最后,switch_to进行内核堆栈和CPU环境切换操作:#define switch_to(prev,next,last) do / unsigned long esi,edi; / asm volatile(pushfl/n/t /* Save flags */ / pushl %ebp/n/t / movl %esp,%0/n/t /* save ESP */ / movl %5,%esp/n/t /* restore ESP */ / movl $1f,%1/n/t /* save EIP */ / pushl %6/n/t /* restore EIP */ / jmp _switch_to/n / 1:/t / popl %ebp/n/t / popfl / :=m (prev-thread.esp),=m (prev-thread.eip), / =a (last),=S (esi),=D (edi) / :m (next-thread.esp),m (next-thread.eip), / 2 (prev), d (next); / while (0)流程描述,prev是进程A的task结构,next是进程B的task结构,last是进程C的结构:1) 保存prev和next指针的值到eax和edx:movl prev, %eaxmovl next, %edx2) 保存eflags 和 ebp 寄存器内容到prev内核态堆栈中:pushflpushl %ebp3) 将esp内容保存到prev-thread.esp中,该字段执行prev内核堆栈的top地址。movl %esp,484(%eax)4) 将next-thread.esp加载到esp中,现在开始,esp执行next的内核堆栈,进程切换完成。movl 484(%edx), %esp5) 保存下面Label 1到prev-thread.eip指针中,当prev进程恢复运行时,从该位置开始运行。movl $1f, 480(%eax)6) 将next-thread.eip的指针内容压到next的内核态堆栈中,通常它的内容也是Label 1。pushl 480(%edx)7) 跳转到_switch_to()C函数执行。jmp _switch_to8) 被替换的进程A继续执行,它在Label 1处,首先是恢复eflags和ebp寄存器内容。注意这里是发生在调度器选择prev在CPU上运行后,次数esp已经执行了prev的内核堆栈。1: popl %ebp popfl9) 将eax内容保存到last任务结构中。这里eax是被进程A切换下来的进程C的task结构指针。movl %eax, last5_switch_to深入分析_switch_to参数是存放在eax和edx中的内容,这通过#define fastcall _attribute_(regparm(3)告诉gcc编译器。1) 获取tss_struct tss、prev_p和next_p的thread_struct结构prev和next、当前CPU逻辑ID。2) 调用_unlazy_fpu(prev_p)根据条件标志选择是否保存prev_p的FPU, MMX, 和XMM寄存器内容。3) load_esp0(tss, next)将next的堆栈地址存放到tss中:tss-esp0 = thread-esp0。4) savesegment(gs, prev-gs)保存gs寄存器到prev-gs,fs已经在栈入口保存,es和ds在内核态下不需要保存。5) load_TLS(next, cpu)从next的tls_array 缓存中加载线程的Thread-Local Storage描述符。TLS在GDT表中位置6、7、8。cpu_gdt_tablecpu6 = next_p-thread.tls_array0;cpu_gdt_tablecpu7 = next_p-thread.tls_array1; cpu_gdt_tablecpu8 = next_p-thread.tls_array2;6) 如果当前特权级别是0并且prev-iopl != next-iopl则恢复IOPL设置set_iopl_mask(next-iopl)。7) 根据thread_info的TIF标志_TIF_WORK_CTXSW和TIF_IO_BITMAP判断是否需要处理debug寄存器和IO位图:_switch_to_xtra(next_p, tss);l 只有当next_p挂起时即if (test_tsk_thread_flag(next_p, TIF_D

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论