プロセスの詳細: 子プロセス、スレッド情報、および共通ステータスの表示

PHPz
リリース: 2024-07-25 08:00:14
オリジナル
377 人が閲覧しました

プロセスの詳細: 子プロセス、スレッド情報、および共通ステータスの表示

Q&A

ps すべての子プロセスを表示しますか? pstree-pid

ps-eLf 各配列の意味は何ですか?

リーリー

プロセス下のすべてのスレッドのCPU使用率/ビデオメモリ/優先度およびその他の情報を表示しますか?トップ-H-p25120

スレッドが実行されている CPU を確認しますか? ps-eouser,pid,ppid,lwp,psr,args-L|grepl3-agent

一般的なプロセスのステータス?

リーリー

プロセスとその変換プロセスの 6 つの状態 (ライフサイクル) - 準備完了状態、実行状態、深いスリープ状態、浅いスリープ状態、停止状態、ゾンビ状態。

母子プロセスの共有とは何ですか?違いは何ですか? ——fork後、母プロセスと子プロセスはファイル記述子やmmapで構築したマッピング領域を共有しますが、プロセス空間やプロセスアドレスまでは親プロセスと全く同じです。完全に独立したスペース)

fork と vfork の違い - vfork は新しいプロセスを作成するために使用され、新しいプロセスの目的は新しいプログラムを実行することです。相違点 1: 親プロセスのアドレス空間は子プロセスにコピーされません。違い 2: vfork は、子プロセスが exec または (exit) を呼び出した後、必ず最初に実行されるようにし、親プロセスの実行をスケジュールできます。

**共有ビデオメモリのアドレス値はユーザープロセス空間の範囲内ですか? **フォーク後、子プロセスは親プロセスと同じアドレスを使用しますか?別プロセスの場合、共有ビデオメモリのプロセス空間に表示されるアドレスは同じですか?

プロセスのライフサイクル、準備完了状態と実行状態は数値的に同じであり、マクロ TASK_RUNNING によって定義されます。

Linux では Task_struct はどのように管理されますか?

ゾンビ プロセスを理解します。ゾンビ プロセスはほぼすべてのビデオ メモリ領域を放棄し、実行可能コードがなく、プロセス リスト内の位置を保持するだけです (リソースは長い間解放されており、Task_struct 構造はまだ存在します)。 。親プロセスが SIGCHLD シグナル処理関数をインストールせず、wait()/waitpid() を呼び出して子プロセスが終了するのを待ち、シグナルを明示的に無視しない場合、そのプロセスはゾンビ状態のままになります。親プロセスが終了すると、init プロセスが手動でこの子プロセスを引き継ぎ、LINUX コミュニティでクリーンアップします。それでも削除できます。親プロセスが終了しない場合、子プロセスはゾンビ状態のままになるため、システム内に多数のゾンビ プロセスが存在することがあります。システムが使用できるプロセス数には制限があります (cat/proc/sys/kernel/pid_max)。大量のゾンビプロセスが形成されると、使用可能なプロセスが存在しないため、システムは新しいプロセスを形成できなくなります。数字。

サブプロセスのリサイクル:

親プロセスは、wait/waitpid およびその他の関数を通じて子プロセスが終了するのを待機します。これにより、親プロセスがハングします。親プロセスが非常にビジーな場合は、シグナル関数を使用して SIGCHLD のハンドラーをインストールできます。これは、子プロセスが終了した後、親プロセスがシグナルを受信し、ハンドラー内で wait を呼び出してリサイクルできるためです。親プロセスが子プロセスの終了を気にしない場合は、シグナル (SIGCHLD、SIG_IGN) を使用して、子プロセスの終了に関心がないことをカーネルに通知できます。子プロセスの終了後、カーネルはそれをリサイクルします。親プロセスにメッセージを送信しなくなる、つまり、親プロセスが子プロセスをフォークし、子プロセスが子プロセスをフォークして終了するという方法もあります。つまり、子プロセスは init によって引き継がれ、子プロセスが終了すると、init がそれをリサイクルします。ただし、子プロセスのリサイクルは自分で行う必要があります。

子プロセスが終了後にゾンビ状態になるのはなぜですか? - 親プロセスが子プロセスの終了ステータスやその他の情報を取得する必要があるためです。

ゾンビ状態はすべての子プロセスが通過しなければならない状態ですか? - 子プロセス (init を除く) は exit() の直後には消えませんが、ゾンビ プロセス (Zombie) と呼ばれるデータ構造を残します (ビデオ メモリの一部を占有します)。リソース (つまり、プロセス テーブルにレコードがまだ存在する) は、親プロセスが処理するのを待っています。 exit() の後に親プロセスが子プロセスを処理する時間がない場合は、 ps コマンドを使用して、子プロセスのステータスが「Z」であることを確認できます。子プロセスが終了する前に親プロセスが終了した場合、子プロセスは init によって引き継がれます。 initはゾンビ状態の子プロセスを親プロセスとして処理します。

ゾンビプロセスを排除する方法:

親プロセスを書き換えて、子プロセスの終了後に子プロセスの本体を収集します。具体的な方法は、SIGCHLD シグナルを受け入れることです。子プロセスが終了すると、SIGCHLD シグナルが親プロセスに送信され、親プロセスはこのシグナルを受信した後、waitpid() 関数を実行して子プロセスの死体を収集します。これは、親プロセスが wait を呼び出さなくても、カーネルは親プロセスに SIGCHLD メッセージを送信するという原則に基づいていますが、このメッセージに応答したい場合は、処理関数を設定できます。 。 SIGCHLD シグナル: 子プロセスが終了すると、親プロセスはこのシグナルを受け取ります。親プロセスがこのシグナルを処理せず、子プロセスを待機しない場合、子プロセスは実際に終了し、カーネル プロセス テーブル内のエントリを占有します。このとき、子プロセスはゾンビ プロセスと呼ばれます。この種の状況は回避する必要があります (親プロセスが SIGCHILD シグナルを無視するかキャッチするか、生成する子プロセスを待つか、親プロセスが最初に終了し、その後子プロセスの終了が手動で引き継がれます)初期プロセス)。

プロセス終了時の exit() 関数、それではスレッド終了とは何でしょうか? ——スレッドが中止される 3 つの状況:

スレッドはスタートアップ関数から戻るだけであり、戻り値はスレッドの終了コードです。スレッドは、同じプロセス内の他のスレッドによってキャンセルされる可能性があります。スレッドは pthread_exit を呼び出します。

スレッドを待機しておらず、スレッドの戻り値に興味がない場合は、スレッドをデタッチ状態に設定し、スレッドの終了時にシステムが占有しているリソースを手動で再利用できるようにすることができます。スレッドは、それ自体をデタッチ状態に変更するために pthread_detach を呼び出すことはできません。他のスレッドによってのみ pthread_detach を呼び出すことができます。

pthread_cancel() を使用すると、あるスレッドが th で指定された別のスレッドをキャンセルできます。

プロセス - リソース割り当ての最小単位、スレッド - プログラム実行の最小単位。プロセスには独立したアドレス空間がありますが、スレッドには独立したアドレス空間がありません (同じプロセス内のスレッドはプロセスのアドレス空間を共有します)。プロセスがクラッシュしても、保護モードの他のプロセスには影響しません。スレッドはプロセスの一部にすぎません。スレッドには独自のスタックとローカル変数がありますが、スレッドには個別のアドレス空間がありません。1 つのスレッドがハングアップすると、プロセス全体が暴走したことになります。そのため、マルチプロセス プログラムはマルチスレッド プログラムよりも強力です。プロセスを切り替えるときにより多くのリソースを消費するため、プロセスが大きくなり、効率が低下します。ただし、同時操作と個々の変数の共有が必要な一部の同時操作では、プロセスではなくスレッドのみを使用できます。

マルチスレッドを使用する理由?

プロセス

プロセスはリソース割り当ての基本単位であり、スレッドはスケジューリングの基本単位です

プロセス情報

Linux スケジューラは実際にスケジューリングのために task_struct を識別します。プロセス スレッドに関係なく、最下層は task_struct に対応します。プロセスとスレッドの違いは、2 つのプロセス間で共有されるリソースはなく、すべてのリソースが 2 つのスレッド間で共有されます。

PCB(ProcessControlBlock)プロセス制御ブロック

task_struct は、Linux カーネルによるプロセスの記述であり、「プロセス記述子」とも呼ばれます。プロセスに必要なすべてのリソースを格納する構造の説明。 /proc/${pid} プロセス関連の情報。オペレーティング システムにとって、プロセスはデータ構造です。

struct task_struct {
longstate; // 进程状态-1为不可运行, 0为可运行, >0为已中断

struct mm_struct*mm; // 指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方
	pid_t pid; // 进程标识符,用来代表一个进程
 
struct task_struct __rcu*parent; // 指向父进程的指针
struct list_headchildren; // 子进程列表
 	struct list_head sibling; // 兄弟进程
struct fs_struct*fs;// 存放文件系统信息的指针
 
struct files_struct *files; // 一个数组,包含该进程打开的文件指针
unsigned int policy; // 调度策略:一般有FIFO,RR,CFS
...
};
ログイン後にコピー

从2.6版本之后,Linux改用了slab分配器动态生成task_struct,只须要在栈底(向上下降的栈)或栈顶(向下下降的栈)创建一个新的结构structthread_info(这儿是栈对象的尾端),你可以把slab分配器觉得是一种分配和释放数据结构的优化策略。通过预先分配和重复使用task_struct,可以防止动态分配和释放带来的资源消耗。

进程的地址空间ref

所谓进程地址空间(processaddressspace),就是从进程的视角听到的地址空间,是进程运行时所用到的虚拟地址的集合。

进程的显存

程序段(Text):程序代码在显存中的映射,储存函数体的二补码代码。

初始化过的数据(Data):在程序运行初早已对变量进行初始化的数据。

未初始化过的数据(BSS):在程序运行初未对变量进行初始化的数据。

栈(Stack):储存局部、临时变量,函数调用时,储存函数的返回表针,用于控制函数的调用和返回。在程序块开始时手动分配显存,结束时手动释放显存,其操作方法类似于数据结构中的栈。

堆(Heap):储存动态显存分配,须要程序员手工分配,手工释放.注意它与数据结构中的堆是两码事,分配方法类似于数组。

注:1.Text,BSS,Data段在编译时早已决定了进程将占用多少VM

可以通过size,晓得这种信息:

正常情况下,Linux进程不能对拿来储存程序代码的显存区域执行写操作,即程序代码是以只读的形式加载到显存中,但它可以被多个进程安全的共享。

创建进程后都创建了什么资源

进程创建

system()通过调用shell启动一个新进程

exec()以替换当前进程映像的方法启动一个新进程

fork()以复制当前进程映像的方法启动一个新进程

fork(2)

执行fork后,父进程的task_struck对拷给子进程,母子进程最初资源完全一样,而且是两份不同的拷贝,因而任何改动都导致两者的分裂。

兄妹进程对显存资源(mm)的管理使用了COW(Copy-On-Write,写时拷贝)技术:

在fork之前,一片显存区对应一份数学地址和一份虚拟地址,显存区的权限为RW;在fork以后,母子进程听到的显存区虚拟地址相同,化学地址也相同,母女进程使用的虽然是同一片化学显存,未发生显存拷贝,操作系统会将此显存区权限改为RO;父或子进程对显存区执行写操作将触发PageFault,操作系统此时会将显存区拷贝一份,母女进程见到的虚拟地址仍旧一样,而且化学地址早已不同。各进程虚拟地址到化学地址的映射由MMU(MemoryManagementUnit,显存管理单元)管理。fork运行在有MMU的CPU上。

Pour les processeurs sans MMU, il est difficile d'appliquer COW et support fork. Les processeurs sans MMU utilisent vfork pour créer des processus, et le processus parent se bloquera toujours jusqu'à ce que le processus enfant se termine ou s'exécute. La différence essentielle entre vfork et fork est que les processus mère et enfant dans vfork partagent la même zone de mémoire vidéo.

L'appel système

fork(2) est utilisé pour créer un nouveau processus, appelé processus enfant, qui s'exécute en même temps que le processus parent (simultané), et l'ordre d'exécution est incertain (asynchrone). pid_t est une définition de macro, son essence est int. En cas de succès, il renvoie deux valeurs, le processus enfant renvoie 0 et le processus parent renvoie l'ID du processus enfant sinon, une erreur renvoie -1

;

L'intégralité de l'espace d'adressage virtuel du parent est copié dans le processus enfant, y compris l'état du verrouillage mutex,

Le processus enfant est exactement le même que le processus parent, à l'exception des points suivants :

Processus de recyclage wait() et waitpid()

Recyclez la structure task_struct du processus enfant via waitpid()/wait().

Différence :

Processus orphelins et processus zombies Processus zombies

Processus zombie : un processus utilise fork pour créer un processus enfant. Si le processus enfant se termine et que le processus parent n'appelle pas wait ou waitpid pour obtenir les informations d'état du processus enfant, le descripteur de processus du processus enfant est toujours enregistré dans le système. Ces processus sont appelés processus zombies. (Le processus enfant peut être vu via /proc/$pid, mais pas le fil de discussion)

Lorsqu'un processus appelle la commande exit pour mettre fin à sa vie, bien qu'il ne soit pas vraiment détruit, il laisse derrière lui une structure de données appelée processus zombie (Zombie) (le système appelle exit, sa fonction est de faire sortir le processus, mais il se limite uniquement à transformer un processus normal en processus zombie et ne peut pas le détruire complètement).

Dans l'état des processus Linux, le processus zombie est d'un type très particulier. Il a déjà cédé presque tout l'espace mémoire vidéo. Il n'a pas de code exécutable et ne peut pas être planifié. pour enregistrer le processus. L'état de sortie et d'autres informations peuvent être collectées par d'autres processus. De plus, le processus zombie n'occupe plus d'espace mémoire vidéo. Il a besoin de son processus parent pour récupérer le cadavre.

Si son processus parent n'installe pas la fonction de traitement du signal SIGCHLD et appelle wait ou waitpid() pour attendre la fin du processus enfant, et n'ignore pas explicitement le signal, alors il restera toujours dans l'état zombie si le parent. Le processus se termine à ce moment-là, puis le processus d'initialisation prendra manuellement en charge ce sous-processus et récupérera son cadavre, et il pourra toujours être éliminé.

Et si le processus parent est une boucle et ne se termine pas, alors le processus enfant restera toujours à l'état zombie. C'est pourquoi il y a parfois de nombreux processus zombies dans le système. Les numéros de processus pouvant être utilisés par le système sont limités. Si un grand nombre de processus zombies sont formés, le système ne pourra pas former de nouveaux processus car il n'y a pas de numéros de processus disponibles.

Cause du processus zombie : une fois le processus enfant terminé, il envoie le signal SIGCHLD au processus parent, et le processus parent l'ignore par défaut ; le processus parent n'appelle pas la fonction wait() ou waitpid() pour attendre le processus zombie. fin du processus enfant. Moyens d'empêcher les processus zombies : Le processus parent appelle wait()/waitpid() pour attendre la fin du processus enfant. De cette façon, le processus parent sera généralement bloqué en attente et ne pourra pas gérer autre chose. Capturez le signal SIGCHLD et appelez la fonction d'attente sur la fonction de traitement du signal. Ce traitement peut éviter le problème décrit en 1. Fork deux fois, le processus parent crée un processus père et le processus père crée un processus fils. Après cela, le processus mère se suicide et le processus fils devient un processus orphelin et est pris en charge par le processus d'initialisation. Processus orphelin

Processus orphelin : le processus parent se termine, mais un ou plusieurs de ses processus enfants sont toujours en cours d'exécution, ces processus enfants deviendront donc des processus orphelins. Les processus orphelins seront pris en charge par le processus d'initialisation (pid=1), et le processus d'initialisation terminera la collecte de statut pour eux. (Si un processus orphelin apparaît dans le système, cela signifie que le processus principal n'a pas effacé les processus enfants avant de quitter)

Thread (LightweightProcess, LWP)

同一进程的多个线程获取进程ID时得到的是惟一ID值。Linux同一进程的多线程,在内核视角实际上每位线程都有一个PID,但在用户空间须要getpid()返回惟一值,Linux使用了一个小方法,引入了TGID的概念linux进程与线程 内核,getpid()返回的的TGID值。

pthread_create()

Linux线程本质上就是进程,只是与进程间资源共享方法不同,线程间共享所有资源。每位线程都有自己的task_struct,因而每位线程都可被CPU调度。多线程间又共享同一进程资源。

在一个线程中创建了另外一个线程,主线程要等到创建的线程返回了,获取该线程的返回值后主线程才退出。这个时侯就须要用到线程挂起。pthread_join函数用于挂起当前线程,直到指定的线程中止为止。

说线程的PID,是指用户空间的进程ID,值就是TGID(threadgroupIDforthethreadgroupleader);当非常强调,线程在内核空间的PID,则指线程在内核中task_struct里特有的PID。top–H命令从线程视角显示CPU占用率。不带参数的top命令,进程ID是主线程的PID(也就是TGID)。

Linux的进程和线程

进程是处于运行期的程序和相关资源的统称,具备一些要素:

拥有一段可执行程序代码。如同一场戏须要一个剧本。代码段可以多个进程共用,如同许多场表演都可以用一份剧本一样。拥有一段进程专用的系统堆栈空间。可以觉得是这个进程的“私有财产”,相应的,也肯定有系统空间堆栈在系统中有进程控制块(或称进程描述符,本文两种说法通用)描述这个进程的相关信息。可以觉得是进程的“户口”。系统通过这个控制块来控制进程的相关行为有独立的储存空间,也就是专有的用户空间,相应的又会有用户空间堆栈。理解各类ID

# ps -eo ppid,pid,tid,lwp,tgid,pgrp,sid,tpgid,args -L | awk '{if(NR==1) print $0; if($9~/a.out/) print $0}'
 PPID PID TID LWPTGIDPGRP SID TPGID COMMAND
 579046 2436128 2436128 2436128 2436128 2436128579046 2436128 ./a.out
 579046 2436128 2436129 2436129 2436128 2436128579046 2436128 ./a.out
 579046 2436128 2436130 2436130 2436128 2436128579046 2436128 ./a.out
ログイン後にコピー

pidstat-t[-ppid号]可以复印出线程之间的关系。

各类ID最后都归结到pid和lwp(tid)上。所以理解各类ID,最终归结为理解pid和lwp(tid)的联系和区别。

PID:进程ID。

LWP:线程ID。在用户态的命令(例如ps)中常用的显示方法。

TID:线程ID,等于LWP。TID在系统提供的插口函数中更常用,例如syscall(SYS_gettid)和syscall(__NR_gettid)。

TGID:线程组ID,也就是线程组leader的进程ID,等于PID。

pgid(processgroupID):进程组ID,也就是进程组leader的进程ID。

pthreadid:pthread库提供的ID,生效范围不在系统级别,可以忽视。

sid:sessionIDforthesessionleader。

TPGID:ttyprocessgroupIDfortheprocessgroupleader。

上图挺好地描述了用户视角(userview)和内核视角(kernelview)看见线程的差异:

轮询

Polling (thread au niveau utilisateur), qui est transparent pour le noyau, c'est-à-dire que le système ne connaît pas l'existence du polling. Il est entièrement planifié par le programme de l'utilisateur lui-même. Parce qu'il est contrôlé par le programme utilisateur lui-même. est difficile à utiliser. Comme la planification d'occupation, qui oblige le contrôle du processeur à passer à d'autres processus/threads, généralement seule la planification coopérative peut être effectuée. Une fois que l'interrogation elle-même a vendu activement le contrôle, d'autres interrogations peuvent être exécutées.

La différence entre goroutine et interprète

Essentiellement, goroutine est un sondage. La différence est que Golang encapsule et traite la planification des goroutines dans de nombreux aspects tels que le temps d'exécution et les appels système. Lorsqu'il rencontre une exécution à long terme ou des appels système, il vend activement le CPU (P) de la goroutine actuelle à d'autres goroutines. recrutement d'exploitation et de maintenance Linux planifié et exécuté, c'est-à-dire que Golang prend en charge l'interprète au niveau linguistique.

Planification différente des processus sous d'autres aspects

Trois principales stratégies de planification du noyau Linux :

1, SCHED_OTHER stratégie de planification de temps partagé,

2, stratégie de planification en temps réel SCHED_FIFOprocessus Linux et noyau de thread, premier arrivé, premier servi

3, stratégie de planification en temps réel SCHED_RR, rotation des tranches de temps

Planification des processus en temps réel

SCHED_FIFO : différentes priorités sont mises en veille selon la priorité la plus élevée, puis exécutées selon la priorité la plus faible ; la même priorité est premier entré, premier sorti.

SCHED_RR : différentes priorités seront mises en veille en fonction de la priorité la plus élevée, puis exécutées selon la rotation à priorité égale ;

Patch Kernel RT :

Les deux paramètres suivants

/proc/sys/kernel/sched_rt_period_us

/proc/sys/kernel/sched_rt_runtime_us

Indique que RT ne peut fonctionner que jusqu'au temps d'exécution pendant la période

Planification normale des processus

SCHED_OTHER :

CFS : Completely Fair Scheduling (nouveau noyau)

Mangrove noire, la valeur du nœud droit est supérieure à la valeur du nœud gauche

Exécutez le plus petit processus de VRuntime jusqu'à présent

Considérant à la fois le CPU/IO et nice

Trouvez toujours le calendrier des threads avec le plus petit vruntime.

vruntime=pruntime/weight×1024;

vruntime est le temps d'exécution virtuel, pruntime est le temps d'exécution chimique et le poids est déterminé par la valeur de Nice (plus la qualité est faible, plus le poids est élevé. Les threads avec moins de temps d'exécution et des valeurs de Nice inférieures auront des valeurs plus petites). vruntime et seront prioritaires pour la planification. Il s'agit d'un processus qui change dynamiquement avec l'opération.

Espace noyau et espace utilisateur Espace noyau et espace utilisateur

L'espace d'adressage virtuel de Linux va de 0 à 4G. Le noyau Linux divise cet espace d'octets de 4 Go en deux parties, et l'octet de 1 Go le plus élevé (de l'adresse virtuelle 0xCxC0000000 à 0xFFFFFFFF) est utilisé par le noyau, appelé « espace du noyau ». " ". Les 3 Go inférieurs (de l'adresse virtuelle 0x00000000 à 0xBFFFFFFF) sont utilisés par chaque processus, appelés « espace utilisateur ». Puisque chaque processus peut accéder au noyau via des appels système, le noyau Linux est partagé par tous les processus du système. du point de vue spécifique du processus, chaque processus peut disposer de 4 Go d'espace virtuel

.

Linux utilise un mécanisme de protection à deux niveaux : le niveau 0 est utilisé par le noyau et le niveau 3 est utilisé par les programmes utilisateur. Chaque processus possède son propre espace utilisateur privé (0~3G). système. Le plus élevé 1 Go d'espace de noyau virtuel est partagé par tous les processus et le noyau.

L'espace noyau stocke le code et les données du noyau, tandis que l'espace utilisateur du processus stocke le code et les données du programme utilisateur. Qu'il s'agisse de l'espace noyau ou de l'espace utilisateur, ils se trouvent tous dans l'espace virtuel. En fait, l'espace du noyau occupe l'octet le plus élevé de 1 Go de chaque espace virtuel, mais il est toujours mappé à la mémoire chimique à partir de l'adresse la plus basse (0x00000000). De plus, l'utilisation d'adresses virtuelles peut protéger efficacement l'espace du noyau contre la destruction. par espace utilisateur. L'adresse virtuelle Le processus de conversion d'adresse chimique est complété par le système d'exploitation et le CPU ensemble (le système d'exploitation définit la table des pages pour le CPU et le CPU effectue la conversion d'adresse via l'unité MMU).

Remarque : Chaque processus du système d'exploitation multitâche s'exécute dans son propre bac à sable de mémoire vidéo. Ce bac à sable est l'espace d'adressage virtuel. En mode 32 bits, il s'agit toujours d'un élément d'adresse de mémoire vidéo. Cette adresse virtuelle est mappée à la mémoire chimique via une table de pages (pagetable), qui est maintenue par le système d'exploitation et référencée par le processeur. Chaque processus possède son propre ensemble de tables de pages.

La répartition de l'espace mémoire du processus est affichée à droite :

Mémoire vidéo

Le tas descend et la pile monte (pourquoi le mien est-il l'inverse ???)

La direction descendante et le grand et petit boutisme de la pile et de la mémoire

Faible->|----------------|

|Quantités globales (toutes les quantités.data initialisées, |

|Quantité non initialisée.bss)|

Début du tas->|----------------|

|Le tas descend vers l'adresse haute|

||

||

|Espace libre|

||

||

|La pile descend jusqu'à l'adresse inférieure|

Début du high stack->|----------------|

以上がプロセスの詳細: 子プロセス、スレッド情報、および共通ステータスの表示の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:itcool.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート