版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、精选优质文档倾情为你奉上精选优质文档倾情为你奉上专心专注专业专心专注专业精选优质文档倾情为你奉上专心专注专业 中国石油大学(华东)操作系统课程设计设计报告螺旋Matrix字符串对比Ncurse界面脚本FloppyLinux驱动Driver进程汇总线程警察小偷线程哲学家系统调用Shell编写模拟内存评语成绩中国石油大学(华东)计算机科学与技术学院要求(本页打印):双面打印,内容篇幅不要过长(每个实验不要超过3页),禁止贴全部程序,只贴关键代码即可。禁止抄袭实验列表如下:实验1:螺旋矩阵实验 Linux下的C编程实验2:字符串对比实验 Linux下的C编程实验3:界面编写实验 基于NCurses的
2、文本界面实验4:脚本编写实验 批量建立和删除用户实验5:内核定制实验 FloppyLinux的实现实验6:驱动程序实验 实现驱动程序插入内核并调用实验7:进程汇总实验 创建、信号量机制和管道实验8:线程操作实验 警察小偷的PV操作实验9:线程操作实验 哲学家进餐的资源竞争实验10:系统调用实验 添加系统调用实验11:终端编写实验 模拟终端shell实验12:内存模式实验 实现malloc和free的内存模拟过程每个实验要求格式如下(内容使用5号字):实验:实验题目实验情景描述实验原理关键代码(包括截图)实验结果及说明总结实验1:螺旋矩阵实验 Linux下的C编程一、实验情景描述完成一个程序,要
3、求输入两个数字即可形成相应的字母螺旋矩阵。例如输入5,6,则程序会生成如下5行6列的矩阵,Z之后循环至A:A BCDEFR STUVGQ BCDWHP AZYXIONMLKJ二、实验原理完成程序ju.c,并用Makefile完成编译。三、关键代码Makefile如下CC=gccOBJS=ju.oEXEC=juall:$(EXEC)$(EXEC):$(OBJS)$(CC) -o $ $(OBJS) clean:rm -f $(OBJS) $(EXEC)ju.c部分代码如下 int total = 1; char digit = 65; x = 0, y = 0; axy = 65; while(
4、total m*n) while(y+1=90) digit = 64; ax+y = +digit; +total; while(x+1=90) digit = 64; a+xy = +digit; +total; while(y-1=0&!axy-1) if(digit=90) digit = 64; ax-y = +digit; +total; while(x-1=0&!ax-1y) if(digit=90) digit = 64; a-xy = +digit; +total; 四、实验结果实验2:字符串对比实验 Linux下的C编程一、 实验情景描述完成一个程序,输入两个字符串,判断其
5、中一个字符串是否是另一个的子串,如果是则输出true,否则输出false。二、 实验原理完成程序ju.c,并用Makefile完成编译。CC=gccOBJS=mystring.oEXEC=mystringall:$(EXEC)$(EXEC):$(OBJS)$(CC) -o $ $(OBJS)clean:rm -f $(OBJS) $(EXEC)三、 关键代码(包括截图)Ju.c的部分代码如下:int main() char s1N,s2N; gets(s1); gets(s2); if ( strstr(s1,s2) ) printf(truen); else printf(falsen);
6、return 0;四、 实验结果及说明总结实验3:界面编写实验 基于NCurses的文本界面实验情景描述将螺旋矩阵程序和字符串程序集成在基于Ncurse的文本界面上,对其进行实现。二、 实验原理Ncurses是一个能提供功能键定义(快捷键),屏幕绘制以及基于文本终端的图形互动功能的动态库。其中,libncurses库用来在显示器上显示文本界面。关键代码部分代码如下: int x=StartX,y=StartY,ch; initial(); box(stdscr,0,0); attron(A_REVERSE); mvaddstr(0,20,TCS NAS Server v1.13 by Cybe
7、rAct); attroff(A_REVERSE); mvaddstr(6,19,* juzheng); mvaddstr(8,19,* mystring); mvaddstr(18,19,* Exit); move(y,x); do ch=getch(); switch(ch) case KEY_UP : if(y=7) y=y-2; else y=y; break; case KEY_DOWN : if(y=17) y=y+2; else y=y; break; case r : if(y=18) endwin(); exit(1); else if(y=16) system(sudo -
8、b /sbin/init 0); else if(y=14) system(sudo -b /sbin/init 6); else if(y=6) endwin(); system(./ju); else if(y=8) endwin(); system(./mystring); else if(y=10) endwin(); clear(); else if(y=12) endwin(); clear(); break; case 27 : endwin(); exit(1); default : break; move(y,x); while(1);四、 实验结果实验4:脚本编写实验 批量
9、建立和删除用户实验情景描述此shell程序实现给系统添加四个新组,每个组代表一个班级,每个班级中添加30个用户。二、 实验原理Shell脚本以文本方式存储,并且必须通过Shell解释后才能够执行。Shell本身是一个程序,负责解释和执行用户输入的命令,也可以用来进行程序设计,它是用户和Linux系统交互的纽带。因为Shell脚本是以文本方式进行存储的,所以可以用任何的文本编辑器来编辑。在文本中输入要执行的Shell命令或Linux命令并保存为一个新的文件。当要运行这个脚本时,可调用这个脚本来执行其中的所有指令。关键代码部分代码如下:#!/bin/shfor class in 1 2 3 4do
10、for number in $(seq 01 30)doUSERNAME=stu$class$numberuseradd $USERNAMEchown -R $USERNAME /home/$USERNAMEdonedone实验结果实验5:内核定制实验 FloppyLinux的实现实验情景描述使用软盘进行GRUB配置,之后使用make menuconfig进行内核配置,之后使用Busybox编译生成一个新的根文件系统,把这些整合到软盘中完成FloppyLinux。二、 实验原理首先打开RedHat虚拟机,将老师给的busybox-1.00.tar.bz2文件拷贝到虚拟机中。为虚拟机增加虚拟软盘
11、在软盘上安装引导器(grub)。首先对软盘建立ex2文件系统,之后将系统中grub目录下的引导文件stage1,stage2复制到软盘最后配置grub信息配置busybox相关选项a.在根目录下新建floppylinux目录,floppylinux下再新建floppylinux,将busybox-1.00.tar.bz2复制到第二个floppylinux下。b.进入busybox-1.00文件夹make menuconfig之后进入配置界面。编译并安装busybox建立临时目录,该目录为软盘的文件系统建立设备列表建立启动配置文件,之后建立连接文件menu.lst制作镜像文件initrd.img
12、,将先前做好的floppylinux根文件系统拷贝到ram1上。检查initrd.img,用loop设备来把他重新挂载到文件系统里,之后压缩initrd.img镜像文件查看压缩之后的大小,大约在403k左右编译linux系统内核,完成配置,执行完毕之后会生成一个bzImage文件,大约在660k左右整合启动盘,重启电脑,输入回车即可输入linux的命令,进入floppylinux。三、 关键代码每一步骤的部分代码如下: rootlocalhost root#mke2fs /dev/fd0rootlocalhost root#mount /dev/fd0 /mnt/floppyrootlocal
13、host floppy# mkdir bootrootlocalhost floppy# mkdir boot/grubrootlocalhost floppy# cp /boot/grub/stage1 /mnt/floppy/boot/grub/rootlocalhost floppy# cp /boot/grub/stage2 /mnt/floppy/boot/grub/rootlocalhost floppylinux# tar -xjvf busybox-1.00.tar.bz2rootlocalhost busybox-1.00# make menuconfigrootlocalh
14、ost _install# mkdir /floppylinux/floppylinux/floppyImagerootlocalhost floppyImage# cp /floppylinux/floppylinux/busybox-1.00/_install/* /floppylinux / floppylinux/ floppyImage/ -rrootlocalhost floppyImage# mkdir dev etc etc/init.d proc mnt tmp varrootlocalhost floppyImage# chmod 755 dev etc etc/init.
15、d bin mnt tmp varrootlocalhost floppyImage# chmod 555 procrootlocalhost etc# vi inittabinittab内容为:sysinit:/etc/init.d/rcS:askfirst:/bin/shrootlocalhost etc# vi fstabfstab内容为proc/procprocdefaults00rootlocalhost init.d# vi rcSrcS内容为:#!/bin/shmounta rootlocalhost grub# vi grub.confgrub.conf内容为:timeout0
16、default10title FloppyLinuxroot(fd0)kernel/boot/bzImageinitrd/initrd.img.gzrootlocalhost grub# ln -s grub.conf menu.lst四、 实验结果 实验6:驱动程序实验 实现驱动程序插入内核并调用一、实验情景描述在其中编写驱动程序matrixdriver.c文件和Makefile文件。之后编译生成驱动程序模块。基于该驱动程序模块编写测试程序matrixtest.c文件。然后将驱动程序模块装入到目标开发板上,建立设备结点并连接,运行测试程序测试之。设定驱动程序实现的字符设备,主设备号为111,
17、此设备号为0。实现的功能是,将驱动程序和第二章的第三题结合起来。即测试程序输入两个数字,在驱动程序即可形成相应的字母螺旋矩阵。二、实验原理首先完成三个脚本的内容。分别编写matrixdriver.c、Makefile和matrixtest.c。其中matrixdriver.c是驱动程序,matrixtest.c是测试程序。完成编写之后,执行make,生成驱动模块evan.o和可执行文件test_demo。之后首先在/dev目录下建立设备结点,并插入驱动模块。之后运行程序matrixtest。完成之后,卸载驱动模块,删除结点。三、 关键代码matrixtest.c部分代码如下:void show
18、buf(char *buf,int m,int n)int i,j=0;int a10241024; for(i=0;im;i+) for(j=0;jn;j+) aij=bufi*n+j; for(i=0;im;i+) for(j=0;jn;j+) printf( %c ,aij); printf(n);Matrixdriver.c部分代码如下: while(total m*n) while(y+1=90) digit = 64; ax+y = +digit; +total; while(x+1=90) digit = 64; a+xy = +digit; +total; while(y-1=
19、0&!axy-1) if(digit=90) digit = 64; ax-y = +digit; +total; while(x-1=0&!ax-1y) if(digit=90) digit = 64; a-xy = +digit; +total; int i,j,k=0; for(i=0;im;i+)for(j=0;jMAX_BUF_LEN) count=MAX_BUF_LEN;copy_to_user(buffer,drv_buf,count);printk(user read data from drivers!n);return count;四、 实验结果实验7:进程汇总实验 创建、
20、信号量机制和管道实验情景描述进程是在CPU及内存中运行的程序代码,是动态执行的代码。在一个操作系统中,每个正在运行的工作都可以称为进程(process)。它包括进程标识符和相关的数据,相关数据又包含进程变量、外部变量以及进程堆栈等。当一个进程启动之后,系统会指定一个唯一的数值来作为该进程的进程标识符,即PID。二、 实验原理1.编写fork.c和Makefile文件。理解新进程的建立和消除zombie进程的过程。2.编写signal.c和Makefile文件。理解kill函数和signal函数的用法,并且理解SIGALRM的用法。signal():用来接收一个指定类型的信号,并调用相关的信号处
21、理函数。kill():用来传送sig参数所指定的信号给pid参数所指定的进程。3.管道(pipe)也是进程通信方式的一种。它用来连接一个进程的输入和另一个进程的输出,是一种单向的通信方式。3.1编写pipe.c和Makefile文件。理解管道的建立,以及管道的特征和通信等内容。pipe():该函数运行之后会打开两个文件描述符。其中fd0用来读取,fd1用来写入。如果pipe()函数运行成功,则返回0;如果发生错误,返回-1。3.2编写popen.c和Makefile文件。理解高级管道的建立和使用。popen():建立一个管道。如果函数运行成功,则返回文件指针;如果发生错误,返回NULL。3.3
22、编写fifo1.c、fifo2.c和Makefile文件。理解有名管道的建立和使用。FIFO即有名管道。它可以在不相关的进程之间进行通信。因为它是通过系统中的文件进行通信,其他的进程只要知道FIFO文件名称,即可加入通信;另外FIFO可以同时具有多个读取端和写入端,多个进程可以同时写入同一个FIFO文件,多个进程也可以同时读取同一个FIFO文件三、 关键代码部分代码如下:process() int x=StartX,y=StartY,ch; initial(); box(stdscr,0,0); attron(A_REVERSE); mvaddstr(0,20,process); attrof
23、f(A_REVERSE); mvaddstr(6,19,* fork); mvaddstr(8,19,* signal); mvaddstr(10,19,* pipe); mvaddstr(12,19,* popen); mvaddstr(14,19,* fifo); mvaddstr(16,19,* Exit); move(y,x); do ch=getch(); switch(ch) case KEY_UP : if(y=16) y=14; else if(y=14) y=12; else if(y=12) y=10; else if(y=10) y=8; else if(y=8) y=6
24、; else y=y; break; case KEY_DOWN : if(y=6) y=8; else if(y=8) y=10; else if(y=10) y=12; else if(y=12) y=14; else if(y=14) y=16; else y=y; break; case r : if(y=16) endwin();clear();main(); if(y=6) endwin();system(./fork); if(y=8) endwin();system(./signal); if(y=10) endwin();system(./pipe); if(y=12) en
25、dwin();system(./popen); if(y=14) endwin();system(./fifo1); break; case t : if(y=6) y=8; else if(y=8) y=10; else if(y=10) y=12; else if(y=12) y=14; else if(y=14) y=16; else if(y=16) y=6; break; case 27 : endwin();clear();main(); default : break; move(y,x); while(1);四、 实验结果 实验8:线程操作实验 警察小偷的PV操作实验情景描述警
26、察和小偷被手铐绑在一起,需要共同逃亡200m,手铐长度3m。使用基于Ncurse的文本界面显示警察与小偷的动态追逐效果。二、 实验原理使用了进程互斥锁mutex,创建了两个进程void *thief(void *args)和void *police(void *args),利用随机数种子产生1-6的随机数,即警察或小偷的一次最大前进距离为6米,假设其中一人落后3米,即可前进6米三、 关键代码部分代码如下:/*thief go first and police follow*/void *thief(void *args) int rand1; while(1) pthread_mutex_lo
27、ck(&mutex); srand(unsigned)time(NULL);/*随机种子*/ rand1=rand()%6+1; if(my_thief-my_police3) /thief can run if(my_thief+rand1my_police+3) /run my_thief+=rand1; else my_thief=my_police+3; pthread_mutex_unlock(&mutex); sleep(1); void *police(void *args) int rand2; while(1) pthread_mutex_lock(&mutex); sran
28、d(unsigned)time(NULL);/*随机种子*/ rand2=rand()%6+1; if(my_police-my_thief3) /thief can run if(my_police+rand2=50 | my_thief=50) if(my_policemy_thief) mvaddstr(14,16,the first come is the police); else mvaddstr(14,16,the first come is the thief); break; 四、 实验结果 实验9:线程操作实验 哲学家进餐的资源竞争实验情景描述编程实现哲学家进餐模型。所谓的
29、哲学家进餐模型,即有五个哲学家围坐在一圆桌旁,桌中央有一盆面条,每两人之间放一只筷子,即总共五根筷子。每个哲学家的行为是思考或者吃面条。为了吃面条,每个哲学家必须只能直接从自己的左边或右边去取筷子,两根筷子都取到了才能够吃面条。而吃完一根面条之前,哲学家不会放掉手里的筷子。二、 实验原理选用互斥锁mutex,如创建5个, pthread_mutex_t m5;模型抽象:6个哲学家 - 6个线程; 6支筷子 - 6把互斥锁 int left(左手), right(右手)6个哲学家使用相同的逻辑,可通用一个线程主函数,void *tfn(void *arg),使用参数来表示线程编号:intthre
30、ad_num = (int)arg;哲学家线程根据编号知道自己是第几个哲学家,而后选定锁,锁住,吃饭。否则哲学家thinking。 A B C D E F6支筷子,在逻辑上形成环: 0 1 2 3 4 5分别对应6个哲学家振荡:如果每个人都攥着自己左手的锁,尝试去拿右手锁,拿不到则将锁释放。过会儿6个人又同时再攥着左手锁尝试拿右手锁,依然拿不到。如此往复形成另外一种极端死锁的现象振荡避免振荡现象:只需6个人中,任意一个人,拿锁的方向与其他人相逆即可三、 关键代码pthread_mutex_t m6; /定义5把互斥锁void *tfn(void *arg)int thread_num, lef
31、t, right;/thread_num:哲学家编号 left:左手 right:右手 srand(time(NULL);thread_num = (int)arg; /为6位哲学家分配筷子 (抽象6只筷子,形成一个闭环)if (thread_num = 6) /第6位哲学家和其他的哲学家不一样,他是先抢右边的筷子left = 0;/把0(右边的筷子)赋值给左手变量,他就会先抢右边的筷子了right = thread_num;else /分配好筷子,让左手拿左边的筷子,右手拿右边的筷子,这里的哲学家都是先抢左边的筷子,再抢右边的筷子的left = thread_num;right =threa
32、d_num+1;while (1) pthread_mutex_lock(&mleft);/先抢左边的筷子 if (pthread_mutex_trylock(&mright) = 0)/尝试去抢右边的筷子 printf(t%c is eating n, A+thread_num);pthread_mutex_unlock(&mright);/放下右边筷子pthread_mutex_unlock(&mleft);/放下左边筷子sleep(rand() % 6);return NULL;四、 实验结果 实验10:系统调用实验 添加系统调用实验情景描述该实验实现一个简单系统调用的添加,功能只是实现
33、打印一些信息,而且能证明这个进程进入过内核态。系统调用的编号名字:_NR_print_info;内核中系统调用的实现程序的名字:sys_print_info。二、 实验原理1.添加系统调用号:系统调用号在文件include/asm/unistd.h中定义,因此需要在这里添加新系统调用号:_NR_print_info。#define _NR_io_submit248#define _NR_io_cancel249#define _NR_alloc_hugepages250#define _NR_free_hugepages251#define _NR_exit_group252#define _
34、NR_lookup_dcookie253#define _NR_set_tid_address258#define _NR_print_info259/* added */2.添加系统调用号之后,系统才能根据这个号作为索引去找syscall_table中的相应表项。之后在系统调用表中添加相应的表项。系统调用表在arch/i386/kernel/entry.S中定义。系统调用处理程序(system_call)会根据eax中的索引到系统调用表(sys_call_table)中去寻找相应的表项。.long SYMBOL_NAME(sys_ni_syscall).long SYMBOL_NAME(sy
35、s_ni_syscall)/* sys_epoll_wait */.long SYMBOL_NAME(sys_ni_syscall).long SYMBOL_NAME(sys_set_tid_address).long SYMBOL_NAME(sys_print_info)/* added ! */.rept NR_syscalls-(.-sys_call_table)/4.long SYMBOL_NAME(sys_ni_syscall).endr3. 在kernel/sys.c文件中添加如下代码:asmlinkage int sys_print_info(int testflag)print
36、k(KERN_EMERG Its my syscall function!n);return 0;在这段代码中,使用到printk()函数。之前在驱动编写部分也使用过这个函数。printk()用来在终端或到系统日志中打印信息。除了第一个参数表示打印的权限级别外,printk()和printf()在其他参数的使用上基本上是一致的。这段代码实现的系统调用在内核态打印了一条信息并返回0。4. 改动完全之后重新编译内核,并用新内核启动。为了防止新编译的内核覆盖原来的内核,可以按照下面所示改一下内核版本号。VERSION=2PATCHLEVEL=4SUBLEVEL=20EXTRAVERSION=-8 (
37、假设原内核版本是2.4.20-8)5. 编译完成之后可以用新内核启动。6. 之后编写一个测试程序test_print_info.c,用来测试新的系统调用。测试程序如下:7.编译测试程序并运行。如果系统调用成功则会在终端打印syscall succeed!。同时由于内核态中的printk()也会打印:Its my syscall function!。四、 实验结果实验11:终端编写实验 模拟终端shell实验情景描述实现一个模拟shell,实现如下功能1)提示符个人标识 2)内部命令bye用来退出3)内部命令run用来执行一个文件 4)调用/bin下的命令二、 实验原理Shell是指“提供使用者
38、使用界面”的软件。它接收用户命令,然后调用相应的应用程序。同时更高级的Shell又是一种程序设计语言,它能够交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令。Shell是操作系统系统的用户界面,提供了用户与内核进行交互操作的接口,负责接收用户输入的命令并把其送入内核去执行。Shell介于操作系统内核与用户之间,负责解释命令行。因此实际上Shell是一个命令解释器,负责解释输入的命令。三、 关键代码while(1)printf($);scanf(%s,command);fflush(stdin);if(strcmp(command,run)=0) run_(); els
39、e if(strcmp(command,bye)=0) bye_(); else if(strcmp(command,print)=0) print_(); else if(strcmp(command,show)=0) show_(); return 0; void show_() system(ls /root); void bye_() exit(0); void print_() printf(guolinn); void run_() system(./ju); 四、 实验结果 实验12:内存模式实验 实现malloc和free的内存模拟过程一、实验情景描述实现malloc和free
40、的内存模拟过程。在malloc()中使用首次适配方法来进行存储分配。当使用free()释放内存时,会对相邻的存储空间合并。为了实现虚拟存储,可以编写两个存储管理器:一个管理用户程序的虚拟地址空间,另外一个实现malloc()和free()。其中虚拟内存管理器可以为应用程序提供大量的内存,而内核一般会提供malloc()。二、 实验原理malloc()和free()功能相对应:malloc用来动态申请内存空间,即从一大块内存中分配除所需要的空间,而free用来释放malloc申请的空间。分配时要留出一部分malloc()不能分配的内核空间,这部分空间供操作系统内核使用。malloc()从堆里面获
41、得空间,其函数返回的指针是指向堆里面的一块内存。当操作系统收到程序的申请时,就会遍历一个记录空闲内存地址的链表,然后就寻找第一个空间大于所申请空间的堆结点,并将该结点从空闲结点链表中删除,同时将该结点的空间分配给程序。如果malloc()用完内存之后,它将会继续向内核申请内存。三、 关键代码typedef struct _malloc/* Turbo C编译器 */ size_t size;/*4 bytes */ struct _malloc *next;/*4 bytes */unsigned magic : 15;/*4 bytes total */ unsigned used : 1;
42、 malloc_t;/* total12 bytes */static char *g_heap_bot, *g_kbrk, *g_heap_top;void *kmalloc(size_t size) unsigned total_size; malloc_t *m, *n; int delta; if(size = 0) return NULL; total_size = size + sizeof(malloc_t);/42 bytes + 12 blks =54 m = (malloc_t *)g_heap_bot; if(m != NULL) if(m-magic != MALLOC
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中医医学经络腧穴学课件-奇穴
- 《学前社会教育》课件
- 2025届海南省部分学校高三上学期全真模拟(二)历史试卷(解析版)
- 2024-2025学年浙江省台州市十校联考高一上学期期中考试历史试题(解析版)
- 《物流仓储管理》课件
- 单位管理制度集合大全员工管理篇
- 《物流管理运输管理》课件
- 单位管理制度汇编大全员工管理
- 单位管理制度合并汇编【职工管理】
- 单位管理制度分享合集职员管理
- 七年级体育与健康 《足球》单元作业设计
- 毛细管升高法测量液体表面张力系数
- 室内覆盖方案设计与典型场景
- 放射性粒子植入自我评估报告
- 2023年山西云时代技术有限公司招聘笔试题库及答案解析
- 浙大中控DCS系统介绍(简洁版)
- GB/T 16288-2008塑料制品的标志
- GB/T 14486-2008塑料模塑件尺寸公差
- 北京市海淀区2022-2023学年高三期末考试历史试题及答案
- 顶板管理实施细则
- 2022年杭州西湖文化旅游投资集团有限公司招聘笔试试题及答案解析
评论
0/150
提交评论