c语言库函数系统调用 c语言库函数的使用方法

如何在Linux内核里增加一个系统调用?一、Linux0.11下添加系统调用:\x0d\x0a\x0d\x0a我在bochs2.2.1中对linux0.11内核添加了一个新的系统调用,步骤如下: \x0d\x0a1./usr/src/linux/include/unistd.h中添加:#define __NR_mytest 87 \x0d\x0a然后在下面声明函数原型:int mytest(); \x0d\x0a2./usr/src/linux/include/linux/sys.h中添加:extern int sys_mytest(); \x0d\x0a然后在sys_call_table中最后加上sys_mytest; \x0d\x0a3.在/usr/src/linux/kernel/sys.c中添加函数实现如下: \x0d\x0aint sys_mytest(){ \x0d\x0aprintk("This is a test!"); \x0d\x0areturn 123; \x0d\x0a} \x0d\x0a4.在/usr/src/linux/kernel/system_call.s中对系统调用号加1(原来是86改成了87) \x0d\x0a5.然后到/usr/src/linux目录下编译内核make clean; make Image \x0d\x0a6. cp /usr/src/linux/include/unistd.h /usr/include/unistd.h \x0d\x0a7. reset bochs \x0d\x0a8. 在/usr/root中生成test.c文件如下: \x0d\x0a#define __LIBRARY__ \x0d\x0a#include\x0d\x0a_syscall0(int,mytest) \x0d\x0aint main(){ \x0d\x0aint a; \x0d\x0aa = mytest(); \x0d\x0aprintf("%d", a); \x0d\x0areturn 0; \x0d\x0a} \x0d\x0a9.然后gcc test.c编译之后运行a.out , 前面所有步骤都通过,但是每次调用都是返回-1,然后我查过errno为1(表示操作不允许),就不知道为什么了? \x0d\x0a系统知道的高手们能够告知一下,不胜感激!这个问题困扰我很久了! \x0d\x0a\x0d\x0a二、新Linux内核添加系统调用\x0d\x0a\x0d\x0a如何在Linux系统中添加新的系统调用\x0d\x0a系统调用是应用程序和操作系统内核之间的功能接口 。其主要目的是使得用户可以使用操作系统提供的有关设备管理、输入/输入系统、文件系统和进程控制、通信以及存储管理等方面的功能,而不必了解系统程序的内部结构和有关硬件细节,从而起到减轻用户负担和保护系统以及提高资源利用率的作用 。\x0d\x0a\x0d\x0aLinux操作系统作为自由软件的代表 , 它优良的性能使得它的应用日益广泛,不仅得到专业人士的肯定,而且商业化的应用也是如火如荼 。在Linux中 , 大部分的系统调用包含在Linux的libc库中,通过标准的C函数调用方法可以调用这些系统调用 。那么,对Linux的发烧友来说,如何在Linux中增加新的系统调用呢? \x0d\x0a1 Linux系统调用机制\x0d\x0a\x0d\x0a在Linux系统中,系统调用是作为一种异常类型实现的 。它将执行相应的机器代码指令来产生异常信号 。产生中断或异常的重要效果是系统自动将用户态切换为核心态来对它进行处理 。这就是说,执行系统调用异常指令时 , 自动地将系统切换为核心态,并安排异常处理程序的执行 。Linux用来实现系统调用异常的实际指令是:\x0d\x0a\x0d\x0aInt $0x80\x0d\x0a\x0d\x0a这一指令使用中断/异常向量号128(即16进制的80)将控制权转移给内核 。为达到在使用系统调用时不必用机器指令编程,在标准的C语言库中为每一系统调用提供了一段短的子程序,完成机器代码的编程工作 。事实上 , 机器代码段非常简短 。它所要做的工作只是将送给系统调用的参数加载到CPU寄存器中,接着执行int $0x80指令 。然后运行系统调用,系统调用的返回值将送入CPU的一个寄存器中,标准的库子程序取得这一返回值,并将它送回用户程序 。\x0d\x0a\x0d\x0a为使系统调用的执行成为一项简单的任务,Linux提供了一组预处理宏指令 。它们可以用在程序中 。这些宏指令取一定的参数,然后扩展为调用指定的系统调用的函数 。\x0d\x0a\x0d\x0a这些宏指令具有类似下面的名称格式:\x0d\x0a\x0d\x0a_syscallN(parameters)\x0d\x0a\x0d\x0a其中N是系统调用所需的参数数目,而parameters则用一组参数代替 。这些参数使宏指令完成适合于特定的系统调用的扩展 。例如,为了建立调用setuid()系统调用的函数,应该使用:\x0d\x0a\x0d\x0a_syscall1( int , setuid,uid_t, uid )\x0d\x0a\x0d\x0asyscallN( )宏指令的第1个参数int说明产生的函数的返回值的类型是整型,第2个参数setuid说明产生的函数的名称 。后面是系统调用所需要的每个参数 。这一宏指令后面还有两个参数uid_t和uid分别用来指定参数的类型和名称 。\x0d\x0a\x0d\x0a另外,用作系统调用的参数的数据类型有一个限制,它们的容量不能超过四个字节 。这是因为执行int $0x80指令进行系统调用时,所有的参数值都存在32位的CPU寄存器中 。使用CPU寄存器传递参数带来的另一个限制是可以传送给系统调用的参数的数目 。这个限制是最多可以传递5个参数 。所以Linux一共定义了6个不同的_syscallN()宏指令,从_syscall0()、_syscall1()直到_syscall5() 。\x0d\x0a\x0d\x0a一旦_syscallN()宏指令用特定系统调用的相应参数进行了扩展,得到的结果是一个与系统调用同名的函数,它可以在用户程序中执行这一系统调用 。\x0d\x0a2 添加新的系统调用 \x0d\x0a如果用户在Linux中添加新的系统调用 , 应该遵循几个步骤才能添加成功,下面几个步骤详细说明了添加系统调用的相关内容 。\x0d\x0a\x0d\x0a(1) 添加源代码\x0d\x0a\x0d\x0a第一个任务是编写加到内核中的源程序,即将要加到一个内核文件中去的一个函数,该函数的名称应该是新的系统调用名称前面加上sys_标志 。假设新加的系统调用为mycall(int number),在/usr/src/linux/kernel/sys.c文件中添加源代码,如下所示:\x0d\x0aasmlinkage int sys_mycall(int number) \x0d\x0a{ \x0d\x0areturn number; \x0d\x0a}\x0d\x0a作为一个最简单的例子 , 我们新加的系统调用仅仅返回一个整型值 。\x0d\x0a\x0d\x0a(2) 连接新的系统调用\x0d\x0a\x0d\x0a添加新的系统调用后,下一个任务是使Linux内核的其余部分知道该程序的存在 。为了从已有的内核程序中增加到新的函数的连接,需要编辑两个文件 。\x0d\x0a\x0d\x0a在我们所用的Linux内核版本(RedHat 6.0,内核为2.2.5-15)中,第一个要修改的文件是:\x0d\x0a\x0d\x0a/usr/src/linux/include/asm-i386/unistd.h\x0d\x0a\x0d\x0a该文件中包含了系统调用清单 , 用来给每个系统调用分配一个唯一的号码 。文件中每一行的格式如下:\x0d\x0a\x0d\x0a#define __NR_name NNN\x0d\x0a\x0d\x0a其中,name用系统调用名称代替,而NNN则是该系统调用对应的号码 。应该将新的系统调用名称加到清单的最后,并给它分配号码序列中下一个可用的系统调用号 。我们的系统调用如下:\x0d\x0a\x0d\x0a#define __NR_mycall 191\x0d\x0a\x0d\x0a系统调用号为191 , 之所以系统调用号是191,是因为Linux-2.2内核自身的系统调用号码已经用到190 。\x0d\x0a\x0d\x0a第二个要修改的文件是:\x0d\x0a\x0d\x0a/usr/src/linux/arch/i386/kernel/entry.S\x0d\x0a\x0d\x0a该文件中有类似如下的清单:\x0d\x0a.long SYMBOL_NAME()\x0d\x0a\x0d\x0a该清单用来对sys_call_table[]数组进行初始化 。该数组包含指向内核中每个系统调用的指针 。这样就在数组中增加了新的内核函数的指针 。我们在清单最后添加一行:\x0d\x0a.long SYMBOL_NAME(sys_mycall)\x0d\x0a\x0d\x0a(3) 重建新的Linux内核\x0d\x0a\x0d\x0a为使新的系统调用生效 , 需要重建Linux的内核 。这需要以超级用户身份登录 。\x0d\x0a#pwd \x0d\x0a/usr/src/linux \x0d\x0a#\x0d\x0a\x0d\x0a超级用户在当前工作目录(/usr/src/linux)下,才可以重建内核 。\x0d\x0a\x0d\x0a#make config \x0d\x0a#make dep \x0d\x0a#make clearn \x0d\x0a#make bzImage\x0d\x0a\x0d\x0a编译完毕后,系统生成一可用于安装的、压缩的内核映象文件:\x0d\x0a\x0d\x0a/usr/src/linux/arch/i386/boot/bzImage \x0d\x0a(4) 用新的内核启动系统 \x0d\x0a要使用新的系统调用 , 需要用重建的新内核重新引导系统 。为此,需要修改/etc/lilo.conf文件,在我们的系统中,该文件内容如下:\x0d\x0a\x0d\x0aboot=/dev/hda \x0d\x0amap=/boot/map \x0d\x0ainstall=/boot/boot.b \x0d\x0aprompt \x0d\x0atimeout=50 \x0d\x0a\x0d\x0aimage=/boot/vmlinuz-2.2.5-15 \x0d\x0alabel=linux \x0d\x0aroot=/dev/hdb1 \x0d\x0aread-only \x0d\x0a\x0d\x0aother=/dev/hda1 \x0d\x0alabel=dos \x0d\x0atable=/dev/had\x0d\x0a\x0d\x0a首先编辑该文件,添加新的引导内核:\x0d\x0aimage=/boot/bzImage-new \x0d\x0alabel=linux-new \x0d\x0aroot=/dev/hdb1 \x0d\x0aread-only\x0d\x0a\x0d\x0a添加完毕 , 该文件内容如下所示:\x0d\x0aboot=/dev/hda \x0d\x0amap=/boot/map \x0d\x0ainstall=/boot/boot.b \x0d\x0aprompt \x0d\x0atimeout=50 \x0d\x0a\x0d\x0aimage=/boot/bzImage-new \x0d\x0alabel=linux-new \x0d\x0aroot=/dev/hdb1 \x0d\x0aread-only \x0d\x0a\x0d\x0aimage=/boot/vmlinuz-2.2.5-15 \x0d\x0alabel=linux \x0d\x0aroot=/dev/hdb1 \x0d\x0aread-only \x0d\x0a\x0d\x0aother=/dev/hda1 \x0d\x0alabel=dos \x0d\x0atable=/dev/hda\x0d\x0a\x0d\x0a这样 , 新的内核映象bzImage-new成为缺省的引导内核 。为了使用新的lilo.conf配置文件,还应执行下面的命令:\x0d\x0a#cp /usr/src/linux/arch/i386/boot/zImage /boot/bzImage-new\x0d\x0a\x0d\x0a其次配置lilo:\x0d\x0a\x0d\x0a# /sbin/lilo\x0d\x0a\x0d\x0a现在,当重新引导系统时 , 在boot:提示符后面有三种选择:linux-new 、linux、dos , 新内核成为缺省的引导内核 。\x0d\x0a至此,新的Linux内核已经建立,新添加的系统调用已成为操作系统的一部分,重新启动Linux,用户就可以在应用程序中使用该系统调用了 。\x0d\x0a\x0d\x0a(5)使用新的系统调用\x0d\x0a\x0d\x0a在应用程序中使用新添加的系统调用mycall 。同样为实验目的,我们写了一个简单的例子xtdy.c 。\x0d\x0a\x0d\x0a/* xtdy.c */ \x0d\x0a#include \x0d\x0a_syscall1(int,mycall,int,ret) \x0d\x0amain() \x0d\x0a{ \x0d\x0aprintf("%d \n",mycall(100)); \x0d\x0a}\x0d\x0a编译该程序:\x0d\x0a# cc -o xtdy xtdy.c\x0d\x0a执行:\x0d\x0a# xtdy\x0d\x0a结果:\x0d\x0a# 100\x0d\x0a注意,由于使用了系统调用,编译和执行程序时,用户都应该是超级用户身份 。
什么是系统调用系统调用时由操作系统实现提供的所有系统调用所构成的集合即程序接口或应用编程接口(Application Programming Interface,API) 。是应用程序同系统之间的接口 。
操作系统的主要功能是为管理硬件资源和为应用程序开发人员提供良好的环境来使应用程序具有更好的兼容性,为了达到这个目的,内核提供一系列具备预定功能的多内核函数,通过一组称为系统调用(system call)的接口呈现给用户 。
系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序 。
扩展资料
对于一般通用的OS而言 , 系统调用分为三大类:
1、进程控制类系统调用
主要用于对进程控制的系统调用有:
(1)创建和终止进程的系统调用 。
(2)获得和设置进程属性的系统调用 。进程的属性包括有进程标识符,进程优先级,最大允许执行时间等 。
(3)等待某事件出现的系统调用 。
2、文件操纵类系统调用
(1)创建和删除文件
(2)打开和关闭文件的系统调用
(3)读和写文件的系统调用
3、进程通信类系统调用
在单机处理系统中,OS经常采用消息传递方式和共享存储区方式 。
当采用消息传递方式时,通信前需先打开一个连接 。为此,应由源进程发出一条打开连接的系统调用 , 而目标进程则应利用接受连接的系统调用表示同意进行通信;
然后,在源和目标进程之间便开始通信 。可以利用发送消息的系统调用或者用接收消息的系统调用来交换信息 。通信结束后,还须再利用关闭连接的系统调用结束通信 。
用户在利用共享存储区进行通信之前,须先利用建立共享存储区的系统调用来建立一个共享存储区,再利用建立连接的系统调用将该共享存储区连接到进程自身的虚地址空间上,然后便可以利用读和写共享存储区的系统调用实现相互通信 。
参考资料来源:百度百科-系统调用
Linux下C语言程序中系统调用和库函数调用可以共存?系统调用c语言库函数系统调用,一般有两种,一种是API,即application program interface即应用程序接口 , c语言库函数系统调用我们所说c语言库函数系统调用的库函数 实际上就是一种API函数,所以,这种情况下,它们是一种东西,所以可以共存,另外一种系统调用,是内核级的,在linux上可以直接通过内核调用指令,或者在C语言代码中通过 asm关键字内联调用 。。这时候,这还是系统调用 。。只不过是更低层的操作系统调用 。。
用户程序如何使用系统调用首先,要知道:操作系统与用户间的接口有两种 , 一种是命令接口,另一种是程序接口 。什么是命令接口:为了便于用户直接或者间接地控制自己的作业 , 操作系统向用户提供了命令接口 , 用户可以通过该接口向作业发出命令以控制作业的运行 。该接口又进一步分为联机用户接口和脱机用户接口 。(参考资料:《计算机操作系统》 第三版 汤小丹编著 西安电子科技大学出版社 23页)什么是程序接口:程序接口是提供给程序员在编程时使用的接口,是用户的程序取得操作系统服务的唯一途径 。可以说,程序接口是为用户程序在执行中访问系统资源而设置的 。程序接口由一组系统调用命令(简称系统调用)组成 。用户通过在程序中使用这些系统调用命令来请求操作系统提供服务 。系统调用详解:系统调用就是用户在程序中调用操作系统所提供的一些子功能 , 每一个系统调用都是一个能完成特定功能的子程序 。具体讲,系统调用就是通过系统调用命令中断现行的用户程序,而转去执行相应的子程序,以完成特定的系统功能;系统调用完成后,控制又返回到系统调用命令的下条指令,被中断的程序将继续执行下去 。需要注意的是:系统调用的执行是在管态下运行的 。Windows系统调用:Windows操作系统提供了丰富的系统调用,这些系统调用又被进一步编写成不同的库函数后放入动态链接库DLL(DLL是动态链接库的英文缩写,全称是是Dynamic Link Library)中,这些库函数构成了Windows操作系统提供给程序员的编程界面,这个编程界面被称为应用编程接口API 。库函数:百科上的解释是:库函数顾名思义是把函数放到库里..是别人把一些常用到的函数编完放到一个文件里,供别人用 。别人用的时候把它所在的文件名用#include加到里面就可以了 。库分动态链接库和静态链接库 。这些库是通过编译连接生成的(在编译软件里 , 可以设置文件生成库文件还是普通的EXE文件) 。其实库函数就好比是在word文件里写了几篇作文,然后保存成一个word文档,那么这个word文档就相当于一个库 , 里面的每一篇作文都是一个库函数 。动态链接库的英文缩写是DLL,Windows中,动态链接库一般被存放在C:\Windows\System目录下,DLL多数情况下是带有DLL扩展名的文件,但也可能是EXE或其他扩展名 。所以说:API函数可以看做是一些函数,在windows操作系统下,这些函数通常存放在动态链接库中 , 其头文件通常包含在windows.h中(windows.h是VC 或者VS中带的) 。这些存放在动态链接库里的API函数是操作系统与用户程序之间的唯一接口,用户程序只能通过这些库函数(API函数)请求操作系统服务,即用户程序只能通过这些存放在动态链接库里的API函数来实现系统调用 。当用户程序中使用了这些API函数时,通常会发生系统调用,操作系统进入管态下运行,系统调用结束后,再返回到用户程序继续往下执行 。其实,C语言中最常用的printf和scanf函数,最后都是通过系统调用来完成的不止这两个函数 , 其实C语言的标准库函数,很多都是通过系统调用实现的 。而要使用系统调用,windows下必然要使用存放在动态链接库里的API函数 。当然,我们在程序中也可以不采用系统调用,因为没人规定你必须使用系统调用(当然,有时候采用系统调用会简单的多) 。比如完成一个简单的加法运算程序,又比如单片机C语言编程,都没有系统调用 。许多应用软件,安装后,在其目录文件夹里通常会有一些DLL文件,这些DLL就是给该软件来调用的 。网上对API的解释如下:API是系统的基石 , 是Windows的一砖一瓦 。明确一个概念,软件是运行在系统平台的支持上的 , 软件的功能其实就是向系统伸请,并由系统完成这些功能的过程 。那么软件要做的事情如何传递给系统呢,也就是这些API的作用了,系统定义了这些API函数,做为支持软件执行系统功能的接口 。不同的操作系统自然API会是不同的.mfc就是对api的封装啊.就是很好的例子,候杰的深入浅出mfc正是讲这些原理的.
opentext和closetexteco前言
一、open与fopen、close与fclose使用区别
二、read与fread、write与fwrite使用区别
三、lseek与fseek使用
四、creat、fgetc、fputc、feof使用
前言
这是在学习Linux文件系统编程时记录的学习心得、
从来源的角度看,open等函数和fopen等函数有所不同:
open是LUNIX系统调用函数,返回文件描述符(File Descriptor),它是文件在文件描述符表里的索引 。
【c语言库函数系统调用 c语言库函数的使用方法】fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api 。返回的是一个指向文件结构的指针 。
从移植角度:fopen是C标准函数,有良好的移植性;而open是LUNIX系统调用,移植性有限 。如windows下相似的功能使用API函数`CreateFile` 。
从适用范围:open返回文件描述符,LUNIX下的一切设备都是以文件的形式操作 。如网络套接字、硬件设备等 。当然包括操作普通正规文件(Regular File) 。fopen是用来操纵普通正规文件(Regular File)
从缓冲角度:open无缓冲 , fopen有缓冲 。缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问 。缓冲就是先对缓冲区操作,然后再对文件操作 。比如执行写文件的操作时 , 先将数据写入内存“缓冲区”,待内存“缓冲区”装满再写入文件 。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写 , 是系统级的输入输出
一、open与fopen、close与fclose使用区别
open和close搭配使用,fopen和fclose搭配使用
int open(const char *pathname, int flags);
FILE *fopen(const char *path, const char *mode);
open函数返回文件描述符号,是文件的索引,fopen返回文件的指针,通过文件指针来访问
int close(int fd);
int fclose(FILE *fp);
二者使用方式大同小异
二、read与fread、write与fwrite使用区别
ssize_t read(int fd, void *buf, size_t count);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
ssize_t write(int fd, const void *buf, size_t count);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
ptr就是缓冲区,fwrite从缓冲区数据写入文件,fread从文件读到缓冲区
read、write返回值是读写的字节大小 , fread、fwrite返回值取决于第三个参数:读写次数
三、lseek与fseek使用
off_t lseek(int fd, off_t offset, int whence);
int fseek(FILE *stream, long offset, int whence);
二者使用相同,int whence有三种位置:
SEEK_SET光标指向头部,SEEK_CUR光标指向当前位置,SEEK_END光标指向尾部
offset相对于位置的偏移量,偏移量负数往前移 , 正数往后移
lseek(fd,0,SEEK_END)利用它的返回值,计算文件大小
四、creat、fgetc、fputc、feof使用
int creat(const char*pathname,mode_t mode)
绝对路径模式:宏表示数字
S_IRUSR4可读
S_IWUSR2可写
S_IXUSR1可执行
S_IRWXU7可读、写、执行
int fputc(int c, FILE *stream);
写入一个字符到fp文件流中,写多个字符可采用循环一个一个写
int fgetc(FILE *stream);
从文件中得到一个字符,可以循环一个一个得到字符,配合feof
int feof(FILE *stream);
判断是否到达文件流末端,未到达末端返回值是0 , 到达末端返回值是非0
do
{
c = fgetc(fp);
if( feof(fp) )
{
break ;
}
printf("%c", c);
}while(1);
复制
参考文章:
linux
c语言
Linux程序设计:文件I/O系统调用、open函数、creat函数
258阅读·0评论·2点赞
2020年9月3日
文件的顺序读写和随机读写,fgetc、fgets、fputc、fputs、fscanf、fprintf、fread、fwrite等等函数的介绍
367阅读·0评论·2点赞
2022年7月22日
Linux——操作文件的系统调用(open、write、read、close)
245阅读·0评论·0点赞
2022年10月15日
Linux应用编程之fopen()、fclose()、fread()、fwrite()函数
2882阅读·0评论·2点赞
2022年4月22日
Linux文件系统【02】creat,open,close,read,write,lseek;ioctl,fcntl,dup , dup2,自定义创建文件命令
542阅读·0评论·0点赞
2022年3月21日
Linux的系统调用open,write,read,close,及相关总结
880阅读·0评论·0点赞
2018年4月20日
linux 系统调用open和close介绍以及作用和调用关系的理解
451阅读·0评论·0点赞
2022年10月11日
fopen() 与 CreateFile() 操作文件
1209阅读·0评论·0点赞
2011年11月14日
文件描述符,Linux操作系统文件的系统调用 open read write close
734阅读·0评论·0点赞
2022年5月17日
【Linux】文件操作(open/read/write/close)、系统调用与库函数的区别
4273阅读·0评论·5点赞
2019年5月19日
Linux 1.文件编程(open、creat、write、read、lseek)
412阅读·0评论·0点赞
2021年8月16日
Linux文件操作函数open、read、 write、lseek、ftruncate(linuxC编程篇)
1003阅读·1评论·2点赞
2021年5月17日
Centos安装python遇到ln: failed to create symbolic link ‘/usr/bin/python’: File exists
2365阅读·0评论·2点赞
2020年9月9日
【Linux】文件操作函数 fopen、fwrite、fread、fclose、fseek 与 open、write、read、close、lseek
482阅读·0评论·0点赞
2021年8月10日
open、fopen与freopen
1317阅读·0评论·0点赞
2015年11月18日
linux下 c语言 用write open二进制写文件,fread与read的区别---open和fopen的区别--fread函数和fwrite函数...
174阅读·0评论·0点赞
2021年5月17日
LinuxC open() umask close() read() write() lseek() fcntl() ioctl() mmap() ext2 stat() dup() dup2()
49阅读·0评论·0点赞
2021年8月18日
Linux文件系统(四)—— openreadwritepoll
2007阅读·0评论·0点赞
2021年12月1日
python3 文件操作(列出、创建、删除、复制) 文件打开模式
1037阅读·2评论·0点赞
2020年11月6日
去首页
看看更多热门内容
关于c语言库函数系统调用和c语言库函数的使用方法的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读