Heim  >  Artikel  >  Betrieb und Instandhaltung  >  Lassen Sie uns über Linux TTY/PTS und seine unterschiedlichen Funktionen sprechen

Lassen Sie uns über Linux TTY/PTS und seine unterschiedlichen Funktionen sprechen

藏色散人
藏色散人nach vorne
2021-09-19 17:00:183255Durchsuche

Die Kolumne Linux-System-Tutorial stellt Ihnen Linux TTY/PTS und ihre unterschiedlichen Funktionen vor. Ich hoffe, dass es Freunden in Not hilfreich sein wird!

Linux TTY/PTS-Übersicht

Wenn wir einen Buchstaben auf der Tastatur eingeben, wie wird er an den entsprechenden Prozess gesendet? Wir sehen Ausgaben wie tty1 und pts/0 bis ps, who und andere Befehle. Was sind ihre Funktionen und Unterschiede?

TTY-Geschichte

Vor dem Aufkommen von Computern, die Multitasking unterstützten

Bevor Computer auf den Markt kamen, verwendeten die Menschen bereits ein Gerät namens Teletype, um Informationen untereinander zu übertragen:

+----------+     Physical Line     +----------+
| teletype |<--------------------->| teletype |
+----------+                       +----------+

Zwei Teletypes sind miteinander verbunden An beiden Enden der Leitung können sich auch modemähnliche Geräte befinden (sie werden hier ignoriert). Beim Tippen auf der Tastatur an einem Ende des Fernschreibers werden die entsprechenden Daten gesendet spezifische Funktion Was es tut, weiß ich nicht viel darüber. (In meinem Kopf stellte ich mir vor, dass das Bild an einem Ende getippt und am anderen Ende gedruckt war)

Dabei handelt es sich um alte Antiquitäten, mit denen ich noch nie in Kontakt gekommen bin, daher kann ich nur einfache Vermutungen anstellen.

Nach dem Aufkommen von Computern, die Multitasking unterstützen

Nachdem Computer Multitasking unterstützten, dachten die Leute darüber nach, diese Fernschreiber als Terminal des Computers an den Computer anzuschließen, damit sie den Computer bedienen konnten.

Es gibt zwei Hauptgründe für den Einsatz von Fernschreibern (persönliche Meinung):

  • In der Realität gibt es bereits eine große Anzahl von Fernschreibern verschiedener Hersteller, die die vorhandenen Ressourcen voll ausnutzen können

  • Das zugehörige Netzwerk von Fernschreibern ist relativ ausgereift und verbunden Praktisch

Die Verbindung hat sich also wie folgt entwickelt:

                                                                      +----------+ 
+----------+   +-------+     Physical Line     +-------+   +------+   |          |
| Terminal |<->| Modem |<--------------------->| Modem |<->| UART |<->| Computer |
+----------+   +-------+                       +-------+   +------+   |          |
                                                                      +----------+
  • Das Terminal auf der linken Seite besteht aus einer Vielzahl von Teletypen

  • Modems werden auf beiden Seiten der physischen Leitung verwendet, was das bedeutet Wir nennen es oft „Katze“, das heißt Da sich das Netzwerk nach und nach weiterentwickelt hat, kann jeder die Verbindung teilen. (Eine grobe Vermutung, vielleicht falsch)

  • UART kann als ein Gerät verstanden werden, das Fernschreibsignale in Signale umwandelt, die von Computern erkannt werden können

Kernel-TTY-Subsystem

Um diese Fernschreiber zu unterstützen, wurde der Computer entwickelt Ein Name namens TTY. Die interne Struktur des Subsystems ist wie folgt:

    +-----------------------------------------------+
    |                    Kernel                     |
    |                                 +--------+    |
    |   +--------+   +------------+   |        |    |       +----------------+
    |   |  UART  |   |    Line    |   |  TTY   |<---------->| User process A |
