首頁  >  文章  >  系統教程  >  深入了解行程:查看子行程、執行緒資訊及常見狀態

深入了解行程:查看子行程、執行緒資訊及常見狀態

PHPz
PHPz原創
2024-07-25 08:00:14333瀏覽

深入了解行程:查看子行程、執行緒資訊及常見狀態

Q&A

ps查看所有子進程? pstree-ppid

ps-eLf各數組涵義?

PID:process id 进程id
PPID: parent process id 父进程id
LWP:表示这是个线程;要么是主线程(进程),要么是线程
NLWP: num of light weight process 轻量级进程数量,即线程数量
TIME: 占用的CPU总时间
C CPU利用率,以整数表示。

查看進程下的所有執行緒cpu借助率/顯存/優先權等資訊? top-H-p25120

查看執行緒在那個CPU上運行? ps-eoruser,pid,ppid,lwp,psr,args-L|grepl3-agent

常見的進程狀態?

-D:不可被唤醒的睡眠状态,通常用于 I/O 情况。
-R:该进程正在运行。
-S:该进程处于睡眠状态,可被唤醒。
-T:停止状态,可能是在后台暂停或进程处于除错状态。
-W:内存交互状态(从 2.6 内核开始无效)。
-X:死掉的进程(应该不会出现)。
-Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。
-<:高优先级(以下状态在 BSD 格式中出现)。
-N:低优先级。
-L:被锁入内存。
-s:包含子进程。
-l:多线程(小写 L)。
-+:位于后台。

進程的六種狀態及其轉換過程(生命週期)-就緒態,運行態,深度睡眠態,淺睡眠態,停止態,殭屍態。

母子進程共享的是?不同的是? ——fork後,母子進程共享有(檔案描述子、mmap建構的映射區。其他都是複製了一份,只是該進程的進程空間甚至進程位址完全跟父進程看著一樣,而且完全是獨立的空間了)

fork與vfork的差異-vfork用於建立一個新進程,而該新進程的目的是exec一個新程式。區別一:不將父進程的位址空間複製到子進程。區別二:vfork保證子程序先運行,在它調用exec或(exit)然後父進程才可能被調度運行。

**共享顯存的位址值是在使用者進程空間範圍內麼? **fork以後子程序使用該位址和父行程是一樣的麼?若果是不同的進程,共享顯存的進程空間中顯示的位址是一樣的麼?

進程的生命週期,就緒態和運行態在數值上是相等的,都是由巨集TASK_RUNNING定義。

Linux中Task_struct是怎麼被管理的呢?

理解殭屍進程-放棄了幾乎所有顯存空間,沒有任何可執行程式碼,也不能被調度,僅僅在進程列表中保留一個位置(資源早已釋放,Task_struct結構還在)。假如他的父程序沒安裝SIGCHLD訊號處理函數呼叫wait()/waitpid()等待子程序結束,又沒有明確忽略該訊號,這麼它就仍然保持殭屍狀態,假如父進程結束了,這麼init程序手動會接手這個子進程,為它收屍LINUX社區,它還是能被消除的。假如父行程不會結束,子行程都會仍然保持殭屍狀態,這就是為何系統中有時會有好多的殭屍行程。系統所能使用的進程號是有限的(cat/proc/sys/kernel/pid_max),假如大量的形成僵死進程,將由於沒有可用的進程號而造成系統不能形成新的進程。

子進程回收:

父進程透過wait/waitpid等函數等待子進程結束,這會造成父進程掛起。假如父進程很忙,這麼可以用signal函數為SIGCHLD安裝handler,由於子進程結束後,父進程會收到該訊號,可以在handler中呼叫wait回收。假如父行程不關心子行程哪些時侯結束,這麼可以用signal(SIGCHLD,SIG_IGN)通知內核,自己對子行程的結束不感興趣,這麼子行程結束後,內核會回收,不再給父行程發送訊號還有一些方法,就是fork兩次,父進程fork一個子進程,之後繼續工作,子進程fork一個孫進程後退出,這麼孫進程被init接管,孫進程結束後,init會回收。不過子進程的回收還要自己做。

子程序結束後為何要步入殭屍狀態?--因為父行程可能要取得子程序的退出狀態等資訊。

殭屍狀態是每位子程序必經的狀態嗎?-任何一個子程序(init除外)在exit()以後,並非馬上就消失掉,而是留下一個稱為殭屍行程(Zombie)的資料結構(它佔用一點顯存資源,也就是行程表裡還有一個記錄),等待父行程處理。如果子行程在exit()以後,父行程還沒來得及處理,這時用ps指令才能看見子行程的狀態是「Z」。如果父進程在子進程結束之前退出,則子進程將由init接管。 init將會以父行程的身分處理殭屍狀態的子行程。

殭屍行程消除的方式:

改寫父進程,在子進程死後要為它收屍。具體做法是接管SIGCHLD訊號號。子進程死後,會傳送SIGCHLD訊號號給父進程,父進程收到此訊號後,執行waitpid()函式為子進程收屍。這是基於這樣的原理:即使父進程沒有呼叫wait,核心也會向它發送SIGCHLD訊息,雖然對的預設處理是忽視,假如想回應這個訊息,可以設定一個處理函數。 SIGCHLD訊號:子進程結束時,父進程會收到這個訊息。假如父行程沒有處理這個訊號,也沒有等待(wait)子程序,子程序其實中止,而且都會在核心行程表中佔有表項,這時的子程序稱為殭屍行程。此類情況我們應避開(父進程或則忽視SIGCHILD訊號,或則捕捉它,或則wait它派生的子進程,或則父進程先中止,這時子進程的中止手動由init進程來接管) 。

行程中止時exit()函數,這麼執行緒中止是哪些呢? ——線程中止的三種情況:

線程只是從啟動函數中返回,返回值是線程的退出碼。執行緒可以被同一行程中的其他執行緒取消。執行緒呼叫pthread_exit。

倘若不等待一個線程,同時對線程的回傳值不感興趣,可以設定這個執行緒為被分離狀態,讓系統在執行緒退出的時侯手動回收它所佔用的資源。一個執行緒不能自己呼叫pthread_detach改變自己為被分離狀態,只能由其他執行緒呼叫pthread_detach。

pthread_cancel()容許一個執行緒取消th指定的另一個執行緒。

進程-資源分配的最小單位,執行緒-程式執行的最小單位。進程有獨立的位址空間,執行緒沒有單獨的位址空間(同一進程內的執行緒共享進程的位址空間),一個進程崩潰後,在保護模式下不會對其它進程形成影響,而執行緒只是一個進程中的不同執行路徑。執行緒有自己的堆疊和局部變量,但執行緒沒有單獨的位址空間,一個執行緒掛掉就等於整個行程跑掉,所以多行程的程式要比多執行緒的程式強壯,但在行程切換時,花費資源較大,效率要差一些。但對於一些要求同時進行而且要共享個別變數的並發操作,只能用線程,不能用進程。

使用多執行緒的理由?

進程

進程是資源分配的基本單位,執行緒是調度的基本單位

進程資訊

Linux調度器實際上是辨識task_struct進行調度。無論行程線程,底層都對應一個task_struct,行程和執行緒的差別是共享資源的多少,兩個行程間完全不共享資源,兩個執行緒間共享所有資源。

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上。

MMU のない CPU の場合、COW とサポート フォークを適用するのは困難です。 MMU を持たない CPU は vfork を使用してプロセスを作成し、子プロセスが終了するか実行されるまで親プロセスは常にブロックされます。 vfork と fork の本質的な違いは、vfork の母プロセスと子プロセスが同じビデオ メモリ領域を共有することです。

fork(2) システムコールは、子プロセスと呼ばれる新しいプロセスを作成するために使用されます。このプロセスは親プロセスと同時に実行され (同時実行)、実行順序は不定です (非同期)。 pid_t はマクロ定義であり、その本質は int です。成功すると 2 つの値が返され、子プロセスは 0 を返し、それ以外の場合はエラーで -1 が返されます。

ミューテックスロックのステータスを含む、親の仮想アドレス空間全体が子プロセスにコピーされます。

次の点を除いて、子プロセスは親プロセスとまったく同じです:

wait() と waitpid() をリサイクルするプロセス

waitpid()/wait() を通じて子プロセスの task_struct 構造体をリサイクルします。

違い:

孤立プロセスとゾンビプロセス ゾンビプロセス

ゾンビプロセス: プロセスが fork を使用して子プロセスを作成し、子プロセスが終了し、親プロセスが wait または waitpid を呼び出して子プロセスのステータス情報を取得しない場合、子プロセスのプロセス記述子は常に次の場所に保存されます。システム。これらのプロセスはゾンビ プロセスと呼ばれます。 (子プロセスは /proc/$pid を通じて確認できますが、スレッドは確認できません)

プロセスがその寿命を終了するために exit コマンドを呼び出すと、実際には破壊されませんが、ゾンビ プロセス (Zombie) と呼ばれるデータ構造が残ります (システムは exit を呼び出し、その機能はプロセスを終了させることですが、通常のプロセスをゾンビ プロセスに変えることのみに限定されており、完全に破壊することはできません)。

Linux プロセスの状態では、ゾンビ プロセスはすでにほとんどすべてのビデオ メモリ領域を放棄しており、実行可能コードを持たず、プロセス リスト内の位置を保持するだけです。さらに、ゾンビ プロセスがビデオ メモリ領域を占有することがなくなり、プロセスの終了ステータスやその他の情報を収集できるようになります。死体を収集するには親プロセスが必要です。

親プロセスが SIGCHLD シグナル処理関数をインストールせず、wait または waitpid() を呼び出して子プロセスが終了するのを待ち、シグナルを明示的に無視しない場合、親プロセスはゾンビ状態のままになります。この時点でプロセスが終了すると、init プロセスが手動でこのサブプロセスを引き継ぎ、その死体を収集しますが、それでも削除することができます。

そして、親プロセスがループしていて終了しない場合、子プロセスはゾンビ状態のままになります。これが、システム内に多くのゾンビプロセスが存在する理由です。システムが使用できるプロセス番号には制限があります。ゾンビプロセスが大量に形成されると、使用可能なプロセス番号がなくなるため、システムは新しいプロセスを形成できなくなります。

ゾンビプロセスの原因: 子プロセスが終了すると、親プロセスに SIGCHLD シグナルが送信されますが、親プロセスはデフォルトでそれを無視します。親プロセスは待機するために wait() または waitpid() 関数を呼び出しません。子プロセスの終了。ゾンビプロセスを防ぐ方法: 親プロセスは wait()/waitpid() を呼び出して子プロセスが終了するのを待ちます。この方法では通常、親プロセスは待機中にブロックされ、他の処理ができなくなります。 SIGCHLD シグナルをキャプチャし、シグナル処理関数上で wait 関数を呼び出すことで、1.で述べた問題を回避できます。 2 回フォークすると、親プロセスは父親プロセスを作成し、父親プロセスは息子プロセスを作成します。その後、母親プロセスは自殺し、息子プロセスは孤立プロセスとなり、init プロセスに取り込まれます。孤立したプロセス

孤立プロセス: 親プロセスは終了しますが、その 1 つ以上の子プロセスがまだ実行されているため、これらの子プロセスは孤立プロセスになります。孤立プロセスは init プロセス (pid=1) によって取り込まれ、init プロセスはそれらのステータスの収集を完了します。 (孤立プロセスがシステムに表示される場合は、メインプロセスが終了する前に子プロセスをクリアしなかったことを意味します)

スレッド (軽量プロセス、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 &#039;{if(NR==1) print $0; if($9~/a.out/) print $0}&#039;
 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 (benang peringkat pengguna) adalah telus kepada kernel, iaitu, sistem tidak mengetahui kewujudan pengundian sepenuhnya dijadualkan oleh program pengguna itu sendiri, kerana ia dikawal oleh program pengguna itu sendiri kepada Seperti menduduki penjadualan, yang memaksa kawalan CPU untuk beralih kepada proses/benang lain, secara amnya hanya penjadualan koperasi boleh dilakukan Selepas pengundian itu sendiri secara aktif menjual kawalan, pengundian lain boleh dilaksanakan.

Perbezaan antara goroutine dan interpreter

Pada asasnya, goroutine adalah pengundian. Perbezaannya ialah Golang merangkum dan memproses penjadualan goroutine dalam banyak aspek seperti masa jalan dan panggilan sistem Apabila ia menghadapi pelaksanaan jangka panjang atau panggilan sistem, ia akan secara aktif menjual CPU (P) goroutine semasa boleh operasi dan penyelenggaraan Linux yang dijadualkan dan dilaksanakan, iaitu, Golang menyokong penterjemah dari peringkat bahasa.

Penjadualan proses yang berbeza dalam aspek lain

Tiga strategi penjadualan utama kernel Linux:

1, SCHED_OTHER strategi penjadualan perkongsian masa,

2, strategi penjadualan masa nyata SCHED_FIFOproses linux dan inti utas, siapa cepat dia dapat

3, strategi penjadualan masa nyata SCHED_RR, putaran kepingan masa

Penjadualan proses masa nyata

SCHED_FIFO: Keutamaan yang berbeza dialihkan ke tidur dahulu mengikut keutamaan yang lebih tinggi, dan kemudian dijalankan mengikut keutamaan yang lebih rendah, keutamaan yang sama ialah masuk dahulu, keluar dahulu.

SCHED_RR: Keutamaan yang berbeza dipindahkan ke tidur terlebih dahulu mengikut keutamaan yang lebih tinggi, dan kemudian dijalankan mengikut keutamaan yang lebih rendah;

Tampalan RT kernel:

Dua parameter berikut

/proc/sys/kernel/sched_rt_period_us

/proc/sys/kernel/sched_rt_runtime_us

Menunjukkan bahawa dalam tempoh tersebut, RT hanya boleh berjalan sehingga masa runtime

Penjadualan proses biasa

SCHED_OTHER:

CFS: Penjadualan Adil Sepenuhnya (irung baharu)

Paya bakau hitam, nilai nod kanan lebih besar daripada nilai nod kiri

Jalankan proses terkecil vruntime setakat ini

Memandangkan kedua-dua CPU/IO dan bagus

Sentiasa cari jadual urutan dengan vruntime terkecil.

vruntime=pruntime/weight×1024;

vruntime ialah masa larian maya, pruntime ialah masa larian kimia, dan berat ditentukan oleh nilai bagus (semakin rendah keelokkan, semakin tinggi berat Benang dengan masa larian yang kurang dan nilai bagus yang rendah akan mempunyai masa vrun yang lebih kecil). akan dijadualkan dahulu. Ini adalah proses yang berubah secara dinamik dengan operasi.

Ruang kernel dan ruang pengguna Ruang kernel dan ruang pengguna

Linux の仮想アドレス空間は 0 ~ 4G の範囲にあり、Linux カーネルはこの 4G バイトの空間を 2 つの部分に分割し、上位の 1G バイト (仮想アドレス 0xCxC0000000 から 0xFFFFFFFF) がカーネルによって使用されます。これは「カーネル空間」と呼ばれます。 「」。下位 3G バイト (仮想アドレス 0x00000000 から 0xBFFFFFFF) は、「ユーザー空間」と呼ばれる各プロセスによって使用されます。各プロセスはシステム コールを通じてカーネルにステップインできるため、Linux カーネルはシステム内のすべてのプロセスによって共有されます。特定のプロセスの観点から見ると、各プロセスは 4G バイトの仮想空間

を持つことができます。

Linux は 2 レベルの保護メカニズムを使用します。レベル 0 はカーネルによって使用され、レベル 3 はユーザー プログラムによって使用されます。各プロセスには独自のプライベート ユーザー スペース (0 ~ 3G) があり、このスペースは他のプロセスからは見えません。最高の 1GB の仮想カーネル空間がすべてのプロセスとカーネルによって共有されます。

カーネル空間にはカーネルのコードとデータが保存され、プロセスのユーザー空間にはユーザー プログラムのコードとデータが保存されます。カーネル空間であれ、ユーザー空間であれ、それらはすべて仮想空間内にあります。実際、カーネル空間は各仮想空間の最上位 1GB バイトを占有しますが、常に最下位アドレス (0x00000000) からケミカル メモリにマッピングされます。さらに、仮想アドレスを使用すると、カーネル空間が破壊されるのを効果的に保護できます。ユーザー空間による仮想アドレス 化学アドレス変換プロセスは、オペレーティング システムと CPU によって一緒に完了します (オペレーティング システムが CPU にページ テーブルを設定し、CPU が MMU ユニットを介してアドレス変換を実行します)。

注: マルチタスク オペレーティング システムの各プロセスは、独自のビデオ メモリ サンドボックスで実行されます。このサンドボックスは、32 ビット モードでは常に 4 GB のビデオ メモリ アドレスです。この仮想アドレスは、オペレーティング システムによって維持され、プロセッサによって参照されるページ テーブル (ページテーブル) を通じて化学メモリにマッピングされます。各プロセスには独自のページ テーブルのセットがあります。

プロセスのメモリ空間の分布を右側に示します:

ビデオメモリ

ヒープが下がり、スタックが上がる(なぜ私の場合は逆なのでしょうか??)

スタックとメモリの降順方向と大小エンディアン

低->|----------------|

|グローバル数量(すべての初期化された数量.data、|

|初期化されていない数量.bss)|

ヒープ開始->|----------------|

|ヒープは上位アドレスに向かってドロップします|

||

||

|フリースペース|

||

||

|スタックは下位アドレスに移動|

ハイスタック開始->|----------------|

以上是深入了解行程:查看子行程、執行緒資訊及常見狀態的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn