Linux系统调用过程分析

上传人:do****y1 文档编号:183726715 上传时间:2023-01-31 格式:DOCX 页数:12 大小:41.93KB
收藏 版权申诉 举报 下载
Linux系统调用过程分析_第1页
第1页 / 共12页
Linux系统调用过程分析_第2页
第2页 / 共12页
Linux系统调用过程分析_第3页
第3页 / 共12页
资源描述:

《Linux系统调用过程分析》由会员分享,可在线阅读,更多相关《Linux系统调用过程分析(12页珍藏版)》请在装配图网上搜索。

1、Linux系统调用分析计算机962班 周从余一.与系统调用有关的一些基本知识1.系统调用的定义在OS的核心中都设置了一组用于实现各种系统共能的子程序并将它们提供 给用户程序调用.每当用户在程序中需要OS提供某种服务时,便可利用一条系统 调用命令,去调用所需的系统过程.所以说系统调用在本质上是一种过程调用.系统 调用是进程和操作系统之间的接口,这些调用一般就是一些汇编指令集,在Linux 系统中这些调用是用C语言和汇编编写的。用户只有通过这些系统调用才能使 用操作系统提供的一些功能.2系统调用与过程调用的区别过程调用调用的是用户程序,它运行在用户态;其被调用过程是系统过程,运行在系 统态下.系统

2、调用是通过软中断机制进入OS核心,经过核心分析后,才能转向响应的命令 处理程序.系统调用返回时通常需要重新调度.系统调用允许嵌套调用.3.中断与异常中断(interrupt)是由外部事件的,可以随时随地发生(包括在执行程序时)所以 用来响应硬件信号。在80386中,又把中断分为两种:可屏蔽中断(Miscible Interrupt)MI不可屏蔽中断(NonMaskable Interrupt)NMI异常(exception)是响应某些系统错误引起的,也可以是响应某些可以在程序中 执行的特殊机器指令引起的.异常也分为两种:处理器异常,(指令内部异常 如overflow等)编程(调试)异常(deb

3、ugger)每一个异常或中断都有一个唯一的标识符,在linux文献中被称为向量。指令内部 异常和NMK不可屏蔽中断)的中断向量的范围从031。32-255的任何向量都 可 以用做可屏蔽中断编程(调试)异常至于可屏蔽中断则取决于该系统的硬件配置。外部中断控制器(External interrupt controler)在中断响应周期(interrtupt acknowledge cycle)把中断向量放到总线上。中断和异常的优先级:最低:INTR中断。最高:除调试错误以外的所有错误 中断指令 INTO,INTn,INT3 当前指令的调试中断 下一指令的调试中断 不可屏蔽中断4.Intel386提

4、供的功能Intel386认识两种事件类:异常与中断。两者都会强制性创建一个进程或任务。 中断能在任何不可预料的时间发生,来响应硬件的信号.386能辨认两种中断来源 可屏蔽中断和不可屏蔽中断.并能辨认两种异常来源:处理器检测异常和程序异常 每一个中断和异常都有一个号码,都对应着一个相应的矢量地址,不可屏蔽中断 和处理器检测异常都已经被安排在0到31的矢量表中了,可屏蔽中断的矢量地 址由硬件决定,外部中断控制器在中断认可时钟周期时将矢量地址放到总线上。 任何在32到255范围内的矢量,都可以作为可屏蔽中断和程序异常用。以下是所 有可能的中断和异常的列表:0 Divide error1 Debug

5、exception3 NMI interrupt4 INTO-detected overflow5 Bound range exceeded6 Invalid opcode7 coprocessor not available8 double fault9 coprocessor segment overrun10 invalid task state segment11 segment not present12 stack fault13 general protection14 page fault15 reserved16 coprocessor error17-31 reserved

6、32-255 maskable interrupt二Linux系统调用的流程1.Linux系统调用的简单流程通常,在OS的核心中都设置了一组用于实现各种系统功能的子程序(过程), 并将它们提供给用户调用。每当用户在程序中需要OS提供某种服务时,变可利用 一条系统调用命令,去调用系统过程。它一般运行在系统态;通过中断进入;返回 时通常需要重新调度(因此不一定直接返回到调用过程)。Linux系统调用的流程非常简单,它由0x80号中断进入系统调用入口,通过使 用系统调用表保存系统调用服务函数的入口地址来实现.Processor调用 syscallN()2.Linux系统中断和异常的使用Linux中,

7、系统调用的执行是通过中断或异常的方式来进行的,他将执行相应 的机器代码指令,来产生中断或异常信号,产生中断或异常的重要效果是系统自 动将用户3模式切换为核心模式,并安排异常处理程序的执行。Linux设置了一个可屏蔽中断int 0x80,我们用向量0x80来把控制传给kernel 这个中断向量的设置(初始化)将在下文提到,这里就不多说了。得一提的是,存 在一个syscallX()宏(乂是作为实际程序调用时的参数)可以方便的调用那么多的 syscall. (syscallX() / usr/src/libc/syscall)每个 syscallX()宏都可以扩展成为一段汇 编代码,通过一个中断来初

8、始化系统调用堆栈和调用_system_call()函数。有关syscallX()的介绍0x80将控制传递给核心。0x80就是系统调用的一个矢量地址。这个中断矢量 表是在系统启动时就初始化好的,以及一些矢量地址,如系统时钟。当用户系统调 用时,执行如下:每个系统调用都通过lib库体现。每一个系统调用在lib库中一般是一个宏 syscallX(),X是具体某个调用的数字参数。有的系统调用更复杂,因为它们有可变 的参数列表,但它们仍用一样的入口指针。每个系统调用宏将展开成一个汇编段,用来建立调用的堆栈段然后通过 调用中断 int $0x80 调用-ENTRY(system_call).注:sysca

9、llX()宏在/usr/include/linux/unistd.h 中, 以下是用_syscallX()宏定义的一些系统调用。static inline _syscall0(int,idle)static inline _syscall0(int,fork)static inline _syscall2(int,clone,unsigned long,flags,char *,esp)static inline _syscall0(int,pause)static inline _syscall0(int,setup)static inline _syscall0(int,sync)stat

10、ic inline _syscall0(pid_t,setsid)static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)static inline _syscall1(int,dup,int,fd)static inline _syscall3(int,execve,const char *,file,char *,argv,char *,envp)static inline _syscall3(int,open,const char *,file,int,flag,int,mode)static inlin

11、e _syscall1(int,close,int,fd)static inline _syscall1(int,_exit,int,exitcode)static inline _syscall3(pidt,t,waitpid,pid_t,pid,int *,wait_stat,intoptions)static inline _syscall0(int,idle)static inline _syscall0(int,fork)static inline _syscall2(int,clone,unsigned long,flags,char *,esp)static inline _sy

12、scall0(int,pause)static inline _syscall0(int,setup)static inline _syscall0(int,sync)static inline _syscall0(pid_t,setsid)static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)static inline _syscall1(int,dup,int,fd)static inline _syscall3(int,execve,const char *,file,char *,argv,char*

13、,envp)static inline _syscall3(int,open,const char *,file,int,flag,int,mode)static inline _syscall1(int,close,int,fd)static inline _syscall1(int,_exit,int,exitcode)static inline _syscall3(pid_t,waitpid,pid_t,pid,int*,wait_stat,int,options)当int $0x80执行后,调用才传送到核心入口指针ENTRY(system_call)。在宏_syscallX(Param

14、eter)中x表示系统调用所需的参数的数目。Parameter是一组参数。SyscallX()宏的第一个参数表明,该系统调用最后调用的同名函数的返回值的 类型。SyscallX()宏的第二个参数表明,该系统调用的同名函数名。后面是系统调用所需要的每个参数,例:setuid()syscall1(int,setui_t,uid)l该例中,int是setuid的返回类型,setuid是函数名。Uid_t是参数类型,Uid是参数。用做系统调用的参数类型有一个限制,他们的容量不能超过4个字节,因为在执行int 0x80时,所有的参数都是通过寄存器传递的,而在386体系结构中,寄存器 是32位的.所以,他

15、们的容量不能超过4个字节(32位)。使用CPU寄存器做参数传递 的另一个限制是,可以传递的参数的数目,使用CPU寄存器做参数传递最多可以传递 五个参数,所以,一共定义了六个不同的syscallX()宏。(从syscall0()至U syscall5()宏) 一旦syscallX()宏被调用,系统使用其调用的特定参数进行扩展,(宏展开)得到的结果 是一个与系统调用同名的函数。他可以在用户的程序中被调用。当syscall O被调用后, 并没有任何的系统代码被执行,直到syscall()调用了 int 0x80 ,中断0x80把调用(控 制)传给核心入口地址中的_system_call(),这个入口

16、地址对任何系统调用都是一样的。_System_call()负责保护所有的寄存器,并检查系统调用是否合法,如果合法那么 根据从_sys_call_table中找出的偏移量,把控制权转给真正的系统。最后,当系统调用完成后,_system_call()还要负责调用_ret_from_sys_call()来断后。 _Ret_from_sys_call()检查是否有必要重新调度,如果有的话,调用他。3.Linux系统对系统调用矢量的初始化灿中断向量准备空间在head.S中调用(head.S在保护模式下的核心初始化中执行)Startup_32() /linux/boot/head.sSetup_idt()

17、 /linux/boot/head.sStartup_32()调用setup_idt来把一切都设置好。Setup_idt()函数初始化了IDT表,包括256个函数入口(每个入口 4字节,共1024字节),但是,没有一 个中断向量在这时被真正的设置好了,现在的IDT只是一个空架子,Setup_idt()是在paging机制刚起作用的时候被调用的,这时,kernel冈U被移 至0xC0000000的地方。IDT表 属性字0X8E00,所有entry的中断服务程序为ignore_int() ignore_int()只打印“unknown interrupt”。此时,idt寄存器尚未指向本表。 说白了

18、,刚才这一段的作用就是为idt表准备空间。Setup_idt ()的代码如下:setup_idt:lea ignore_int,%edxmovl $(KERNEL_CS 16),%eaxmovw %dx,%ax /* selector = 0x0010 = cs */movw $0x8E00,%dx/* interrupt gate - dpl=0, present */lea SYMBOL_NAME(idt),%edimov $256,%ecxrp_sidt:movl %eax,(%edi)movl %edx,4(%edi)addl $8,%edidec %ecxjne rp_sidtret

19、设置系统中断Linux进入保护模式对一些必要的核心数据进行初始化后,转入start_kernel()模块。该模块调用trap_init函数设置IDT表各项内容(arch/i386/kernel/traps.c).void trap_init(void)set_call_gate(&default_ldt,lcall7);set_trap_gate(0,÷_error);set_trap_gate(1,&debug);set_trap_gate(2,&nmi);set_system_gate(3,&int3);/* int3-5设置成system_gate(实际为DPL设置成3的*/

20、 set_system_gate(4,&overflow);/* 386陷阱门)可以让任意用户访问和调用.*/set_system_gate(5,&bounds);set_trap_gate(6,&invalid_op);/*各项只能由操作系统访问的出错陷阱处理入口 */ set_trap_gate(7,&device_not_available);/* trap_gate实际为DPL设置成0的*/ set_trap_gate(8,&double_fault);/* 386陷阱门*/set_trap_gate(9,&coprocessor_segment_overrun);set_trap_g

21、ate(10,&invalid_TSS);set_trap_gate(11,&segment_not_present);set_trap_gate(12,&stack_segment);set_trap_gate(13,&general_protection);set_trap_gate(14,&page_fault);set_trap_gate(15,&spurious_interrupt_bug);set_trap_gate(16,&coprocessor_error);set_trap_gate(17,&alignment_check);for (i=18;i48;i+)set_trap

22、_gate(i,&reserved);set_system_gate(0x80,&system_call);/*把中断0x80的入口设置为system_call*/其中与系统调用相关的是:set_system_gate(0x80,&system_call);设定了 0x80中断set_system_gate 的原形(在文件 arch/i386/kernel/traps.c 中)定义如下:#define set_system_gate(n,addr) _set_gate(&idtn,15,3,addr)其中_set_gate()”也是在该文件中定义的宏:#define _set_gate(gat

23、e_addr,type,dpl,addr) _asmvolatile_ (“movw %dx,%axnt” “movw %2,%dxnt” “movl %eax,%0nt” “movl %edx,%1” :=m” (*(long *) (gate_addr), =m” (*(1+(long *) (gate_addr) :”i” (short) (0x8000+(dpl13) + (type8), “d” (char *) (addr),”a” (KERNEL_CS 16) :”ax”,”dx”)gate_addr是一个指向64位门描述符的指针.上述代码所做的实际上是把门描述符 对应的32位偏

24、移地址(offset)设置成addr(处理程序的入口地址),段选择子(selector) 设置成KERNEL_CS核心段的段地址(因为各类中断和陷阱的处理程序都在核心部分),门 描述符属性字中的类型字段(Type)设置成type的值,而描述符的DPL字段设置成dpl的 值。因此,set_system_gate(0x80,&system_call)用宏展开后,实际上就是把中断描述 表(IDT)的第0x80项设置成为入口地址为system_call,描述符特权级DPL为3的386 陷阱门。这样,当用户程序使用INT 0x80指令时,就实现了应用程序从处于Ring 3用户地 址空间向Ring 0级的

25、操作系统核心空间的切换,并把CPU的控制权交给了操作系统, 由操作系统来执行具体的各项系统调用。3.INT 0x80 (即sy就的_c*)的具体实现当用户调用INT 0x80而进入system_call函数后,首先检查用来存放系统调用编号 的 eax 的值是否超出 IDT 表的项数 NR_syscalls(NR_syscalls 是在“/include/linux/sys.h” 文件中定义的宏,其值为256,表示80x86微机上最多可容纳的系统调用个数)。如没有 超出的话,就根据eax的值从系统调用表(sys_call_table)中得到对应的系统调用入口, 并通过call指令转入各个具体函数

26、(sys_*)的处理过程。系统调用表(sys_call_table)在“/arch/i386/entry.S”中定义,该表保存了所有Linux 基于Intel x86系列体系结构的计算机的166个系统调用入口地址,其中每项都被说明成 long型。下面是其中几项:.dataENTRY(sys_call_table).long SYMBOL_NAME(sys_setup).long SYMBOL_NAME(sys_exit).long SYMBOL_NAME(sys_fork).long SYMBOL_NAME(sys_read) .long SYMBOL_NAME(sys_chmod).long

27、 SYMBOL_NAME(sys_chown).long 0/*专门为afs_syscall保留的系统调用*/ .long SYMBOL_NAME(sys_mremap).long 0,0/* 2个被保留的系统调用*/.long SYMBOL_NAME(sys_vm86).space (NR_syscalls-166)*4这个sys_call_table以偏移量的方式来确定实际相应的系统调用代码,如sys_setup,sys_fork等,这些都是实际服务函数的入口地址,当系统调用被认为是 合法的时候(即调用INT 0x80时,eax的值小于NR_syscalls),将会进入这些具体的系 统服务

28、过程,执行相应的工作,完成所要求的功能。system_call的原代码也在Entry.S文件中,下面将对其作一分析,以清晰它的主要流 程.ENTRY(system_call)pushl %eax# save orig_eaxSAVE_ALL#调用宏“SAVE_ALL”保存现有通用寄存器.关于该宏的具体作用以 及所牵涉的数据结构将与“RESTORE_ALL” 一起在下文介绍.movl $-ENOSYS,EAX(%esp)#将返回码ENOSYS(表示由于调用了不存在的sys_ call而出现错误)存入刚才由SAVE_ALL压进堆栈的 EAX字段,以便当下面的代码检测到这种错误时,向用 户程序反馈信

29、息cmpl $(NR_syscalls),%eax #检测该系统调用是否合法,是否调用了不存在的 jae ret_from_sys_call # sys_call,如是,则出错返回.movl SYMBOL_NAME(sys_call_table)(,%eax,4),%eax#表示将eax的值乘以4个字节,找到在sys_call_table中 的实际地址,(因为在sys_call_table中,每一个项长度 为4个字节),并把相应系统调用代码的线性地址存入。3乂 寄存器,以便使用.testl %eax,%eax #检测是否调用了被保留的sys_calls(此时eax=0), je ret_fro

30、m_sys_call# 如是,则出错返回.movl SYMBOL_NAME(current_set),%ebx#把指向当前进程PCB的指针赋与ebx寄存器 andl $CF_MASK,EFLAGS(%esp) # clear carry - assume no errors movl %db6,%edx movl %edx,dbgreg6(%ebx)#保存当前硬件调试状态寄存器(DR6 ,当出现调试异常 事故时,处理机就把DR6置位,以表明异常事故的类型) 的信息.注:dbgreg6已在Entry.S中定义为值52,即当前 进程控制块偏移量为52字节处是用来保存硬件调试状 态寄存器的(相应的还

31、定义了其他字段的偏移量).testb $0x20,flags(%ebx) #检测当前进程控制块的flags字段的PF_TRACESYS位 是否置位,即进程是否处于调试状态.jne 1f#如果处于调试状态,则转入相应的处理过程.call *%eax#正式调用所选的系统调用(返回值存放在eax寄存器中).movl %eax,EAX(%esp) # save the return value jmp ret_from_sys_call#系统调用返回.ALIGN1: call SYMBOL_NAME(syscall_trace) movl ORIG_EAX(%esp),%eaxcall *SYMBOL

32、_NAME(sys_call_table)(,%eax,4)movl %eax,EAX(%esp) # save the return valuemovl SYMBOL_NAME(current_set),%eaxcall SYMBOL_NAME(syscall_trace=m (*(1+(long *) (gate_addr) :i” (short) (0x8000+(dpl13)+(type8), d (char *) (addr),a (KERNEL_CS =0)return (type) _res; errno=-_res; return -1; 即为:int open(const c

33、har* file,int flag,int mode)long _res;_asm_volatile(int$0x80:=a(_res):0(_NR_#open), b”(long)file),c”(long)flag), d(long)(mode);if(_res=0)return(int)_res;errno=-_res;return -1;这就是一个完整的系统调用,也就是我们编程中常常遇到的函数open。用来打开一个 文件.。在这里,open被定义为内联,主要是为了提高该系统调用的速度,并不是所 有的系统调用都使用内联的,在这个版本的Linux中,只定义了如上一些内联函数。(见Linu

34、x如何处理中断和异常)在这个函数中,有三个参数,file,flag,mode.它们的值分别被存入寄存器ebx,ecx,edx 并且eax指定为将返回值传回给_res。eax在这里被初始化为_NR_open,而_NR_open 在unistd.h中被宏定义为数值5#define _NR_open5这个数值就是实际系统调用程序段sys_open入口地址在sys_call_table中的索引, 这个参数被放入eax,将会作为sys_call_table的索引使用。asm_volatile将所有 参数指定,并调用中断int 0x80,如上所述由于中断矢量表初始化时,已经将其指向矢 量地址&system

35、_call,因此,系统转入该代码入口-ENTRY(system_call)2. 系统调用的通用入口地址:所有的系统调用都将使用这个入口 ENTRY(system_call),无论其参数到底是什么。 其最重要的参数-sys_call_table的索引都是放在寄存器eax中的,因此总能以 此找到相应的系统调用服务程序。如上所述,首先SAVE_ALL保存现场,然后将eax的值与$(NR_syscalls)比较因为_NR_open的值为5,没有超过256个系统调用的限制,因此合法,继续执行下去。Call *SYMBOL_NAME(sys_call_table)(,%eax,4)将以 eax 的值 5,

36、在 sys_call_table 中找到sys_open的入口地址,进入该程序。3. 进入实际的系统调用服务程序:asmlinkage int sys_open(const char * filename, int flags, int mode)char * tmp;int fd, error;/*关闭中断,在执行该服务程序时,不许其它中断响应 lock_kernel();/*找到一个空的文件描述符入口,然后将其空闲位置为否*/*如果找不到,返回负值*/fd = get_unused_fd();if (fd 0)goto out;/*大概尝试在内存页面内查找该文件*/tmp = getnam

37、e(filename);error = PTR_ERR(tmp);/*如果打开文件有错,退出*/if (IS_ERR(tmp)- goto out_fail;/*调用do_open打开文件*/error = do_open(tmp, flags, mode, fd);/*将文件名装入quicklist,放入内存中,加快下次的寻找*/ putname(tmp);if (error)goto out_fail;out:/*解锁,打开中断*/ unlock_kernel();return fd;out_fail:/*将fd号的空闲标志位置位,表示已释放*/ put_unused_fd(fd);fd

38、= error;goto out;当程序执行完了之后,系统返回到ENTRY(system_call)中,然后按照第二节 所叙述的流程,一步步进行。直到返回到原来的syscall3()宏定义的函数 error = PTR_ERR(tmp);/*如果打开文件有错,退出*/if (IS_ERR(tmp)-goto out_fail;/*调用do_open打开文件*/error = do_open(tmp, flags, mode, fd);/*将文件名装入quicklist,放入内存中,加快下次的寻找*/ putname(tmp);if (error)goto out_fail;out:/*解锁,打

39、开中断*/ unlock_kernel();return fd;out_fail:/*将fd号的空闲标志位置位,表示已释放*/ put_unused_fd(fd);fd = error;goto out;当程序执行完了之后,系统返回到ENTRY(system_call)中,然后按照第二节 所叙述的流程,一步步进行。直到返回到原来的syscall3()宏定义的函数 putname(tmp);if (error)goto out_fail;out:/*解锁,打开中断*/unlock_kernel();return fd;outfail:/*将fd号的空闲标志位置位,表示已释放*/put_unuse

40、d_fd(fd);fd = error;goto out;当程序执行完了之后,系统返回到ENTRY(system_call)中,然后按照第二节 所叙述的流程,一步步进行。直到返回到原来的syscall3()宏定义的函数 open中,此时,变量_res已取得返回值,如果文件打开时出错,此时_res 为负值,并将其绝对值赋给全局变量errno,作为以后处理错误信息 的参数,并且返回-1。如果文件打开成功,将返回_res.到此为止,就是一个完整的Linux中的系统调用.四.Linux系统调用分析的总结系统调用是操作系统与用户程序间的主要接口,系统调用是很底层的操作,这部分 直接关系到用户对整个系统资

41、源的使用问题。系统调用构架是否做的好,直接关系 到这个操作系统的效率和稳定性。因此,能够有一个成熟,完善的系统调用方式 是非常重要的,而它的设计也是较复杂的,它与进程的调度,中断的响应都有直接关系。 在我分析的这个版本的Linux中,系统调用是很有层次性的,用户不能直接的使用系统 提供的服务程序,而必须经过一个宏调用,进入系统调用入口,经过系统的许多确认和 调度后,才允许被调用,这也是几乎所有操作系统所必须做到的。经过对系统调用部分的分析,加深了我对操作系统的理解。在没有接触过操作 系统原代码以前,总认为操作系统是很神秘的,在这之后,我才发现其实它也是用我们 已经学过的C和汇编写的,只是需要巧妙的数据结构和高效率的算法.如果我们在这两方 面有了一定的造诣,我想我们也能写出一个操作系统来.通过这次实验,我学到了许多编 程方面的技巧,如模块的思想,C与汇编的连用等等.同时,我阅读程序的能力也有了一定 的提高,这次实验受益非浅.参考文献:David A Rusling “The Linux Kernel”Michael K. Johnson, Stanley Scalsky“How System Calls Work on Linux/i86徐峻忻尚波

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

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!