当前位置:   article > 正文

Linux期末复习部分内容

linux期末复习

注意:此复习资料中没有关于Sheel编程的部分

第一章: Linux操作系统的简述

1. 自由软件

自由软件是一种思想活动,强调用户该如何使用软件的自由。
可以自由的运行,自由的拷贝,自由的更改,自由的更改再发行。
这种指的自由,并不是价格免费,和价格无关,是使用软件对所有用户来说是自由的。

2. Linux 的起源,发展,特点,常见发行版本

2.1 起源发展

简单概括:Linux系统基于Unix系统诞生于1991年,由芬兰大学生林纳斯·托瓦兹(Linus Benedict Torvalds)和后来陆续加入的众多爱好者共同开发完成。

Linux的标志和吉祥物为一只名字叫作Tux的企鹅——Torvalds’Unix

linux的发展:
(1)1984年,Andrew S.Tanenbaum 开发了用于教学的Unix 系统,命名为MINIX.
(2)1989年,AndrewS.Tanenbaum 将MINIX 系统运行于X86 的PC 计算机平台。
(3)1990年,芬兰赫尔辛基大学学生Linus Torvalds 首次接触MINIX系统。
(4)1991年,LimusTorvalds 开始在MINIX上編写各种驱动程序等操作系统内核组件。
(5)1991年底,Limus Torvalds 公开了Linux 内核源码0.02 版
(6) 1993年,Linux 1.0版发行,Linux 转向GPL 版权协议。
(8) 1994 年,Linux 的第一个商业发行版Slackware 问世。
(9) 1996年,美国国家标准技术局的计算机系统实验室确认Linux 版本1.2.13 (由Open Linux 公司打包)符合POSIX 标准
(10)1999 年,Linux 的简体中文发行版问世。
(11) 2000 年后,Linux 系统日趋成熟,涌现木量基于Linux 服务器平台的应用,并广应用于基于ARM技术的嵌入式系统中。

2.2 特点

一般不会让你具体去解释的。

  • 是一款自由软件
  • 具有开放性
  • 可以多用户使用,多任务执行
  • 良好的用户界面
  • 丰富的网络功能
  • 安全稳定
  • 良好的可移植性
  • 设备独立性
  • 支持多文件系统

2.3 常见发行版本

  • Red Hat
    世界上做流行的Linux版本之一,其特点就是人群数量大,资料非常多,如果你有什么不明白的地方,很容易找到人来问,而且网上一般的Linux教程都是以Red Hat 为例进行讲解的。
  • Ubuntu
    当今最受欢迎的免费操作系统。侧重于它在这个市场的应用,在服务器,云计算,甚至一些运行Ubuntu Linux的移动设备上很常见,它使用APT软件管理工具来安装和更新软件。
  • Debian
    运行起来极其稳定,使得它非常适用于服务器。Debian平时维护3套正式的软件库和一套非免费软件库。
    它派生出了许多的Linux发行版,拥有的软件非常的丰富,又多达37500多个软件包。
  • OpenSuse
    免费的,并不供商业用途使用,仅供个人使用.它通过Yast来管理软件包。使用和管理服务器应用程序非常容易
  • Fedora
    是较具有知名度的Linux发行版之一,目标是创建一套新颖的,多功能并且自由的操作系统
  • CentOS
    是一款企业级Linux发行版,它使用红帽企业级Linux中的免费源代码重新构建而成。这种重构版本完全去掉了注册商标,由于出自相同的源码,CentOS的外观和行为几乎和红帽企业级Linux如出一辙。

第二章: Linux的常用命令

1. 常用的系统命令

1.1 date

显示系统当前的日期和时间

1.2 pwd

显示当前工作路径

1.3 cd

用于切换当前工作路径

1.4 cal

显示日历,可显示公元1~9999年中某年某月的日历。

1.5 who

显示当前已经登陆到系统的所有用户名,登录终端以及登陆时间

1.6 wc

用于统计给定文件的行数,字数,字符数
-l 表示统计行数,-w 表示统计单词数,-c表示统计字符数

1.7 uname

显示操作系统当前信息

1.8 clear

清屏操作

1.9 logout

  • 登录时尽量少用root账号登录,因为权限太高了,少用可以避免很多的操作失误
    使用 su 用户 进入其他用户
  • 在提示符下输入logout即可注销当前登录的用户

1.10 shutdown

  • shutdown
    shutdown -h now 立刻进行关机
    shutdown -h 1 一分钟以后关机
    shutdown -r now 立刻重启系统
  • halt 直接使用,关机,作用和上面一样
  • reboot 现在重新启动系统
  • sync 把内存的数据同步到磁盘

注意细节:
不管是重启系统还是关闭系统,尽量要先运行sync命令,把内存的数据写入到磁盘中

2. 输入/输出重定向

  • 输入重定向的符号是 " < " ,执行该命令后,"<"后面的文件替代用户从键盘上输入内容
  • 输出重定向是 “>” 或者 “>>” ,“>“将输出的内容直接写到指定文件,”>>” 重定向附加,将输出内容附加到指定文件后面。

3. 管道

可以把一个命令的输出信息作为另外一个命令的输入信息,
从而将两个或两个以上的命令连接在一起,实现复杂的功能。
管道功能通过管道线 “|” ,实现,管道线 “|” 前面命令的输出信息作为后面命令的输入信息。

4. 文件目录操作命令

4.1 ls

/**用来显示文件列表,基本语法为:ls [选项] [目录或文件名]
	-a : 显示所有文件,包括隐藏文件
	-l : 以长格式显示目录下的内容列表
	-i : 显示文件索引节点号
	-r : 以文件名反序排列并输出目录内容列表
	-t : 用文件和目录的更改时间进行排序
	-m : 用"," 隔开每个文件和目录的名称
	-R/r: 递归显示指定目录下的所有文件及子目录
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.2 cd

用来切换工作目录到指定目录,可以用相对路径或绝对路径表示指定目录

4.3 cat

用来显示文件的内容,还可以利用输入/输出重定向功能建立小型文件或将两个文件连接起来。

4.4 more

用来分屏显示大文件,当显示满一屏后停下来,并且在屏幕的底部出现一个提示信息,

给出以显示文件的百分比。

4.5 head

用于显示文件的开头内容,默认显示前10行

4.6 tail

用于显示文件的尾部内容,默认显示后10行。

4.7 cp

/**
	用来将一个或多个源文件或目录复制到指定的目标目录中。
	-f : 强行复制文件或目录,不论文件或目录是否存在
	-i : 覆盖已有文件之前询问一下客户
	-l : 对源文件建立硬链接,而非复制文件
	-s : 对源文件建立符号链接,而非复制文件
	-R/r : 递归处理,将指定目录下的所有文件与子目录一并处理
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.8 mv

/**
	将文件从一个目录移到另一个目录,或对文件或目录进行重命名。
	-b : 当目标文件存在时,覆盖前,为其创建一个备份
    -f : 直接覆盖现有的文件或者目录
    -i : 覆盖前询问顾客,看是否进行覆盖
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.9 rm

/**
	删除一个目录中的一个或多个文件或者目录,也可以将某个目录及其下的所有文件和子目录均删除
	-f : 强制删除文件或者目录
	-i : 删除前会询问用户是否确认进行删除
	-R/r : 将指定目录下的所有文件与子目录一并处理
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.10 touch

创建新的空文件或者改变已有文件的时间标签(已有文件的数据不变)

4.11 file

识别文件类型,也可以用来辨别一些文件的编码格式。

4.12 find

/**
	在文件系统中查找指定的文件,可以根据文件的名称,大小,建立时间等信息查找文件。
	-name : 指定文件名字符串作为寻找文件的匹配标准,可以使用通配符
	-type : 查找符合指定的文件类型的文件
	-perm : 查找符合指定的权限值的文件或目录
	-links: 查找符合指定的硬链接数目的文件或者目录
	-size : 查找符合指定的文件大小的文件
	......
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

第三章: Vi和Vim编辑器的使用

vi和vim的基本介绍

vi

所有的linux系统都会内建vi文本编辑器

vim

vim具有程序编辑的能力,可以看做是vi的增强版本,可以主动的以字体颜色辨别语法的正确性,方便程序设计。
代码补全、编译及错误跳转等方便编程的功能特别丰富

vi和vim的三种常见模式

正常模式

以vi或者vim打开一个文档就直接进入一般模式了。(默认的模式)

在正常模式下可以使用快捷键

编辑模式

在正常模式下按下i字母后,会进入到编辑模式
该模式下,程序员可以输入内容

命令行模式

在这个模式当中,可以提供你相关指令,完成读取,存盘,替换,离开vi/vim,显示行号的动作

一些快捷键的使用

拷贝(正常模式下使用)

拷贝当前行 yy

拷贝当前行向下的5行 5yy,并粘贴(粘贴的命令是 p)

删除(正常模式下使用)

删除当前行 dd
删除当前行向下的5行 5dd

查找某个单词(命令行下使用)

命令行下 /关键字,回车后就会查找,再输入n就是依次往下查找

设置行号(命令行下使用)

set nu是设置行号,set nonu是取消行号

末行和首行(正常模式下使用)

输入 G 会到达文档的最末行
输入 gg 会到达文档的首行

动作撤销(正常模式下使用)

在正常模式下输入 u 就会撤销上次的动作

第四章: 文件系统与操作

1. 磁盘

磁盘主要是由磁盘盘片,传动手臂,磁头与主轴电动机以及传动轴所组成。
一个磁盘可以有一个或多个盘片,每个盘片上下都会有一个磁头用来读/写数据。

2. 硬盘分区

硬盘分区包括主分区,扩展分区和逻辑分区

3. Linux文件目录结构

3.1 一些重要的目录

  • /bin
    是binary的缩写,这个目录存放着最经常使用的命令
  • /sbin
    Super User,存放的是系统管理员使用的系统管理程序
  • /home
    存放普通用户的目录,在linux中每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的
  • /root
    该目录为系统管理员,也称作超级权限者的用户主目录
  • /lib
    系统开机所需要最基本的动态连接数据库,起作用类似于windows里的DLL文件,几乎所有的应用程序都要用
  • 到这些共享库
  • /lost+found
    这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件
  • /etc
    所有的系管理所需要的配置文件和子目录
  • /usr
    用户的很多应用程序和文件都放在这个目录下,类似于Windows下的program files目录
  • /boot
    存放的是启动linux时使用的一些核心文件,包括一些连接文件以及镜像文件
  • /proc
    这个目录是一个虚拟的目录,他是系统内存的映射,访问这个目录来获取系统信息
  • /src
    service,该目录存放一些服务启动之后需要提取的一些数据
  • /tmp
    存放一些临时文件
  • /dev
    设备管理器,把所有的硬件用文件的形式存储
  • /usr/local
    另一个给主机额外安装软件所安装的目录
  • /var
    存放着不断扩充着的东西,习惯将经常修改的目录放在这个目录下,包括各种日志文件
  • /media
    linux会自动识别一些设备,例如光盘,光驱等等,识别后,linux会把识别的设备挂载到这个目录下
  • /mnt
    让用户临时挂载别的文件系统的,将外部的存储进行挂载,进入到该目录就可以查看里面的内容了

3.2 Linux目录总结

1.linux 的目录中有且仅有一个根目录/
2.linux 的各个目录存放的内容是规划好的,不要乱放文件
3.linux 是以文件的形式管理我们的设备,因此linux系统,一切皆为文件
4.linux 的各个文件目录下存放什么内容,大家必须有一个认识。

3.3 inode

索引节点。每个存储设备或存储设备的分区被格式化为文件系统后有两部分,一部分是block;另外一部分是inode。

文件存储在硬盘上,最小存储单位是"扇区"(Sector),每个扇区能存储512B。操作系统在读取硬盘的时候,不会一个分区一个分区的读取,这样的效率太低,而是一次性连续读多个扇区,及一次性读一个块(Block)。这种块是存取的最小单位,块的大小最常见的是4KB.所以Block是用来存储数据用的。inode就是用来存储这些数据的信息,这些信息包括文件大小,属主,归属的用户组、读/写权限等。

inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快找到对应的文件。

inode包含文件的元信息,具体有以下内容:文件的字节数、文件拥有者的User ID、文件的Group ID、文件的读写权限、文件的时间戳、链接数、文件数据Block的位置。

4. 对文件系统的操作

4.1 fdisk 磁盘分区

创建和维护分区表的程序,兼容了DOS类型的分区表、BSD或者SUN类型的磁盘列表。
fdisk用来创建分区,删除分区,查看分区信息等基本操作。

4.2 mkfs 格式化命令

当硬盘分区完成后就要进行对文件系统的格式化。mkfs本身并不执行建立文件系统的工作,而是去调用相关的程序来执行。

4.3 mount 挂载命令

在Linux操作系统中,需要将某些设备或者是已经建立好的文件系统安装到Linux文件目录树的某个位置上,这个过程叫做挂载,文件系统所挂载的目录就是挂载点
我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的目录上,然后通过访问这个目录来访问存储设备。

5. 常见的操作函数

文件描述符

文件描述符说明
0STDIN_FILENO标准输入
1STDOUT_FILENO标准输出
2STDERR_FILENO标准错误输出

5.1 open

功能:用于打开或创建文件,在打开文件或创建文件时可以指定文件的属性以及用户的权限等参数
返回值:成功的话返回文件描述符;出错的话返回-1
open函数建立了一条到文件或设备的访问路径。

#include<fcnt1.h>
int open(const char *path,int flags,mode t_mode);
/**
	flags 用于描述文件打开方式,常数定义在头文件中
*/
  • 1
  • 2
  • 3
  • 4
  • 5

