




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、ARM-uClinux下编写加载驱动程序详细过程本文主要介绍在uClinux下,通过加载模块的方式调试IO控制蜂鸣器的驱动程序。实验过程与上篇文章所 讲的过程基本相似,更多注重细节及注意事项。本文适合学习ARMLinux的初学者。/=硬件平台:MagicARM2200教学试验开发平台(LPC2290)Linux version 2.4.24,gcc version 2.95.3电路连接:P0.7蜂鸣器,低电平发声。实验条件:uClinux内核已经下载到开发板上,能够正常运行;与宿主机相连的网络、串口连接正常。/=编写蜂鸣器的驱动程序相对来说容易实现,不需要处理中断等繁琐的过程,本文以蜂鸣器的驱
2、动 程序为例,详细说明模块化驱动程序设计的主要过程和注意事项。一、编写驱动程序驱动程序的编写与上文所说的编写过程基本相同,这里再详细说明一下。/=蜂鸣器驱动程序:beep.c文件/#include /* 模块相关*/#include /* 内核相关*/#include /*linux 定义类型*/#include /*文件系统 file_opertions 结构体定义*/#include /* 出错信息*/*PINSEL0注意:低2位是UART0复用口,不要改动*/#define PINSEL0 (*(volatile unsigned*) 0 xE002C000)/*P0 口控制寄存器*/
3、#define IO0PIN (*(volatile unsigned*) 0 xE0028000) #define IO0SET (volatile unsigned*) 0 xE0028004)#define IO0DIR (volatile unsigned*) 0 xE0028008)#define IO0CLR (*(volatile unsigned*) 0 xE002800C)#define MAJOR_NUMBER 254/* 自定义的主设备号*/#define BEEP_CMD 0/*自定义的控制命令*/*函数声明*/static int beep_open(struct i
4、node *inode, struct file *file);static int beep_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int beep_release(struct inode *inode, struct file *file);static int beep_init(void);static void beep_cleanup(void);/ / *volatile static int beep_major = MAJOR_NUM
5、BER;/*全局变量:主设备号自定义为 254*/ /*/*注册函数:用到file_operations结构体。将蜂鸣器结构体自命名为beep_test,在注册模块时要用到*/static struct file_operations beep_test = owner : THIS_MODULE,ioctl : beep_ioctl,open : beep_open,release : beep_release,; /*注意:此处的分号(;)不要丢掉*/ / *#define BEEPCON 0 x00000080static void beep_port_init(void) 蜂鸣器端口初
6、始化:设置P0.7 口为输出,初始值为高(蜂鸣器不 发声) IO0DIR = BEEPCON;IO0SET = BEEPCON;static void beep(int beep_status) 蜂鸣器操作:根据参数(beep_status)状态判断是否发声if(beep_status = 0)IO0CLR = BEEPCON;elseIO0SET = BEEPCON;static int beep_open(struct inode *inode, struct file *file) /beep_test结构体中的 open()函数 实体,以下同MOD_INC_USE_COUNT; /注册
7、模块数加 1beep_port_init();return 0;static int beep_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)if(cmd = 0)printk(beep on!n);beep(0);elseprintk(beep off!n);beep(1);return 0;static int beep_release(struct inode *inode, struct file *file)MOD_DEC_USE_COUNT; 模块数减 1retur
8、n 0;static int beep_init(void) 模块加载、初始化函数:将模块加载到内核运行int result;result = register_chrdev(beep_major, named_beep”, &beep_test);if(result 0)printk(KERN_INFObeep: cant get major numbern);return result;if(beep_major = 0) beep_major = result;printk(KERN_INFObeep: init OK!n);/*注意:驱动程序运行在内核空间,从内核打印信息要用print
9、k()函数而不是printf()函数,而且要 配有优先级*/return 0;static void beep_cleanup(void) 模块卸载函数:将模块从内核卸载出去unregister_chrdev(beep_major, named_beep);/ / */*以下部分是驱动程序的关键,后面做详细说明*/module_init(beep_init);/module_exit(beep_cleanup);int init_module(void) /加载模块return beep_init();void cleanup_module(void) 卸载模块beep_cleanup();/
10、驱动程序文件结束 /以上是整个驱动程序文件的全部内容,将文件保存,这里将其命名为beep.c。整个驱动程序很简 单,只填写了几个操作函数beep_open()、beep_release()和beep_ioctl()。其实控制蜂鸣器用 beep_ioctl() 一个函数即可,其它函数基本都是空操作。在驱动文件最后的两个函数对驱动程序来数是及其重要的。应用程序与内核的区别就是应用程序 从头到尾完成一个任务,而内核则为以后处理某些请求而注册自己,完成这个任务后,他的“主”函数就 立即终止。换句话说,init_module()函数(名称不能更改)是模块入口点,如同应用程序的main()函数 一样,换句
11、话说,模块入口点init_module()函数的任务就是为以后调用模块的函数做准备; cleanup_module()函数(名称不能更改)是模块的第二个入口点,此函数仅当模块被卸载前才被调用。它 的功能是去掉init_module()函数所作的事情。这两个函数由linus/modele.h头文件声明,有关模块实 现的源代码可以参见./kernel/module.c。init_module()函数在模块被加载时执行,模块的初始化就是通过调用init_module()函数完成的。 它注册驱动设备,需调用register_chrdev()函数实现。register_chrdev有3个参数:(1):希
12、望获得 的设备主号,即beep_major全局变量,如果是0,系统将选择一个没有被占用的设备号返回;(2):设 备文件名,自定义设备文件名,这里用named_beep,它返回这个驱动程序所使用的主设备号;(3):用 来登记驱动程序实际执行操作的函数指针,即beep_test结构体。如果登记成功,register_chrdev返回 设备的主设备号;否则返回一个负值。模块是内核的一部分,但并未被编辑到内核中,他们被分别编译和连接成目标文件。用命令insmod 插入一个模块到内核中,用命令rmmod卸载一个模块。这两个命令分别调用init_module()函数和 cleanup_module()函数
13、。关于insmod和rmmod命令,后面还会用到。在2.3版本以后的Linux内核中,提供了一种新的方法来命名这两个函数。例如可以定义 beep_init()和beep_cleanup()两个函数,然后在源代码文件末尾使用下面的语句,其效果是一样的。module_init(beep_init);module_exit(beep_cleanup);注意:这两个宏是在linux/init.h中被定义的,所以源码文件中必须包括这个头文件。而且, 这两行语句必须在函数声明后使用,否则会编译出错。驱动程序部分先暂且介绍到这,继续往下介绍,如何将驱动程序加载到内核中去,又如何利用驱 动程序来控制蜂鸣器发声
14、。二、编译驱动程序编译驱动程序的过程比较无聊,按照步骤一步一步进行即可。但首先需要了解linux的基本操作 命令,最好会Makefile文件的编写,至少能看懂也行。下面一步一步的介绍:1、将驱动程序beep.c文件传到宿主机中。这里所说的“宿主机”是运行Linux的PC机,可以是安装了 Linux操作系统的本地机,亦可以是 Linux服务器。由于嵌入式Linux的开发板资源有限,不可能在开发板上运行开发和调试工具。统称需要 交叉编译调试的方式进行,即“宿主机+目标板”的形式。程序在宿主机上编译一连接一定位,得到的可执 行文件则在目标板上运行。而“目标板”就是实验的硬件平台MagicARM220
15、0教学试验开发平台。本实验是在PC机上通过虚拟机搭建的宿主机。目标板和宿主机通过串口和网口连接,其中串口当 作终端,作为人机交互界面。若目标板可以看成一台计算机的话,那么串口终端就相当于这台计算机的显 示器,通过linux命令对目标板进行相关操作;而网口是与宿主机相连接,作为数据传输、共享的通道。 这里利用linux的NFS服务器将宿主机系统下/home/armwork目录作为共享目录,在目标板上通过mount 命令将此目录挂在到目标板上的/mnt目录下,于是打开目标板的/mnt目录所见的内容就是宿主机上 /home/armwork目录的内容。当然以上所说的内容包括宿主机的建立、交叉开发环境(
16、arm-elf-gcc)的安装、uClinux系统 移植、网卡串口驱动、嵌入TCP/IP协议栈等工作都已经做好,这里只是利用这一平台介绍驱动程序的编写。 而且这些工作步骤也比较单调,可以很容易找到现成的步骤说明,这里就不做过多说明了。说了一堆前提条件,现在开始继续。下面的工作都是在宿主机上进行的。前面所说的beep.c驱动文件是在Windows环境下编写的(当然也可以在Linux下编写,如用vi 编写),使用SSH Secure或FlashFXP等FTP工具,将beep.c文件上传到宿主机中,先在宿主机的 /home/armwork目录下建立一个新目录命名为beep。在宿主机的终端命令行上输入
17、下面两条命令:(这里 假定宿主机中已经存在有/home/armwork目录,当然也可以直接在图形编辑环境下新建目录)cd /home/armworkmkdir beepcd beep这三条命令意思分别为:将/home/armwork目录指定为当前目录;在当前目录下建立beep目录;将beep 目录指定为当前目录。然后,利用FTP工具将beep.c文件上传到刚刚建立的beep目录下,输入ls命令显示当前目录 下的内容:ls -l(命令均为为字母l,不是数字1)显示内容为文件的详细信息:-rw-r一r一1 root root2891 5 月 5 18:12 beep.c依次为操作权限、用户、文件大
18、小、日期、文件名等信息。2、编写Makefile文件。以下是Makefile文件的详细内容,将其保存命名为Makefile (文件名不能更改)#各项对应驱动程序的文件名。EXEC = beep OBJS = beep.o SRC = beep.c#交叉环境所在的目录,根据各自机器存放位置修改。INCLUDE = /usr/src/uClinux-dist/linux-2.4.x/include#所使用的交叉环境 CC = arm-elf-gcc LD = arm-elf-ldMODCFLAGS = -D_KERNEL_ -I$(INCLUDE) -Wall -O2 -fno-strict-al
19、iasing -fno-common -pipe -fno-builtin -D_linux_ -g -DNO_MM -mapcs-32 -march=armv4 -mtune=arm7tdmi -mshort-load-bytes -msoft-float -nostdinc -iwithprefix include LDFLAGS = -m armelf -rall: $(EXEC)$(EXEC): $(OBJS)$(LD) $(LDFLAGS) -o $ $(OBJS)%.o:%.c$(CC) $(MODCFLAGS) -mapcs -c $ ./loadbeep 注意:是“.”和“/”
20、后面跟文件名,表示执行此文件。执行此文件等同于执行以上命令。同时还用注意 权限问题,如果出现“./loadbeep: Permission denied ”提示信息,表示目标板没有对宿主机文件执行 的权限。这时需要在宿主机上修改loadbeep文件的使用权限,在宿主机的root用户(linux下拥有最好 权限的用户)下输入以下命令:# chmod 755 loadbeep这样,其它用户就拥有执行该文件的权限了。其中命令前的“#”表示root用户,$”表示其它用户。若模块使用后,不再需要,则可以使用rmmod命令卸载模块:rmmod beep这一命令将调用cleanup_module()函数。若
21、要查看当前已经加载过的设备模块,可以使用lsmod命令查看:lsmod这一命令只是输出的是/proc目录下的modules文件,当然可以用cat命令直接查看该文件。至此,模块加载工作也完成了。四、编写应用程序,测试驱动模块编写测试应用程序和编写驱动程序的过程基本相同,这里不再重复,程序代码如下:/=测试程序:main.c文件/#include main()int fd;int i;fd = open(/dev/beep, O_RDONLY);/*打开设备文件beep, O_RDONLY表示以只读方式打开,并会调用驱动程序中的beep_open()函数*/if(fd = -1) 若打开失败,则报
22、告出错,退出printf(Can not open filen);exit(-1);for(i=0; i3; i+) /让蜂鸣器每隔1秒响一次,共响三次ioctl(fd, 0, 0); /IO操作函数,指令码为0,调用驱动程序中的beep_ioctl()函数,控制蜂 鸣器发声sleep(1); /Linux系统函数,让进程暂停一段时间,可用于延时,时间单位是秒ioctl(fd, 1, 0); /IO操作函数,指令码为1,调用驱动程序中的beep_ioctl()函数,控制蜂 鸣器停止发声sleep(1);close(fd); /关闭设备文件,并会调用驱动程序中的beep_release()函数p
23、rintf(Success!n); 用户程序运行在用户空间,打印信息用printf()函数return(0);/测试程序文件结束/=测试程序完成,下面将测试程序按照上传驱动程序的方法上传到宿主机中。在宿主机的/home/armwork目录下新建一个目录,这里命名为exc目录。将测试程序main.c上 传到这个目录下。下面编写编译测试程序的Makefile文件。#EXEC = mainOBJS = main.oSRC = main.cCC =arm-elf-gccBASEPATH =/usr/src/uClinux-distLIBPATH =$(BASEPATH)/libLLIBPATH =$(
24、LIBPATH)/uClibc/libINCLUDEPATH =$(BASEPATH)/linux-2.4.x/includeLDFLAGS =-Os -g -Dlinux -D_linux_ -Dunix -D_uClinux_ -DEMBEDLDLIBS =-I$(LIBPATH)/uClibc/include -I$(LIBPATH)/libm -I$(LIBPATH)/libcrypt_old -I$(BASEPATH) -fno-builtin -nostartfiles -D_PIC_ -fpic -msingle-pic-base -I$(INCLUDEPATH)LDLIBS_E
25、XEC =-Wl,-elf2flt $(LLIBPATH)/crt0.o $(LLIBPATH)/crti.o $(LLIBPATH)/crtn.o -L$(LIBPATH)/uClibc/. -L$(LLIBPATH) -L$(LIBPATH)/libm -L$(LIBPATH)/libnet -L$(LIBPATH)/libdes -L$(LIBPATH)/libaes -L$(LIBPATH)/libpcap -L$(LIBPATH)/libcrypt_old -L$(LIBPATH)/libssl -L$(LIBPATH)/zlib -lc LDLIBS_OBJS =-call: $(
26、EXEC)$(EXEC): $(OBJS)$(CC) $(LDFLAGS) $(LDLIBS) $(LDLIBS_EXEC) -o $ $(OBJS)%.o:%.c$(CC) $(LDFLAGS) $(LDLIBS) $(LDLIBS_OBJS) -c $ -o $clean:-rm -f $(EXEC) *.elf *.gdb *.o#保存并命名为Makefile (文件名不能更改),将当前目录设置为/home/armwork/exc目录,上传 Makefile文件到当前目录中,输入make命令,编译测试程序main.c文件,生成main.o目标文件和main 可执行文件。显示输出以下结果:
27、arm-elf-gcc -Os -g -Dlinux -D_linux_ -Dunix -D_uClinux_ -DEMBED -I/usr/src/uClinux-dist/lib/uClibc/include -I/usr/src/uClinux-dist/lib/libm -I/usr/src/uClinux-dist/lib/libcrypt_old -I/usr/src/uClinux-dist -fno-builtin -nostartfiles -D_PIC_ -fpic -msingle-pic-base -I/usr/src/uClinux-dist/linux-2.4.x/
28、include -c -c main.c -o main.oarm-elf-gcc -Os -g -Dlinux -D_linux_ -Dunix -D_uClinux_ -DEMBED -I/usr/src/uClinux-dist/lib/uClibc/include -I/usr/src/uClinux-dist/lib/libm -I/usr/src/uClinux-dist/lib/libcrypt_old -I/usr/src/uClinux-dist -fno-builtin -nostartfiles -D_PIC_ -fpic -msingle-pic-base -I/usr
29、/src/uClinux-dist/linux-2.4.x/include -Wl,-elf2flt /usr/src/uClinux-dist/lib/uClibc/lib/crt0.o /usr/src/uClinux-dist/lib/uClibc/lib/crti.o /usr/src/uClinux-dist/lib/uClibc/lib/crtn.o -L/usr/src/uClinux-dist/lib/uClibc/.-L/usr/src/uClinux-dist/lib/uClibc/lib -L/usr/src/uClinux-dist/lib/libm -L/usr/sr
30、c/uClinux-dist/lib/libnet -L/usr/src/uClinux-dist/lib/libdes -L/usr/src/uClinux-dist/lib/libaes -L/usr/src/uClinux-dist/lib/libpcap-L/usr/src/uClinux-dist/lib/libcrypt_old -L/usr/src/uClinux-dist/lib/libssl -L/usr/src/uClinux-dist/lib/zlib -lc -o main main.o编译后,该目录多出几个文件。用ls命令查看详细文件信息,显示结果如下:-rwxr-r
31、-1rootroot29464 5月6 11:34 main-rw-r-r-1rootroot515 5 月5 17:31 main.c-rwxr-xr-x1rootroot742336 5月6 11:34 main.gdb-rw-r-r-1rootroot6828 5 月6 11:34 main.o-rw-r-r-1rootroot951 5月5 14:42 Makefile从显示结果可以看出,现在的main可执行文件只有宿主机的root用户拥有可执行权限,其它用户只拥有 只读权限,使用chmod命令更改权限:# chmod 755 main用ls命令查看修改后的文件详细信息为:-rwxr-
32、xr-x 1 root root29464 5 月 6 11:34 main这样,目标板也拥有对该文件的可执行权限了。现在回过头来再看目标板,通过超级终端,设置当前目录为/mnt/exc目录,执行main文件(输 入./main命令),一切正常则可以听到测试程序所设计的:蜂鸣器每秒响一声,共响三声,同时超级终端 有如下显示输入:beep on!beep off!beep on!beep off!beep on!beep off!Success!其中最后一条“Success!是在测试程序主函数里倒数第二条代码实现的(见前面的测试程序),其它显 示是在驱动程序中beep_ioctl()函数里实现的
33、。至此,测试工作完成。五、总结本文从编写驱动程序开始到编译驱动程序、加载模块一直到测试驱动程序,详细的介绍了如何在 ARM的uClinux系统环境下加载模块化驱动程序及具体实现过程,完整阐述了模块化驱动程序的编写方法 及注意事项,对ARM-Linux的初学者来说能起到一定的引导作用。而本文对Makefile文件的编写及分析没 有进行介绍,在以后的文章中会详细介绍Makefile文件的编写。以上所完成的实验过程是在MagicARM2200 教学试验开发平台实验测试通过的,所将内容大多是我的个人理解,而我也只是初学者,因此纰漏在所难 免,也望各位读者指教。全文完Linux下编写驱动程序2008年0
34、5月01日 星期四18:44摘要:设备驱动程序是操作系统内核与机器硬件之间的接口。设备驱动 程序为应用程序屏蔽了硬件的细节。那么驱动程序如何书写实现这一接口功能是 本文讨论的重点,并以一简单的驱动程序介绍书写细节。在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。 (应用程序一般是在用户态下进行)也就是说系统必须在驱动程序的子函数返回 后才能进行其它的工作,即驱动程序不能进入死循环。字符型设备驱动程序的编写包含一下信息:#define _NO_VERSION_#include #include char kernel_version=UTS_RELEASE这段定义了一些版本信息,
35、虽然用处不大,但也必不可少。最好要包含。由于用户进程是通过设备文件同硬件打交道,对 设备文件的操作不外乎就是一些系统调用,如open,read,write,close ,(注意,不是fopen,fread,)但是如何把系统调用和驱动程序联系起来呢? 这需要了解一个非常关键的数据结构:struct file_opertions(int(*seek)(struct inode*, struct file*, off_t, int);/* 文件定位*/ int(*read)(struct inode*, struct file*, char, int);/* 读取数据*/ int(*write)(s
36、truct inode*, struct file*, off_t, int);/* 写数据 */ int(*readdir)(struct inode*, struct file*, struct dirent*, int);/*取 相关目录*/int(*select)(struct inlde*, struct file*, int, select_table*);/非阻塞 设备访问*/int(*ioctl)(struct inlde*, struct file*, unsigned int, unsigned long); int(*mmap)(struct inlde*, struct
37、 file*, struct vm_area_struct*);int(*open)(struct inlde*, struct file*);int(*release)(struct inlde*, struct file*);int(*fsync)(struct inlde*, struct file*);/* 强制同步*/int(*fasync)(struct inlde*, struct file*);int(*check_media_change)(struct inlde*, struct file*);int(*revalidata)(dev_t dev);/* 使设备重新有效*
38、/ 其中 read, write, open, close (release), ioctl 是最核心的,必 须实现的。这个结构体的每个成员的名字都对应着一个系统调用。用户进程利用系 统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主 设备号找到相应的设备注册程序,然后读取这个数据结构相应的函数指针,接着 把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样, 则编写设备驱动程序的主要工作就是编写子函数,并填充file_operatons的各 个域。以下是简单的字符型设备的驱动程序编写方式,例子程序并不牵扯到具 体设备,只是个编写框架。#inc
39、lude /Linux 基本类型定义#include /文件系统相关头文件#include /memmory management 内存管理#include /错误代码#include /汇编文件unsigned int test_major = 0; /*定义一个主设备号(主设备号、从设备号在 Linux设备管理中有相关介绍)*/static int read_test(struct inode *inode, struct file *file, char *buf, int count)( /*本函数对应于file_opertions中read的实现,函数名自己定义。inode为设 备节
40、点,file为设备文件描述符(open()打开后自动或得),buf为数据缓冲区, count为数据传送个数。“static ”这里修饰函数名表示函数只在本文件中有效。这里函数只实现简单数据拷贝功能。*/int left;if(verify_area(VERIFY_WRITE, buf, count) = -EFAULT) /验证缓存中 的数据是否有效return -EFAULT; /错误码,在 包含for(left二count; left0; left-)(_put_user(1, buf, 1);/* “ _”表示内核调用函数,此函数表示把数据从内核空间放到用 户空间,参数依次表示:填充数、
41、用户空间、数据量。*/buf+;return count;这个函数是为read调用准备的。当调用read时,read_test()被调用, 它把用户的缓冲区全部写1。buf是read调用的一个参数。它是用户进程空间的 一个地址。但是在read_test被调用时,系统进入核心态(内核空间),必须用 put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有 很多类似功能的函数,参考内核调用接口函数。在向用户空间拷贝数据之前,必 须验证buf空间是否可用。这就用到verify_area()。static int write_test(struct inode *inode
42、*inode, struct file *file, const char *buf, int count)(return count;写数据函数,具体没有实现,直接返回计数值。static int open_test(struct inode *inode, struct file *file)MOD_INC_USE_COUNT; /宏:注册模块数加 1return 0; 返回0表示成功,根据函数自己定义。这个函数比较简单,它不牵扯到设备文件,仅将模块数加1。static void release_test(struct inode *inode, struct file *file)(MO
43、D_DEC_USE_COUNT; /模块数减 1以上实现四个函数,后三个函数都是空操作,实际调用发生时什么也不 做,它们仅仅为file_operations结构体提供函数指针。下面开始注册刚刚写好的函数struct file_operations test_fops =/*file_operations 结构体名,test_fops 结构体对象*/(NILL, /*seek*/read_test,write_test,NULL,/*test_readdir*/NULL,/*test_mmap*/open_test,release_test,NULL, /*test_fsvnc*/NULL, /
44、*test_fasync*/ /*其它位置均填为空NILL*/;设备驱动程序的主体可以说是写好了,现在把驱动程序嵌入内核。驱动 程序可用按照两种方式编译。一种是编译进内核(kernel),另一种是编译成模 块(modules)如*.o文件,如果编译进内核的话,会增加内核的大小,还要改 动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。1、登记注册设备:方式一:编译进内核,利用函数init_module()int init_module(void)(int result;result = register_chrdev(0, test”, &test_fops);/*内核函数:注册一个字符型设备到内核中去。参数:指定设备号(0:表 示内核根据主设备号获得并返回给result)、设备名、设备结构体名*/if(result 0)(printk(KERN_INFOtest: cant get major numbern);/*在内核空间驱动程序打印数据,参数:打印优先级、打印信息*/return result;if(test_major = 0) test_major = result;return 0;方式二:编译加载模块方式,利用insmod命令,在用insmod命令将编译好的模 块调用内存时,init_module
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年医疗机构药品购销协议模板
- 2025年企业股权变动合作协议模板
- 2025年中国银行住宅按揭贷款合同文本
- 2025年住宅楼梯安全性能提升申请协议样本
- 2025年交通工具采购与租赁合作协议范本
- 2025年档案寄存合同模板
- 2025年卫生保障合作协议
- 2025年公寓室内设计施工合同范本
- 2025年兄弟户籍划分协议范本
- 2025年精简版子女抚养权协议书范本
- 2025年舞蹈培训机构学员培训合同范本
- 2025年保险销售业务人员岗位职业技能资格知识考试题(附答案)
- 儿科护理模拟考试题与参考答案
- 注意缺陷与多动障碍疾病科普幼儿心理健康教育课件
- 水利行业知识培训课件
- 区域临床检验中心
- 2025-2030年中国人力资源服务行业全国市场开拓战略制定与实施研究报告
- 2024年07月长沙农村商业银行股份有限公司2024年招考3名信息科技专业人才笔试历年参考题库附带答案详解
- 中医预防流感知识讲座
- 事故隐患内部报告奖励机制实施细则
- 《CT、MR的临床应用》课件
评论
0/150
提交评论