热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux入职基础2.8_文件系统与VFS简介

文件系统与VFS简介一、概念理解文件系统(亦称文件管理系统),是文件存储在磁盘等存储设备上的组织方法和数据结构。具体地说,

文件系统与VFS简介

一、概念理解

文件系统(亦称文件管理系统),是文件存储在磁盘等存储设备上的组织方法数据结构。具体地说,它对存储设备的空间进行组织和分配,负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。

每个计算机公司的操作系统在实现这些功能时的均有各自的方法,故大家常说,DOS、Windows、OS/2、Macintosh和UNIX-based操作系统都自有支持的文件系统。举例来说,windows 98 以前的微软操作系统主要的文件系统是 FAT (或 FAT16),windows 2000 以后的版本有NTFS 文件系统,Linux 的文件系统则有 Ext等。

当系统加载一个文件到内存后,如果该文件没有被更动过,则在内存区段的文件数据会被配置为干净(clean)的。但如果内存中的文件数据被更改过了(例如你用 nano 去编辑过这个文件),此时该内存中的数据会被配置为脏的 (Dirty)。此时所有的动作都还在内存中运行,并没有写入到磁盘中!系统会不定时的将内存中配置为『Dirty』的数据写回磁盘,以保持磁盘与内存数据的一致性。

二、Linux文件系统规划

文件系统通常会将这两部份的数据分别存放在不同的区块,权限与属性放置到 inode 中,至于实际数据则放置到 data block 区块中。另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息。

①superblock

Superblock 是记录整个 filesystem 相关信息的地方,他记录的信息主要有:

block 与 inode 的总量;

未使用与已使用的inode / block 数量;

block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128 bytes);

filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;

一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。

②inode

记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block 号码;

③block:

实际记录文件的内容,若文件太大时,会占用多个 block 。文件系统一开始就将 inode 与 block 规划好了,除非重新格式化(或者利用 resize2fs 等命令变更文件系统大小),否则 inode 与 block 固定后就不再变动。

④data block (数据区块)

data block 是用来放置文件内容数据地方,在 Ext2 文件系统中所支持的 block 大小有 1K, 2K 及 4K 三种而已。在格式化时 block 的大小就固定了,且每个 block 都有编号,以方便 inode 的记录。

block 大小而产生的 Ext2 文件系统限制如下:


Block 大小

1KB

2KB

4KB

最大单一文件限制

16GB

256GB

2TB

最大文件系统总容量

2TB

8TB

16TB

Block基本限制:

block 的大小与数量在格式化完就不能够再改变了(除非重新格式化)

每个 block 内最多只能够放置一个文件的数据;

如果文件大于 block 的大小,则一个文件会占用多个 block 数量;

如果文件小于 block ,则该 block 的剩余容量就不能够再被使用了。

每个 block 仅能容纳一个文件的数据,如果你的文件都非常小,block 在格式化时却选用最大的 4K 时,可能会产生一些容量的浪费。例如 BBS 网站的数据!

在进行文件系统的格式化之前,请先想好该文件系统预计使用的情况。即分区大概使用哪些文件类型与文件大小!

⑤inode table (inode 表格)

inode 的内容在记录文件的属性以及该文件实际数据是放置在哪几号block内。

inode 记录的文件数据:

文件的存取模式(read/write/excute);

文件的拥有者与群组(owner/group);

文件的容量;

文件创建或状态改变的时间(ctime);

最近一次的读取时间(atime);

最近修改的时间(mtime);

定义文件特性的旗标(flag),如 SetUID...;

该文件真正内容的指向(pointer);

inode 的数量与大小也是在格式化时就已经固定:

每个 inode 大小均固定为 128bytes;

每个文件都仅会占用一个inode 而已;

承上,因此文件系统能够创建的文件数量与 inode 的数量有关;

系统读取文件时需要先找到inode,并分析 inode 所记录的权限与用户是否符合,若符合才能够开始实际读取 block 的内容。

⑥Filesystem Description (文件系统描述说明)

这个区段可以描述每个block group 的开始与结束的 block 号码,以及说明每个区段 (superblock, bitmap, inodemap, data block) 分别介于哪一个 block号码之间。这部份也能够用 dumpe2fs 来观察的。

⑦block bitmap (区块对照表)

从 blockbitmap 当中可以知道哪些 block 是空的,因此我们的系统就能够很快速的找到可使用的空间来处置文件。

如果你删除某些文件时,那么那些文件原本占用的 block 号码就得要释放出来,此时在 block bitmap 当中相对应到该 block 号码的标志就得要修改成为“未使用中”。

⑧inode bitmap (inode 对照表)

这个其实与 blockbitmap 是类似的功能,只是 block bitmap 记录的是使用与未使用的 block 号码,至于 inode bitmap 则是记录使用与未使用的 inode 号码。

三、观察Linux文件系统

读取磁盘的效能,特别是高并发的网站服务器,如果文件真的太过离散,确实还是会发生读取效率低落的问题。那么可以将整个 filesystme 内的数据全部复制出来,将该 filesystem 重新格式化,再将数据给他复制回去即可解决这个问题。

其次,partition 的规划并不是越大越好, 而是真的要针对您的主机用途来进行规划才行。因为一般来说,我们将 inode table 与 data block 称为数据存放区域,至于其他例如 superblock、 block bitmap 与 inode bitmap 等区段就被称为 metadata (中介数据)。因为 superblock, inode bitmap 及 block bitmap 的数据是经常变动的,每次新增、移除、编辑时都可能会影响到这三个部分的数据,因此才被称为中介数据。

做为服务器用途的文件系统,可以从上述角度去优先磁盘性能。

[root@www ~]# dumpe2fs[-bh] 设备文件名

选项与参数:

-b :列出保留为坏轨的部分(一般用不到吧!?)

-h :仅列出 superblock 的数据,不会列出其他的区段内容!

[root@localhost~]# dumpe2fs  /dev/sda2

dumpe2fs 1.39 (29-May-2006)

Filesystem volume name:   /        <&#61;&#61;这个是文件系统的名称(Label

Last mounted on:         

Filesystem UUID:          dac4804e-d7d9-4265-ae6d-4216d5eaa70a

Filesystem magic number:  0xEF53

Filesystem revision #:    1 (dynamic)

Filesystem features:      has_journal ext_attr resize_inodedir_index filetype needs_recovery spars                                                                             e_super large_file 

Default mount options:    user_xattr acl      <&#61;&#61;默认挂载的参数

Filesystem state:         clean          <&#61;&#61;这个文件系统是没问题的(clean

Errors behavior:          Continue

Filesystem OS type:       Linux

Inode count:              1280000      <&#61;&#61;inode的总数

Block count:              1279175       <&#61;&#61;block的总数

Reserved block count:     63958

Free blocks:              1142588        <&#61;&#61;还有多少个 block 可用

Free inodes:              1274746        <&#61;&#61;还有多少个 inode 可用

First block:              0

Block size:               4096            <&#61;&#61;每个 block 的大小

Fragment size:            4096

Reserved GDT blocks:      312

Blocks per group:         32768

Fragments per group:      32768

Inodes per group:         32000

Inode blocks per group:   1000

Filesystem created:       Fri Nov 13 07:31:36 2015

Last mount time:          Mon Nov 16 09:32:59 2015

Last write time:          Mon Nov 16 09:32:59 2015

Mount count:              9

Maximum mount count:      -1

Last checked:             Fri Nov 13 07:31:36 2015

Check interval:           0 ()

Reserved blocks uid:      0 (user root)

Reserved blocks gid:      0 (group root)

First inode:              11

Inode size:               128      <&#61;&#61;每个 inode 的大小

Journal inode:            8

Default directory hash:   tea

Directory Hash Seed:      db1e05f7-5293-49e4-a654-6f4ea6a401cb

Journal backup:           inode blocks

Journal size:             128M

Group 0: (Blocks 0-32767)      // block 的启始/结束号码

 Primary superblock at 0, Group descriptors at 1-1    <&#61;&#61;超级区块在 0 block

 Reserved GDT blocks at 2-313

 Block bitmap at 314 (&#43;314), Inode bitmap at 315 (&#43;315)    <&#61;&#61; Block bitmap 所在的 block

 Inode table at 316-1315 (&#43;316)        <&#61;&#61;inodetable 所在的 block

  0free blocks, 31989 free inodes, 2 directories   <&#61;&#61;所有 block 都用完

 Free blocks:

 Free inodes: 12-32000                    <&#61;&#61;剩余未使用的 inode 号码

实际观察 root 家目录内的文件所占用的 inode 号码时&#xff0c;可以使用 ls -i 这个选项来处理.

[root&#64;localhost ~]# ls -li

800009 -rw------- 1 root root  1646 Nov 12 23:42 anaconda-ks.cfg

800037 drwxr-xr-x 2 root root  4096 Nov 12 23:51 Desktop

800224 -rw-r--r-- 1 root root     0 Nov 15 15:51 enginer001.txt

800223 lrwxrwxrwx 1 root root    14 Nov 14 11:10 enginer002.txt ->enginer001.txt

800002 -rw-r--r-- 1 root root 33244 Nov 1223:42 install.log

800003 -rw-r--r-- 1 root root  4068 Nov 12 23:42 install.log.syslog

800228 -rwxr-xr-x 1 root root    21 Nov 14 11:56 mylsfile.shell

800220 d--------- 2 root root  4096 Nov 15 17:45 mysoft_dir

800225 -rw-r--r-- 1 root root    33 Nov 14 11:01 sinfo001.txt

举例&#xff1a;

[root&#64;localhost ~]# ll -d / /bin /boot /proc /sbin

drwxr-xr-x 25 root root  4096 Nov 17 09:13 /      <&#61;&#61;一个 4K block

drwxr-xr-x  2 root root  4096 Nov 13 08:38/bin

drwxr-xr-x  4 root root  1024 Nov 12 23:50/boot    <&#61;&#61;一个 1K block

dr-xr-xr-x 125 root root     0 Nov 16 17:32 /proc

drwxr-xr-x  2 root root 12288 Nov 13 08:38 /sbin    <&#61;&#61;三个 4K block

文件名是记录在目录的block 当中&#xff0c;因此当我们要读取某个文件时&#xff0c;就务必会经过目录的inode 与 block &#xff0c;然后才能够找到那个待读取文件的 inode 号码&#xff0c;最终才会读到正确的文件的 block 内的数据。

目录树是由根目录开始读起&#xff0c;因此系统透过挂载的信息可以找到挂载点的 inode 号码(通常一个 filesystem 的最顶层 inode 号码会由 2 号开始喔&#xff01;)&#xff0c;此时就能够得到根目录的 inode 内容&#xff0c;并依据该 inode 读取根目录的 block 内的文件名数据&#xff0c;再一层一层的往下读到正确的档名。如下&#xff1a;

[root&#64;localhost ~]#  ll -di / /etc /etc/passwd

     2 drwxr-xr-x 25 rootroot  4096 Nov 17 09:13 /

544001 drwxr-xr-x 98 root root 12288 Nov 17 19:15 /etc

546076 -rw-r--r--  1 rootroot  1656 Nov 17 19:15 /etc/passwd

四、数据的不一致(Inconsistent) 与日志式文件系统(Journalingfilesystem)

例如你的文件在写入文件系统时&#xff0c;因为不知名原因导致系统中断(例如突然的停电啊、系统核心发生错误啊&#xff5e;等等的怪事发生时)&#xff0c;所以写入的数据仅有 inode table 及 data block 而已&#xff0c;最后一个同步升级中介数据的步骤并没有做完&#xff0c;此时就会发生 metadata 的内容与实际数据存放区产生不一致 (Inconsistent) 的情况。

不过&#xff0c;这样的检查真的是很费时&#xff5e;因为要针对 metadata 区域与实际数据存放区来进行比对&#xff0c;呵呵&#xff5e;得要搜寻整个 filesystem 呢&#xff5e;如果你的文件系统有 100GB 以上&#xff0c;而且里面的文件数量又多时。在对 Internet 提供服务的服务器主机上面&#xff0c;这样的检查真的会造成主机复原时间的拉长。

这也就造成后来所谓日志式文件系统的兴起了&#xff01;避免文件系统不一致的情况发生&#xff0c;在我们的 filesystem 当中规划出一个区块&#xff0c;该区块专门在记录写入或修订文件时的步骤&#xff0c;简化一致性检查的步骤。日志方法如下&#xff1a;

①预备&#xff1a;当系统要写入一个文件时&#xff0c;会先在日志记录区块中纪录某个文件准备要写入的信息&#xff1b;

②实际写入&#xff1a;开始写入文件的权限与数据&#xff1b;开始升级 metadata 的数据&#xff1b;

③结束&#xff1a;完成数据与metadata 的升级后&#xff0c;在日志记录区块当中完成该文件的纪录。

在这样的程序当中&#xff0c;万一数据的纪录过程当中发生了问题&#xff0c;那么我们的系统只要去检查日志记录区块&#xff0c;就可以知道哪个文件发生了问题&#xff0c;针对该问题来做一致性的检查即可&#xff0c;而不必针对整块 filesystem 去检查&#xff0c;这样就可以达到快速修复 filesystem 的能力&#xff01;这就是日志式文件最基础的功能。

五、文件系统与 VFS

U盘插入电脑的usb接口&#xff0c;其文件系统格式fat32&#xff0c;挂载后&#xff0c;我们可以使用 cp 命令从fat32文件系统格式的u盘拷贝数据到 ext3 文件系统格式的硬盘&#xff1b;而这样的操作涉及到两个不同的文件系统。

其次&#xff0c;虽然 Linux 的标准文件系统是 ext2&#xff0c;ext3&#xff08;日志功能的&#xff09;&#xff0c;但是&#xff0c;Linux还有支持很多文件系统格式的&#xff0c;包括 SGI 的 XFS 文件系统&#xff0c;可以适用更小型文件的 Reiserfs 文件系统&#xff0c;以及 Windows 的 FAT 文件系统等等。

常见的支持文件系统有&#xff1a;

    传统文件系统&#xff1a;ext2 / minix / MS-DOS / FAT (用 vfat 模块) / iso9660(光盘)等等&#xff1b;

    日志式文件系统&#xff1a;ext3 / ReiserFS / Windows&#39; NTFS / IBM&#39;s JFS / SGI&#39;s XFS

    网络文件系统&#xff1a; NFS / SMBFS

通过上述描述&#xff0c;Linux 的核心又是如何认识与管理这些文件系统呢&#xff1f;

原因是&#xff1a;整个 Linux 的系统都是透过一个名为Virtual Filesystem Switch 的核心功能去读取 filesystem 的。

假设你的 / 使用的是/dev/hda1 &#xff0c;用 ext3 &#xff0c;而 /home 使用 /dev/hda2 &#xff0c;用 reiserfs &#xff0c;那么你取用 /home/dmtsai/.bashrc 时&#xff0c;有特别指定要用的什么文件系统的模块来读取吗&#xff1f;应该是没有吧&#xff01;这个就是 VFS 的功能啦&#xff01;透过这个 VFS 的功能来管理所有的 filesystem。

实现VFS原理如下&#xff1a;

Linux系统以一组通用对象的角度看待所有文件系统。这些对象是超级块&#xff08;superblock&#xff09;、inode、dentry 和文件。

超级块&#xff1a;在每个文件系统的根上&#xff0c;超级块描述和维护文件系统的状态。

Inode&#xff1a;文件系统中管理的每个对象&#xff08;文件或目录&#xff09;在 Linux 中表示为一个 inode

Dentry&#xff1a;用来实现名称和 inode 之间的映射&#xff0c;有一个目录缓存用来保存最近使用的 dentry。dentry 还维护目录和文件之间的关系&#xff0c;从而支持在文件系统中移动。

想要知道你的 Linux 支持的文件系统有哪些&#xff0c;可以察看底下这个目录

[root&#64;localhost ~]# ls -l/lib/modules/$(uname -r)/kernel/fs

total 232

drwxr-xr-x 2 root root 4096 Nov 12 23:39 autofs4

drwxr-xr-x 2 root root 4096 Nov 12 23:39 cachefiles

drwxr-xr-x 2 root root 4096 Nov 12 23:39 cifs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 configfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 cramfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 dlm

drwxr-xr-x 2 root root 4096 Nov 12 23:39 ecryptfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 exportfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 ext3

drwxr-xr-x 2 root root 4096 Nov 12 23:39 ext4

drwxr-xr-x 2 root root 4096 Nov 12 23:39 fat

drwxr-xr-x 2 root root 4096 Nov 12 23:39 freevxfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 fscache

drwxr-xr-x 2 root root 4096 Nov 12 23:39 fuse

drwxr-xr-x 3 root root 4096 Nov 12 23:39 gfs2

drwxr-xr-x 2 root root 4096 Nov 12 23:39 hfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 hfsplus

drwxr-xr-x 2 root root 4096 Nov 12 23:39 jbd

drwxr-xr-x 2 root root 4096 Nov 12 23:39 jbd2

drwxr-xr-x 2 root root 4096 Nov 12 23:39 jffs2

drwxr-xr-x 2 root root 4096 Nov 12 23:39 lockd

drwxr-xr-x 2 root root 4096 Nov 12 23:39 msdos

drwxr-xr-x 2 root root 4096 Nov 12 23:39 nfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 nfs_common

drwxr-xr-x 2 root root 4096 Nov 12 23:39 nfsd

drwxr-xr-x 2 root root 4096 Nov 12 23:39 nls

drwxr-xr-x 2 root root 4096 Nov 12 23:39 squashfs

drwxr-xr-x 2 root root 4096 Nov 12 23:39 udf

drwxr-xr-x 2 root root 4096 Nov 12 23:39 vfat

[root&#64;localhost ~]# ls -l/lib/modules/$(uname -r)/kernel/fs | wc –l

     30    

合计支持30种文件系统。




推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 配置IPv4静态路由实现企业网内不同网段用户互访
    本文介绍了通过配置IPv4静态路由实现企业网内不同网段用户互访的方法。首先需要配置接口的链路层协议参数和IP地址,使相邻节点网络层可达。然后按照静态路由组网图的操作步骤,配置静态路由。这样任意两台主机之间都能够互通。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
author-avatar
小嫣师妹
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有