5.2 creat

功能:用于创建一个文件。
返回值:若成功返回以只写方式打开的文件描述符;若出错,则为-1.

# include<unistd.h>
int creat(const char *path,modet t_mode);
  • 1
  • 2

5.3 close

功能:用于关闭一个被打开的文件
返回值:若成功返回0;出错返回-1.

# include<unistd.h>
//fd是需要关闭文件的文件描述符
int close(int fd);
  • 1
  • 2
  • 3

当一个进程终止时,它所有的打开文件都由内核自动关闭。

5.4 rename

功能:用于修改文件名称
返回值:若成功返回0;若出错返回-1.

#include<stdio.h>
//参数oldpath是文件的原路径;newpath是文件的新路径
int rename(const char *oldpath,const char *newpath);
  • 1
  • 2
  • 3

5.5 remove

功能:删除文件
返回值:若成功返回0;出错返回-1

#include<stdio.h>
//参数pathname是文件的路径
int remove(const char*pathname);
  • 1
  • 2
  • 3

5.6 chmod

功能:修改文件的访问权限
返回值:成功返回0;出错返回-1

#include<sys/types.h>
#include<sys/stat.h>
int chmod(const char *path,mod_t mod);
  • 1
  • 2
  • 3

5.7 chown

功能:修改文件所有者
返回值:成功返回0;出错返回-1

#include<sys/types.h>
#include<sys/stat.h>
//参数path是文件的路径;owner是指定文件的所有者;group是指定文件的组
int chown(const char *path,uid_t owner,gid_t group);
  • 1
  • 2
  • 3
  • 4

5.8 lessk

功能:用于在指定的文件描述符中将文件指针定位到相应的位置

#include<unistd.h>
off_t lessk(int file_des,off_t offset,int whence);
  • 1
  • 2

成功时返回新的文件偏移量,失败时返回-1.利用返回值可以测试文件描述符是否能够设置读写偏移量。当读写偏移量大于文件长度时,写操作将会在文件中构成一个空洞,空洞读为0,但是空洞并不占用磁盘空间,其处理方式与文件系统的实现有关。

5.9 write

功能:向文件写入数据
返回值:成功返回自己写的字节数;出错返回-1.

#include<unistd.h>
/**
	file_des:文件描述符,标识要读取的文件。为0,从标准输入中读取数据
	*buf:缓冲区,用来存储读入的数据
	nbytes:要读取的字符数
*/
ssize_t write(int file_des,const void *buf,size_t nbytes);    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.10 read

功能:从文件中读取数据
返回值:成功返回读取到的字节数,若已回到文件结尾返回0;出错返回-1.

#include<unistd.h>
/**
	file_des:文件描述符,标识要写入的文件。为1,就向标准输出写数据;为2,向标准错误写数据
	*buf:待写入的文件,是一个字符串指针
	nbytes:要写入的字符数
*/
ssize_t write(int file_des, void *buf,size_t nbytes);    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.11 dup和dup2

都用于复制一个现有的文件描述符,成功返回新的文件描述符,出错返回-1。区别主要有两点

  • dup一定返回当前可用的最小文件描述符;
  • dup2指定参数file_des2为新的文件描述符,当file_des2打开时,先将其关闭,当file_des和file_des2相等时,返回file_des2而不关闭它。
    dup
#include<unistd.h>
int dup(int file_des);    
  • 1
  • 2

dup允许用户复制一个file_des文件描述符。存入一个已经存在的文件描述符,它就会返回一个与该描述符相同的新的文件描述符

dup2

#include<unistd.h>
int dup2(int file_des,int file_des2);
  • 1
  • 2

dup2函数如果调用成功,file_des2将变成file_des的复制品,两个文件描述符都指向同一个文件,并且file_des指向的文件,返回的是新的文件描述符,出错都返回-1.

第五章: 进程管理

1. 进程的概念

进程可以理解为程序执行的实例,它包括可执行程序以及其他相关的系统资源,如打开的文件,挂起的信号,内核内部数据,处理器状态,内存地址空间以及包含全局变量的数据段等。

在Linux操作系统中,进程是操作系统调度的基本单位,创建进程的目的就是可以使多个程序并发的执行,从而提高系统的资源利用率和吞吐量

从狭义上来说,进程是指正在运行的程序的实例;从广义上来说,进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。

1.1 进程与程序的区别

  • 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
  • 程序可以作为一种软件资料长期存在,是永久的;进程具有生命周期,是动态的和暂时的
  • 程序不能申请系统资源,而且不能被系统调度,也不能作为独立的运行单位,因此他不占用系统的运行资源。作为资源分配和独立运行的基本单元都是进程
  • 进程和程序不是一一对应的:一个程序执行后可产生多个进程;一个进程执行过程中可以执行一个或多个程序

2. 进程的分类

2.1 按照模式运行分类

2.1.1 内核态进程

这类进程运行在内核模式下,执行一些内核指令(Ring 0).

2.1.2 用户态进程

工作在用户模式下,执行用户指令(Ring 3).

2.2 按照特点来分类

2.2.1 交互进程

由shell终端启动的进程,它既可以在前台运行,也可以在后台运行。运行中需要与用户进行交互操作

2.2.2 批处理进程

是一个进程集合,负责按照顺序启动其他的进程。

2.2.3 守护进程

一种开机后一直运行的进程。
他是实现特定功能或者执行系统相关任务的后台进程。

2.3 按照状态分类

2.3.1 守护进程

守护进程的父进程都是init进程;守护进程没有控制终端。

2.3.2 孤儿进程

一个父进程退出后,它的一个或多个子进程还在运行,那么这些子进程都被称为孤儿进程。
孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。

2.3.3 僵尸进程

一个子进程结束但是没有完全释放内存,该进程称为僵尸进程。
僵尸进程的父进程结束后该僵尸进程就会被init进程所收养,最终会被回收。
僵尸进程会导致资源的浪费,而孤儿进程不会。

2.3.4 僵尸进程的危害
  1. 进程的退出状态必须被维持下去,因为他要关心他的进程(父进程),父进程如果一直不读取,那子进程一直处于Z状态

  2. 维护退出状态本身就是要用数据维护,也属于进程的基本信息,所以保存在task_struct中,换句话说,Z状态一直不退出,PCB就要一直维护

  3. 父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存。

  4. 内存泄露

2.3.5 如何避免僵尸进程?
  1. 通过signa(SIGCHLD, SIG_1GN)通知内核对子进程的结束不关心,由内核回改。如果不想让父进程挂起,可以在父进程中加入一条语句t: sig a(SICCHLD,SIG IGN)表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

  2. 父进程调用iwait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。

  3. 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。

  4. 通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进棋其巧进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。
    第一种方法忽略SICHLD信号,这常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

3. 进程控制函数

3.1 fork

由任何一个用户创建一个子进程,创建成功后,子进程存在于系统之中,并且独立于父进程。

#include<unistd.h>
pid_t fork(void);
  • 1
  • 2
  • 在父进程中,fork函数会返回新创建子进程的进程ID
  • 在子进程中fork的返回值为0
  • 如果出现错误,fork函数返回一个负值

3.2 vfork

也是创建一个子进程,但是与fork也有些许的不同
对fork函数来说,父子进程的执行次序不确定,而且子进程复制父进程的处理数据段。
对vfork来说,vfork函数会保证子进程先运行,在它调用exec函数或者exit函数之后父进程才能被调度运行,子进程在调用exec函数或者exit函数之前与父进程数据是共享的。

#include<sys/types.h>
#include<unistd.h>
pid_t vfork(void);
  • 1
  • 2
  • 3

调用成功时,子进程返回0,父进程返回子进程ID

3.3 system

会调用fork函数产生的子进程,由子进程调用/bin/sh -c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的过程。

#include<stdlib.h>
int system(const *command);    
  • 1
  • 2

system函数在调用/bin/sh时失败返回127,其他失败原因返回-1.若参数string为null,则返回非零值。
如果system函数在调用成功则最后返回执行shell命令后的返回值,但是此返回值也可能是调用/bin/sh失败后返回的127,因此最好检查errno来确认是否成功。

3.4 execve

可以取代调用进程的内容,子进程可以调用execve函数执行另外一个程序。当调用execve函数时,进程执行的程序完全被替换成新程序,但是其进程ID并不会变,只是用新程序替换了当前进程的正文,数据,堆和栈。

#include<unistd.h>
int execve(const chat* filename,const char* argv[],const char * envp[]);
  • 1
  • 2

函数的第一个参数filename是可执行文件名称;
第二个参数argv是需要传递给可执行程序的参数;
第三个参数是环境变量数组。
调用成功则加载新的程序从启动代码开始执行,不在返回;如果出错则返回-1.

3.5 getpid

获取当前进程的进程号,返回值就是当前进程的进程号

#include<unistd.h>
pid_t getpid(void);
  • 1
  • 2

3.6 getppid

获取当前进程父进程的进程号,返回值就是当前进程父进程的进程号

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/837546
推荐阅读
相关标签
  

闽ICP备14008679号