<------>|        |<->|            |<->|        |    |       +----------------+
    |   | driver |   | discipline |   | driver |<---------->| User process B |
    |   +--------+   +------------+   |        |    |       +----------------+
    |                                 +--------+    |
    |                                               |
    +-----------------------------------------------+
  • Der UART-Treiber stellt eine Verbindung zu externen UART-Geräten her.

  • Die Liniendisziplin führt hauptsächlich einige Eingabe- und Ausgabeverarbeitungen durch TTY-Treiber

  • TTY-Treiber Wird zur Handhabung verschiedener Endgeräte verwendet

  • Der User-Space-Prozess verarbeitet das Terminal über den TTY-Treiber

Der Einfachheit halber wird der UART-Treiber und die Leitungsdisziplin nicht verwendet Sie können in der folgenden Einleitung separat aufgeführt werden.

TTY-Gerät

Für jedes Terminal erstellt der TTY-Treiber ein entsprechendes TTY-Gerät. Wenn mehrere Terminals angeschlossen sind, sieht es so aus dies:

                      +----------------+
                      |   TTY Driver   |
                      |                |
                      |   +-------+    |       +----------------+
 +------------+       |   |       |<---------->| User process A |
 | Terminal A |<--------->| ttyS0 |    |       +----------------+
 +------------+       |   |       |<---------->| User process B |
                      |   +-------+    |       +----------------+
                      |                |
                      |   +-------+    |       +----------------+
 +------------+       |   |       |<---------->| User process C |
 | Terminal B |<--------->| ttyS1 |    |       +----------------+
 +------------+       |   |       |<---------->| User process D |
                      |   +-------+    |       +----------------+
                      |                |
                      +----------------+

Wenn der Treiber empfängt Wenn ein Terminal angeschlossen ist, wird ein entsprechendes TTY-Gerät gemäß dem Terminalmodell und den Parametern erstellt (der Gerätename im obigen Bild heißt ttyS0, da die meisten Terminalverbindungen seitdem serielle Verbindungen sind). Jedes Terminal kann unterschiedlich sein. Es verfügt über eigene spezielle Befehle und Nutzungsgewohnheiten, sodass die Konfiguration jedes TTY-Geräts unterschiedlich sein kann. Wenn Sie beispielsweise die Löschtaste drücken, löschen einige möglicherweise die vorherigen Zeichen und andere möglicherweise die folgenden. Wenn die Konfiguration nicht korrekt ist, verhalten sich einige Tasten möglicherweise nicht wie gewünscht. Dies ist auch der Fall, wenn wir simulierte Terminals verwenden . Wenn die Standardkonfiguration nicht unseren Gewohnheiten entspricht, müssen einige personalisierte Konfigurationen vorgenommen werden.

Später, mit der kontinuierlichen Weiterentwicklung von Computern, sind Fernschreibgeräte nicht mehr erforderlich. Jede Maschine verfügt über eine eigene Tastatur und einen eigenen Monitor ssh, aber die TTY-Treiberarchitektur des Kernels hat sich nicht geändert. Wenn wir mit den Prozessen im System für E/A interagieren möchten, müssen wir immer noch TTY-Geräte verwenden, daher sind verschiedene Terminalsimulationssoftware erschienen, und es gibt auch mehrere Simulationen gängige Terminals wie VT100, VT220, XTerm usw.

  1. Mit dem Befehl toe -a können Sie alle vom System unterstützten Terminaltypen auflisten. toe -a列出系统支持的所有终端类型

  2. 可以通过命令infocmp来比较两个终端的区别,比如infocmp vt100 vt220

Mit dem Befehl infocmp können Sie den Unterschied zwischen zwei Terminals vergleichen, z. B. infocmp vt100 vt220 gibt die Differenz zwischen vt100 und vt220 aus. So gehen Programme mit TTY um wird eingeführt

Aus dem obigen lsof ist ersichtlich, dass stdin(0u), stdout(1u) und stderr(2u) der aktuell laufenden Bash- und lsof-Prozesse alle an dieses TTY gebunden sind.

Das Folgende ist ein Strukturdiagramm der Interaktion zwischen tty und Prozessen und E/A-Geräten:
#先用tty命令看看当前bash关联到了哪个tty
dev@debian:~$ tty
/dev/pts/1

#看tty都被哪些进程打开了
dev@debian:~$ lsof /dev/pts/1
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
bash     907  dev    0u   CHR  136,1      0t0    4 /dev/pts/1
bash     907  dev    1u   CHR  136,1      0t0    4 /dev/pts/1
bash     907  dev    2u   CHR  136,1      0t0    4 /dev/pts/1
bash     907  dev  255u   CHR  136,1      0t0    4 /dev/pts/1
lsof    1118  dev    0u   CHR  136,1      0t0    4 /dev/pts/1
lsof    1118  dev    1u   CHR  136,1      0t0    4 /dev/pts/1
lsof    1118  dev    2u   CHR  136,1      0t0    4 /dev/pts/1

#往tty里面直接写数据跟写标准输出是一样的效果
dev@dev:~$ echo aaa > /dev/pts/2
aaa

Sie können tty als Pipe verstehen, und an einem Ende geschriebener Inhalt kann vom anderen Ende gelesen werden, und umgekehrt umgekehrt.

  • 这里input和output可以简单的理解为键盘和显示器,后面会介绍在各种情况下input/ouput都连接的什么东西。

  • tty里面有一个很重要的属性,叫Foreground process group,记录了当前前端的进程组是哪一个。process group的概念会在下一篇文章中介绍,这里可以简单的认为process group里面只有一个进程。

  • 当pts/1收到input的输入后,会检查当前前端进程组是哪一个,然后将输入放到进程组的leader的输入缓存中,这样相应的leader进程就可以通过read函数得到用户的输入

  • 当前端进程组里面的进程往tty设备上写数据时,tty就会将数据输出到output设备上

  • 当在shell中执行不同的命令时,前端进程组在不断的变化,而这种变化会由shell负责更新到tty设备中

  • 从上面可以看出,进程和tty打交道很简单,只要保证后台进程不要读写tty就可以了,即写后台程序时,要将stdin/stdout/stderr重定向到其它地方(当然deamon程序还需要做很多其它处理)。

    先抛出两个问题(后面有答案):

    • 当非前端进程组里面的进程(后台进程)往tty设备上写数据时,会发生什么?会输出到outpu上吗?

    • 当非前端进程组里面的进程(后台进程)从tty设备上读数据时,会发生什么?进程会阻塞吗?

    TTY是如何被创建的

    下面介绍几种常见的情况下tty设备是如何创建的,以及input和output设备都是啥。

    键盘显示器直连(终端)

    先看图再说话:

                       +-----------------------------------------+
                       |          Kernel                         |
                       |                           +--------+    |       +----------------+ 
     +----------+      |   +-------------------+   |  tty1  |<---------->| User processes |
     | Keyboard |--------->|                   |   +--------+    |       +----------------+
     +----------+      |   | Terminal Emulator |<->|  tty2  |<---------->| User processes |
     | Monitor  |<---------|                   |   +--------+    |       +----------------+
     +----------+      |   +-------------------+   |  tty3  |<---------->| User processes |
                       |                           +--------+    |       +----------------+
                       |                                         |
                       +-----------------------------------------+

    键盘、显示器都和内核中的终端模拟器相连,由模拟器决定创建多少tty,比如你在键盘上输入ctrl+alt+F1时,模拟器首先捕获到该输入,然后激活tty1,这样键盘的输入会转发到tty1,而tty1的输出会转发到显示器,同理用输入ctrl+alt+F2,就会切换到tty2。

    当模拟器激活tty时如果发现没有进程与之关联,意味着这是第一次打开该tty,于是会启动配置好的进程并和该tty绑定,一般该进程就是负责login的进程。

    当切换到tty2后,tty1里面的输出会输出到哪里呢?tty1的输出还是会输出给模拟器,模拟器里会有每个tty的缓存,不过由于模拟器的缓存空间有限,所以下次切回tty1的时候,只能看到最新的输出,以前的输出已经不在了。

    不确定这里的终端模拟器对应内核中具体的哪个模块,但肯定有这么个东西存在

    SSH远程访问

     +----------+       +------------+
     | Keyboard |------>|            |
     +----------+       |  Terminal  |
     | Monitor  |<------|            |
     +----------+       +------------+
                              |
                              |  ssh protocol
                              |
                              ↓
                        +------------+
                        |            |
                        | ssh server |--------------------------+
                        |            |           fork           |
                        +------------+                          |
                            |   ↑                               |
                            |   |                               |
                      write |   | read                          |
                            |   |                               |
                      +-----|---|-------------------+           |
                      |     |   |                   |           ↓
                      |     ↓   |      +-------+    |       +-------+
                      |   +--------+   | pts/0 |<---------->| shell |
                      |   |        |   +-------+    |       +-------+
                      |   |  ptmx  |<->| pts/1 |<---------->| shell |
                      |   |        |   +-------+    |       +-------+
                      |   +--------+   | pts/2 |<---------->| shell |
                      |                +-------+    |       +-------+
                      |    Kernel                   |
                      +-----------------------------+

    这里的Terminal可能是任何地方的程序,比如windows上的putty,所以不讨论客户端的Terminal程序是怎么和键盘、显示器交互的。由于Terminal要和ssh服务器打交道,所以肯定要实现ssh的客户端功能。

    这里将建立连接和收发数据分两条线路解释,为了描述简洁,这里以sshd代替ssh服务器程序:

    建立连接

    • 1.Terminal请求和sshd建立连接

    • 2.如果验证通过,sshd将创建一个新的session

    • 3.调用API(posix_openpt())请求ptmx创建一个pts,创建成功后,sshd将得到和ptmx关联的fd,并将该fd和session关联起来。

    #pty(pseudo terminal device)由两部分构成,ptmx是master端,pts是slave端,
    #进程可以通过调用API请求ptmx创建一个pts,然后将会得到连接到ptmx的读写fd和一个新创建的pts,
    #ptmx在内部会维护该fd和pts的对应关系,随后往这个fd的读写会被ptmx转发到对应的pts。
    
    #这里可以看到sshd已经打开了/dev/ptmx
    dev@debian:~$ sudo lsof /dev/ptmx
    COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    sshd    1191  dev    8u   CHR    5,2      0t0 6531 /dev/ptmx
    sshd    1191  dev   10u   CHR    5,2      0t0 6531 /dev/ptmx
    sshd    1191  dev   11u   CHR    5,2      0t0 6531 /dev/ptmx
    • 4.同时sshd创建shell进程,将新创建的pts和shell绑定

    收发消息

    • 1.Terminal收到键盘的输入,Terminal通过ssh协议将数据发往sshd

    • 2.sshd收到客户端的数据后,根据它自己管理的session,找到该客户端对应的关联到ptmx上的fd

    • 3.往找到的fd上写入客户端发过来的数据

    • 4.ptmx收到数据后,根据fd找到对应的pts(该对应关系由ptmx自动维护),将数据包转发给对应的pts

    • 5.pts收到数据包后,检查绑定到自己上面的当前前端进程组,将数据包发给该进程组的leader

    • 6.由于pts上只有shell,所以shell的read函数就收到了该数据包

    • 7.shell对收到的数据包进行处理,然后输出处理结果(也可能没有输出)

    • 8.shell通过write函数将结果写入pts

    • 9.pts将结果转发给ptmx

    • 10.ptmx根据pts找到对应的fd,往该fd写入结果

    • 11.sshd收到该fd的结果后,找到对应的session,然后将结果发给对应的客户端

    键盘显示器直连(图形界面)

     +----------+       +------------+
     | Keyboard |------>|            |
     +----------+       |  Terminal  |--------------------------+
     | Monitor  |<------|            |           fork           |
     +----------+       +------------+                          |
                            |   ↑                               |
                            |   |                               |
                      write |   | read                          |
                            |   |                               |
                      +-----|---|-------------------+           |
                      |     |   |                   |           ↓
                      |     ↓   |      +-------+    |       +-------+
                      |   +--------+   | pts/0 |<---------->| shell |
                      |   |        |   +-------+    |       +-------+
                      |   |  ptmx  |<->| pts/1 |<---------->| shell |
                      |   |        |   +-------+    |       +-------+
                      |   +--------+   | pts/2 |<---------->| shell |
                      |                +-------+    |       +-------+
                      |    Kernel                   |
                      +-----------------------------+

    为了简化起见,本篇不讨论Linux下图形界面里Terminal程序是怎么和键盘、显示器交互的。

    这里和上面的不同点就是,这里的Terminal不需要实现ssh客户端,但需要把ssh服务器要干的活也干了(当然ssh通信相关的除外)。

    SSH + Screen/Tmux

    常用Linux的同学应该对screen和tmux不陌生,通过它们启动的进程,就算网络断开了,也不会受到影响继续执行,下次连上去时还能看到进程的所有输出,还能继续接着干活。

    这里以tmux为例介绍其原理:

     +----------+       +------------+
     | Keyboard |------>|            |
     +----------+       |  Terminal  |
     | Monitor  |<------|            |
     +----------+       +------------+
                              |
                              |  ssh protocol
                              |
                              ↓
                        +------------+
                        |            |
                        | ssh server |--------------------------+
                        |            |           fork           |
                        +------------+                          |
                            |   ↑                               |
                            |   |                               |
                      write |   | read                          |
                            |   |                               |
                      +-----|---|-------------------+           |
                      |     ↓   |                   |           ↓
                      |   +--------+   +-------+    |       +-------+  fork   +-------------+
                      |   |  ptmx  |<->| pts/0 |<---------->| shell |-------->| tmux client |
                      |   +--------+   +-------+    |       +-------+         +-------------+
                      |   |        |                |                               ↑
                      |   +--------+   +-------+    |       +-------+               |
                      |   |  ptmx  |<->| pts/2 |<---------->| shell |               |
                      |   +--------+   +-------+    |       +-------+               |
                      |     ↑   |  Kernel           |           ↑                   |
                      +-----|---|-------------------+           |                   |
                            |   |                               |                   |
                            |w/r|   +---------------------------+                   |
                            |   |   |            fork                               |
                            |   ↓   |                                               |
                        +-------------+                                             |
                        |             |                                             |
                        | tmux server |<--------------------------------------------+
                        |             |
                        +-------------+

    系统中的ptmx只有一个,上图中画出来了两个,目的是为了表明tmux服务器和sshd都用ptmx,但它们之间又互不干涉。

    这种情况要稍微复杂一点,不过原理都是一样的,前半部分和普通ssh的方式是一样的,只是pts/0关联的前端进程不是shell了,而是变成了tmux客户端,所以ssh客户端发过来的数据包都会被tmux客户端收到,然后由tmux客户端转发给tmux服务器,而tmux服务器干的活和ssh的类似,也是维护一堆的session,为每个session创建一个pts,然后将tmux客户端发过来的数据转发给相应的pts。

    由于tmux服务器只和tmux客户端打交道,和sshd没有关系,当终端和sshd的连接断开时,虽然pts/0会被关闭,和它相关的shell和tmux客户端也将被kill掉,但不会影响tmux服务器,当下次再用tmux客户端连上tmux服务器时,看到的还是上次的内容。

    TTY和PTS的区别

    从上面的流程中应该可以看出来了,对用户空间的程序来说,他们没有区别,都是一样的;从内核里面来看,pts的另一端连接的是ptmx,而tty的另一端连接的是内核的终端模拟器,ptmx和终端模拟器都只是负责维护会话和转发数据包;再看看ptmx和内核终端模拟器的另一端,ptmx的另一端连接的是用户空间的应用程序,如sshd、tmux等,而内核终端模拟器的另一端连接的是具体的硬件,如键盘和显示器。

    常见的TTY配置

    先先来看看当前tty的所有配置:

    dev@dev:~$ stty -a
    speed 38400 baud; rows 51; columns 204; line = 0;
    intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
    -parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
    -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel -iutf8
    opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
    isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

    stty还可以用来修改tty的参数,用法请参考man stty

    只要是有权限的程序,都可以通过Linux提供的API来修改TTY的配置,下面介绍一些常见的的配置项。

    rows 51; columns 204;

    这个配置一般由终端控制,当终端的窗口大小发生变化时,需要通过一定的手段修改该配置,比如ssh协议里面就有修改窗口大小的参数,sshd收到客户端的请求后,会通过API修改tty的这个参数,然后由tty通过信号SIGWINCH通知前端程序(比如shell或者vim),前端程序收到信号后,再去读tty的这个参数,然后就知道如何调整自己的输出排版了。

    intr = ^C

    tty除了在终端和前端进程之间转发数据之外,还支持很多控制命令,比如终端输入了CTRL+C,那么tty不会将该输入串转发给前端进程,而是将它转换成信号SIGINT发送给前端进程。这个就是用来配置控制命令对应的输入组合的,比如我们可以配置“intr = ^E”表示用CTRL+E代替CTRL+C。

    start = ^Q; stop = ^S;

    这是两个特殊的控制命令,估计经常有人会碰到,在键盘上不小心输入CTRL+S后,终端没反应了,即没输出,也不响应任何输入。这是因为这个命令会告诉TTY暂停,阻塞所有读写操作,即不转发任何数据,只有按了CTRL+Q后,才会继续。这个功能应该是历史遗留,以前终端和服务器之间没有流量控制功能,所以有可能服务器发送数据过快,导致终端处理不过来,于是需要这样一个命令告诉服务器不要再发了,等终端处理完了后在通知服务器继续。

    该命令现在比较常用的一个场景就是用tail -f命令监控日志文件的内容时,可以随时按CTRL+S让屏幕停止刷新,看完后再按CTRL+Q让它继续刷,如果不这样的话,需要先CTRL+C退出,看完后在重新运行tail -f命令。

    echo

    在终端输入字符的时候,之所以我们能及时看到我们输入的字符,那是因为TTY在收到终端发过去的字符后,会先将字符原路返回一份,然后才交给前端进程处理,这样终端就能及时的显示输入的字符。echo就是用来控制该功能的配置项,如果是-echo的话表示disable echo功能。

    -tostop

    如果你在shell中运行程序的时候,后面添加了&,比如./myapp &,这样myapp这个进程就会在后台运行,但如果这个进程继续往tty上写数据呢?这个参数就用来控制是否将输出转发给终端,也即结果会不会在终端显示,这里“-tostop”表示会输出到终端,如果配置为“tostop”的话,将不输出到终端,并且tty会发送信号SIGTTOU给myapp,该信号的默认行为是将暂停myapp的执行。

    TTY-bezogene Signale

    Zusätzlich zu SIGINT, SIGTTOU und SIGWINCHU, die in der Konfigurationseinführung oben erwähnt wurden, gibt es mehrere TTY-bezogene Signale

    SIGTTIN

    Wenn der Hintergrundprozess das TTY liest, sendet das TTY das Signal an In der entsprechenden Prozessgruppe besteht das Standardverhalten darin, die Ausführung von Prozessen in der Prozessgruppe anzuhalten. Wie kann die Ausführung eines ausgesetzten Prozesses fortgesetzt werden? Weitere Informationen finden Sie im nächsten Artikel unter SIGCONT.

    SIGHUP

    Wenn das andere Ende des tty beispielsweise hängt, wird die SSH-Sitzung getrennt, sodass sshd das mit ptmx verknüpfte fd schließt. Der Kernel sendet ein SIGHUP-Signal an alle mit dem tty verbundenen Prozesse und den Prozess empfängt Das Standardverhalten nach dem Empfang dieses Signals besteht darin, den Prozess zu beenden.

    SIGTSTP

    Wenn das Terminal STRG+Z eingibt, sendet das TTY nach dem Empfang SIGTSTP an die Front-End-Prozessgruppe. Sein Standardverhalten besteht darin, die Front-End-Prozessgruppe in das Backend zu verschieben und die Ausführung aller zu unterbrechen Prozesse in der Prozessgruppe.

    Signale im Zusammenhang mit TTY können erfasst und das Standardverhalten geändert werden.

    Fazit: In diesem Artikel werden allgemeine TTY-Funktionen und -Features vorgestellt Gruppe, Job, Hintergrundprogramm etc., bitte bleiben Sie dran.

    Verwandte Empfehlungen: „

    Linux-Video-Tutorial

    Das obige ist der detaillierte Inhalt vonLassen Sie uns über Linux TTY/PTS und seine unterschiedlichen Funktionen sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen