一、進(jìn)程和線程的概念
1.進(jìn)程和線程的定義
進(jìn)程并不只是一段可以運(yùn)行的代碼顿痪,也包含了運(yùn)行代碼所需要的資源镊辕。
在操作系統(tǒng)來(lái)看,進(jìn)程是資源管理的最小單元员魏,而我們又知道丑蛤,線程是程序執(zhí)行的最小單元。
話說(shuō)回來(lái)撕阎,Linux系統(tǒng)至少有一個(gè)進(jìn)程,一個(gè)程序可以對(duì)應(yīng)多個(gè)進(jìn)程碌补,一個(gè)進(jìn)程只能對(duì)應(yīng)一個(gè)程序虏束,一個(gè)進(jìn)程包含一個(gè)或多個(gè)線程。
所以厦章,一個(gè)進(jìn)程的組成實(shí)體實(shí)際是兩大部分:資源的集合和線程的集合镇匀。進(jìn)程中的線程是動(dòng)態(tài)的對(duì)象, 代表了進(jìn)程指令的執(zhí)行。資源袜啃,包括地址空間汗侵、打開(kāi)的文件、用戶(hù)信息等等群发,由進(jìn)程內(nèi)的線程共享晰韵。線程有自己的私有數(shù)據(jù):程序計(jì)數(shù)器,検旒耍空間以及寄存器雪猪。
總結(jié)來(lái)說(shuō),在linux系統(tǒng)下起愈,進(jìn)程主要具有以下四個(gè)要素:
1)有一個(gè)程序供其運(yùn)行只恨。這段程序不一定是進(jìn)程所專(zhuān)有,可以與其他進(jìn)程一起使用抬虽;
2)有起碼的“私有財(cái)產(chǎn)”官觅,這就是進(jìn)程專(zhuān)用的系統(tǒng)堆棧空間阐污;
3)有“身份證”休涤,也就是task_struct結(jié)構(gòu),也稱(chēng)之為“進(jìn)程控制塊”(PCB)疤剑。有了這個(gè)數(shù)據(jù)結(jié)構(gòu)滑绒,進(jìn)程才能成為內(nèi)核調(diào)度的一個(gè)基本單位接受內(nèi)核的調(diào)度闷堡。同時(shí),這個(gè)結(jié)構(gòu)又是進(jìn)程的“財(cái)產(chǎn)登記卡”疑故,記錄著進(jìn)程占用的各項(xiàng)資源杠览。
4)有獨(dú)立的存儲(chǔ)空間,意味著擁有專(zhuān)有的用戶(hù)空間纵势;還意味著除前述的系統(tǒng)空間堆棧外還有其專(zhuān)有的用戶(hù)空間堆棧踱阿。(PS:進(jìn)程的系統(tǒng)空間是不能獨(dú)立的,除了各進(jìn)程獨(dú)有的系統(tǒng)堆椙仗空間外软舌,任何進(jìn)程都不可能直接改變用戶(hù)空間的內(nèi)容)。
以上條件是必要條件牛曹,缺少其中一條佛点,都不能稱(chēng)其為“進(jìn)程”。如果只缺第四條黎比,那就稱(chēng)為“線程”超营。
在linux系統(tǒng)中,“進(jìn)程”和“任務(wù)”是同一個(gè)意思阅虫,在內(nèi)核的代碼中逞荼眨混用這兩個(gè)名詞和概念。例如每個(gè)進(jìn)程都要有一個(gè)task_struct數(shù)據(jù)結(jié)構(gòu)颓帝,而其號(hào)碼卻又是pid米碰、喚醒一個(gè)睡眠進(jìn)程的函數(shù)名為wake_up_process()。
之所以有這樣的情況是因?yàn)楣撼牵琹inux源自Unix和i386系統(tǒng)結(jié)構(gòu)吕座,而unix中的進(jìn)程在Intel的技術(shù)資料中稱(chēng)為“任務(wù)”,嚴(yán)格來(lái)說(shuō)有點(diǎn)區(qū)別工猜,但是對(duì)于系統(tǒng)的實(shí)現(xiàn)來(lái)說(shuō)是一回事米诉。
2.task_struct的定義
操作系統(tǒng)通過(guò)一個(gè)稱(chēng)作PCB(Process Control Block,進(jìn)程控制塊)的數(shù)據(jù)結(jié)構(gòu)管理一個(gè)進(jìn)程篷帅,也稱(chēng)為tesk_struct結(jié)構(gòu)體史侣,這個(gè)結(jié)構(gòu)體包含了一個(gè)進(jìn)程所需的所有信息。它定義在linux-2.6.38.8/include/linux/sched.h文件中魏身。
除了最起碼的“財(cái)產(chǎn)”惊橱,即task_struct數(shù)據(jù)結(jié)構(gòu)和系統(tǒng)堆棧之外,一個(gè)進(jìn)程還要有一些附加的資源箭昵。例如税朴,進(jìn)程擁有堵路的存儲(chǔ)空間,就要有用于虛擬內(nèi)存管理的mm_struct數(shù)據(jù)結(jié)構(gòu)以及附屬的vm_area數(shù)據(jù)結(jié)構(gòu),以及相應(yīng)的頁(yè)面目錄項(xiàng)和頁(yè)面表正林,
但這些都從屬于task_struct資源泡一。task_struct數(shù)據(jù)結(jié)構(gòu)在這方面相當(dāng)于登記卡的作用,其具體結(jié)構(gòu)源代碼如下:
struct task_struct
{
? ? /*? ? * offsets of these are hardcoded elsewhere - touch with care
? ? */volatilelongstate;/* -1 unrunnable, 0 runnable, >0 stopped */? ? unsigned longflags;/* per process flags, defined below */int sigpending;
? ? mm_segment_t addr_limit;? /* thread address space:
? ? 0-0xBFFFFFFF for user-thead
? ? 0-0xFFFFFFFF for kernel-thread
? ? */structexec_domain *exec_domain;
? ? volatilelong need_resched;
? ? unsigned long ptrace;
? ? intlock_depth;/* Lock depth *//*? ? * offset 32 begins here on 32-bit platforms. We keep
? ? * all fields in a single cacheline that are needed for
? ? * the goodness() loop in schedule().
? ? */long counter;
? ? long nice;
? ? unsigned long policy;
? ? structmm_struct *mm;
? ? int has_cpu, processor;
? ? unsigned long cpus_allowed;
? ? struct list_head run_list;
? ? unsigned long sleep_time;
? ? structtask_struct *next_task, *prev_task;
? ? structmm_struct *active_mm;
? ? /* task state */structlinux_binfmt *binfmt;
? ? int exit_code, exit_signal;
? ? intpdeath_signal;/*? The signal sent when the parent dies? */? ?
? ? unsigned long personality;
? ? intdumpable:1;
? ? intdid_exec:1;
? ? pid_t pid;
? ? pid_t pgrp;
? ? pid_t tty_old_pgrp;
? ? pid_t session;
? ? pid_t tgid;
? ? /* boolean value for session group leader */int leader;
? ? /*? ? * pointers to (original) parent process, youngest child, younger sibling,
? ? * older sibling, respectively.? (p->father can be replaced with
? ? * p->p_pptr->pid)
? ? */structtask_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
? ? struct list_head thread_group;
? ? /* PID hash table linkage. */structtask_struct *pidhash_next;
? ? structtask_struct **pidhash_pprev;
? ? wait_queue_head_t wait_chldexit; /* for wait4() */structsemaphore *vfork_sem;/* for vfork() */? ? unsigned long rt_priority;
? ? unsigned long it_real_value, it_prof_value, it_virt_value;
? ? unsigned long it_real_incr, it_prof_incr, it_virt_incr;
? ? struct timer_list real_timer;
? ? struct tms times;
? ? unsigned long start_time;
? ? long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
? ? /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */? ? unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
? ? intswappable:1;
? ? /* process credentials */? ? uid_t uid,euid,suid,fsuid;
? ? gid_t gid,egid,sgid,fsgid;
? ? int ngroups;
? ? gid_t groups[NGROUPS];
? ? kernel_cap_t? cap_effective, cap_inheritable, cap_permitted;
? ? intkeep_capabilities:1;
? ? structuser_struct *user;
? ? /* limits */struct rlimit rlim[RLIM_NLIMITS];
? ? unsigned short used_math;
? ? charcomm[16];
? ? /* file system info */int link_count;
? ? structtty_struct *tty;/* NULL if no tty */? ? unsigned intlocks;/* How many file locks are being held *//* ipc stuff */structsem_undo *semundo;
? ? structsem_queue *semsleeping;
? ? /* CPU-specific state of this task */struct thread_struct thread;
? ? /* filesystem information */structfs_struct *fs;
? ? /* open file information */structfiles_struct *files;
? ? /* signal handlers */? ? spinlock_t sigmask_lock; /* Protects signal and blocked */structsignal_struct *sig;
? ? sigset_t blocked;
? ? struct sigpending pending;
? ? unsigned long sas_ss_sp;
? ? size_t sas_ss_size;
? ? int(*notifier)(void*priv);
? ? void*notifier_data;
? ? sigset_t *notifier_mask;
? ? /* Thread group tracking */? ? u32 parent_exec_id;
? ? u32 self_exec_id;
? ? /* Protection of (de-)allocation: mm, files, fs, tty */? ? spinlock_t alloc_lock;
};
下面對(duì)結(jié)構(gòu)中幾個(gè)重要的成分做介紹:
1)state(第6行)
該變量表示進(jìn)程當(dāng)前運(yùn)行的狀態(tài)觅廓,具體定義如下:
1#define TASK_RUNNING? ? ? ? ? ? ? 02#define TASK_INTERRUPTIBLE? ? ? ? 13#define TASK_UNINTERRUPTIBLE? ? ? 24#define TASK_ZOMBIE? ? ? ? ? ? ? 4//僵尸進(jìn)程5#define TASK_STOPPED? ? ? ? ? ? ? 8
狀態(tài)TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE均表示進(jìn)程處于睡眠狀態(tài)鼻忠。但是TASK_UNINTERRUPTIBLE表示進(jìn)程處于“深度睡眠”,而不受“信號(hào)”(signal杈绸,也稱(chēng)軟中斷)的打擾帖蔓,而TASK_INTERRUPTIBLE則可以因信號(hào)的到來(lái)而被喚醒。內(nèi)核中提供了不同的函數(shù)瞳脓,讓一個(gè)進(jìn)程進(jìn)入不同深度的睡眠或?qū)⑦M(jìn)程從睡眠中喚醒塑娇。具體地說(shuō),函數(shù)sleep_on()和wake_up()用于深度睡眠劫侧,而interruptible_sleep_on()和wake_up_interruptible()則用于淺度睡眠埋酬。深度睡眠一般只用于臨界區(qū)和關(guān)鍵性的部位,而“可中斷”的睡眠那就是通用的了板辽。特別地奇瘦,當(dāng)進(jìn)程在“阻塞性”的系統(tǒng)調(diào)用中等待某一事件發(fā)生時(shí),應(yīng)該進(jìn)入可中斷睡眠劲弦,否則就不能對(duì)別的中斷做出反應(yīng),別的進(jìn)程就不能通過(guò)發(fā)一個(gè)信號(hào)來(lái)“殺掉”這個(gè)進(jìn)程了醇坝。
TASK_RUNNING狀態(tài)并不是表示一個(gè)進(jìn)程正在執(zhí)行中邑跪,或者說(shuō)這個(gè)進(jìn)程就是“當(dāng)前進(jìn)程”,而是表示這個(gè)進(jìn)程可以被調(diào)度執(zhí)行而成為當(dāng)前進(jìn)程呼猪。當(dāng)進(jìn)程處于這樣的可執(zhí)行(或就緒)狀態(tài)時(shí)画畅,內(nèi)核就將該進(jìn)程的task_struct結(jié)構(gòu)通過(guò)其隊(duì)列頭(見(jiàn)第30行)掛入一個(gè)“運(yùn)行隊(duì)列”。
TASK_ZOMBIE狀態(tài)表示進(jìn)程已經(jīng)“去世”而戶(hù)口尚未注銷(xiāo)宋距。
TASK_STOPPED主要用于調(diào)試的目的轴踱,進(jìn)程接收到 一個(gè)SIGSTOP信號(hào)后就將運(yùn)行狀態(tài)改成???? TASK_STOPPED而進(jìn)入“掛起”狀態(tài),然后在接收到SIGCONT信號(hào)時(shí)又恢復(fù)繼續(xù)運(yùn)行谚赎。
2)flags(第7行)
flags反應(yīng)進(jìn)程狀態(tài)相關(guān)信息淫僻,但并不是運(yùn)行狀態(tài),而是與管理有關(guān)的其他信息壶唤。
1#define PF_ALIGNWARN? ? ? ? 0x00000001? ? ? /*print alignment warning msgs*/
2#define PF_STARTING? ? ? ? 0x00000002? ? ? /*being created*/
3#define PF_EXITING? ? ? ? ? 0x00000004? ? ? /*getting shut down*/
4#define PF_FORKNOEXEC? ? ? 0x00000040? ? ? /*forked but did not exec*/
5#define PF_SUPERPRIV? ? ? ? 0x00000100? ? ? /*uses super-user privileges*/
6#define PF_DUMPCORE? ? ? ? 0x00000200? ? ? /*dumped core*/
7#define PF_SIGNALED? ? ? ? 0x00000400? ? ? /*killed by signal*/
8#define PF_MEMALLOC? ? ? ? 0x00000800? ? ? /*Allocating memory*/
9#define PF_VFORK? ? ? ? ? ? 0x00001000? ? ? /*wake up parent in mm_release*/
10#define PF_USEDFPU? ? ? ? ? 0x00100000? ? ? /*task used FPU this quantum(SMP)*/
3)sigpending(第8行)
表示進(jìn)程收到了“信號(hào)”但是尚未處理雳灵。
4)counter(第23行)
與進(jìn)程調(diào)度有關(guān)
5)add_limit
虛擬地址空間的上限,對(duì)進(jìn)程而言是其用戶(hù)空間的上限闸盔,所以是0xbfff ffff;對(duì)內(nèi)核線程而言則是系統(tǒng)空間額的上限悯辙,所以是0xffff ffff
6)binfnt
應(yīng)用程序的文件格式。
7)pgrp,session,leader
當(dāng)一個(gè)用戶(hù)登錄時(shí),就開(kāi)始了一個(gè)進(jìn)程組(session)躲撰,此后創(chuàng)建的進(jìn)程都屬于這同一個(gè)session针贬。
8)user
指向一個(gè)user_struct結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)代表進(jìn)程所屬的用戶(hù)拢蛋。
9)rlim
這是一個(gè)結(jié)構(gòu)數(shù)組桦他,表明進(jìn)程歲各種資源的使用數(shù)量所受的限制。
3.task_struct如何在linux中被管理
task_struct可以以三種方式被管理瓤狐,他們分別是:樹(shù)瞬铸,哈希表和鏈表,具體如下圖础锐,其中圓代表一個(gè)個(gè)進(jìn)程的task_struct嗓节。
二、進(jìn)程的生命周期
進(jìn)程是一個(gè)動(dòng)態(tài)的實(shí)體皆警,所以他是有生命的拦宣。從創(chuàng)建到消亡,是一個(gè)進(jìn)程的整個(gè)生命周期信姓。在這個(gè)周期中鸵隧,進(jìn)程可能會(huì)經(jīng)歷各種不同的狀態(tài)。一般來(lái)說(shuō)意推,所有進(jìn)程都要經(jīng)歷以下的3個(gè)狀態(tài):
就緒態(tài)豆瘫。指進(jìn)程已經(jīng)獲得所有所需的其他資源,正在申請(qǐng)?zhí)幚硖幚砥髻Y源菊值,準(zhǔn)備開(kāi)始執(zhí)行外驱。這種情況下糠涛,稱(chēng)進(jìn)程處于就緒態(tài)助赞。
阻塞態(tài)。指進(jìn)程因?yàn)樾枰却栀Y源而放棄處理器御吞,或者進(jìn)程本不擁有處理器儿子,且其他資源也沒(méi)有滿(mǎn)足瓦哎,從而即使得到處理器也不能開(kāi)始運(yùn)行。這種情況下柔逼,進(jìn)程處于阻塞態(tài)蒋譬。阻塞狀態(tài)也稱(chēng)休眠狀態(tài)或者等待狀態(tài)。
運(yùn)行態(tài)卒落。進(jìn)程得到了處理器羡铲,并不需要等待其他任何資源,正在執(zhí)行的狀態(tài)儡毕,稱(chēng)之為運(yùn)行態(tài)也切。只有在運(yùn)行態(tài)時(shí)扑媚,進(jìn)程才可以使用所申請(qǐng)到的資源。
在Linux系統(tǒng)中雷恃,將各種狀態(tài)進(jìn)行了重新組織疆股,由此得到了Linux進(jìn)程的幾個(gè)狀態(tài):
RUNNING:正在運(yùn)行或者在就緒隊(duì)列中等待運(yùn)行的進(jìn)程。也就是上面提到的運(yùn)行態(tài)和就緒態(tài)進(jìn)程的綜合倒槐。一個(gè)進(jìn)程處于RUNNING狀態(tài)旬痹,并不代表他一定在被執(zhí)行。由于在多任務(wù)系統(tǒng)中讨越,各個(gè)就緒進(jìn)程需要并發(fā)執(zhí)行两残,所以在某個(gè)特定時(shí)刻,這些處于RUNNING狀態(tài)的進(jìn)程之中把跨,只有一個(gè)能得到處理器人弓,而其他進(jìn)程必須在一個(gè)就緒隊(duì)列中等待。即使是在多處理器的系統(tǒng)中着逐,Linux也只能同時(shí)讓一個(gè)處理器執(zhí)行任務(wù)崔赌。
UNINTERRUPTABLE:不可中斷阻塞狀態(tài)。處于這種狀態(tài)的進(jìn)程正在等待隊(duì)列中耸别,當(dāng)資源有效時(shí)健芭,可由操作系統(tǒng)進(jìn)行喚醒,否則秀姐,將一直處于等待狀態(tài)慈迈。
INTERRUPTABLE:可中斷阻塞狀態(tài)。與不可中斷阻塞狀態(tài)一樣省有,處于這種狀態(tài)的進(jìn)程在等待隊(duì)列中吩翻,當(dāng)資源有效時(shí),可以有操作系統(tǒng)進(jìn)行喚醒锥咸。與不可中斷阻塞狀態(tài)有所區(qū)別的是,處于此狀態(tài)中的進(jìn)程亦可被其他進(jìn)程的信號(hào)喚醒细移。
STOPPED:掛起狀態(tài)搏予。進(jìn)程被暫停,需要通過(guò)其它進(jìn)程的信號(hào)才能被喚醒弧轧。導(dǎo)致這種狀態(tài)的原因有兩種雪侥。其一是受到相關(guān)信號(hào)(SIGSTOP,SIGSTP,SIGTTIN或SIGTTOU)的反應(yīng)。其二是受到父進(jìn)程ptrace調(diào)用的控制精绎,而暫時(shí)將處理器交給控制進(jìn)程速缨。
ZOMBIE:僵尸狀態(tài)。表示進(jìn)程結(jié)束但尚未消亡的一種狀態(tài)代乃。此時(shí)進(jìn)程已經(jīng)結(jié)束運(yùn)行并釋放掉大部分資源旬牲,但尚未釋放進(jìn)程控制塊仿粹。
Linux進(jìn)程狀態(tài):T (TASK_STOPPED or TASK_TRACED),暫停狀態(tài)或跟蹤狀態(tài)原茅。
???????? 向進(jìn)程發(fā)送一個(gè)SIGSTOP信號(hào)吭历,它就會(huì)因響應(yīng)該信號(hào)而進(jìn)入TASK_STOPPED狀態(tài)(除非該進(jìn)程本身處于TASK_UNINTERRUPTIBLE狀態(tài)而不響應(yīng)信號(hào))。(SIGSTOP與SIGKILL信號(hào)一樣擂橘,是非常強(qiáng)制的晌区。不允許用戶(hù)進(jìn)程通過(guò)signal系列的系統(tǒng)調(diào)用重新設(shè)置對(duì)應(yīng)的信號(hào)處理函數(shù)。) ???????? 向進(jìn)程發(fā)送一個(gè)SIGCONT信號(hào)通贞,可以讓其從TASK_STOPPED狀態(tài)恢復(fù)到TASK_RUNNING狀態(tài)朗若。
???????? 當(dāng)進(jìn)程正在被跟蹤時(shí),它處于TASK_TRACED這個(gè)特殊的狀態(tài)昌罩】扌福“正在被跟蹤”指的是進(jìn)程暫停下來(lái),等待跟蹤它的進(jìn)程對(duì)它進(jìn)行操作峡迷。比如在gdb中對(duì)被跟蹤的進(jìn)程下一個(gè)斷點(diǎn)银伟,進(jìn)程在斷點(diǎn)處停下來(lái)的時(shí)候就處于TASK_TRACED狀態(tài)。而在其他時(shí)候绘搞,被跟蹤的進(jìn)程還是處于前面提到的那些狀態(tài)彤避。
???????? 對(duì)于進(jìn)程本身來(lái)說(shuō),TASK_STOPPED和TASK_TRACED狀態(tài)很類(lèi)似夯辖,都是表示進(jìn)程暫停下來(lái)琉预。 而TASK_TRACED狀態(tài)相當(dāng)于在TASK_STOPPED之上多了一層保護(hù),處于TASK_TRACED狀態(tài)的進(jìn)程不能響應(yīng)SIGCONT信號(hào)而被喚醒蒿褂。只能等到調(diào)試進(jìn)程通過(guò)ptrace系統(tǒng)調(diào)用執(zhí)行PTRACE_CONT圆米、PTRACE_DETACH等操作(通過(guò)ptrace系統(tǒng)調(diào)用的參數(shù)指定操作),或調(diào)試進(jìn)程退出啄栓,被調(diào)試的進(jìn)程才能恢復(fù)TASK_RUNNING狀態(tài)娄帖。
???????? Linux進(jìn)程狀態(tài):Z (TASK_DEAD - EXIT_ZOMBIE),退出狀態(tài)昙楚,進(jìn)程成為僵尸進(jìn)程近速。
???????? 進(jìn)程在退出的過(guò)程中,處于TASK_DEAD狀態(tài)堪旧。
???????? 在這個(gè)退出過(guò)程中削葱,進(jìn)程占有的所有資源將被回收,除了task_struct結(jié)構(gòu)(以及少數(shù)資源)以外淳梦。于是進(jìn)程就只剩下task_struct這么個(gè)空殼析砸,故稱(chēng)為僵尸。 ???????? 之所以保留task_struct爆袍,是因?yàn)閠ask_struct里面保存了進(jìn)程的退出碼首繁、以及一些統(tǒng)計(jì)信息作郭。而其父進(jìn)程很可能會(huì)關(guān)心這些信息。比如在shell中蛮瞄,$?變量就保存了最后一個(gè)退出的前臺(tái)進(jìn)程的退出碼所坯,而這個(gè)退出碼往往被作為if語(yǔ)句的判斷條件。 ???????? 當(dāng)然挂捅,內(nèi)核也可以將這些信息保存在別的地方芹助,而將task_struct結(jié)構(gòu)釋放掉,以節(jié)省一些空間闲先。但是使用task_struct結(jié)構(gòu)更為方便状土,因?yàn)樵趦?nèi)核中已經(jīng)建立了從pid到task_struct查找關(guān)系,還有進(jìn)程間的父子關(guān)系伺糠。釋放掉task_struct蒙谓,則需要建立一些新的數(shù)據(jù)結(jié)構(gòu),以便讓父進(jìn)程找到它的子進(jìn)程的退出信息训桶。
???????? 父進(jìn)程可以通過(guò)wait系列的系統(tǒng)調(diào)用(如wait4累驮、waitid)來(lái)等待某個(gè)或某些子進(jìn)程的退出,并獲取它的退出信息舵揭。然后wait系列的系統(tǒng)調(diào)用會(huì)順便將子進(jìn)程的尸體(task_struct)也釋放掉谤专。 ???????? 子進(jìn)程在退出的過(guò)程中,內(nèi)核會(huì)給其父進(jìn)程發(fā)送一個(gè)信號(hào)午绳,通知父進(jìn)程來(lái)“收尸”置侍。這個(gè)信號(hào)默認(rèn)是SIGCHLD,但是在通過(guò)clone系統(tǒng)調(diào)用創(chuàng)建子進(jìn)程時(shí)拦焚,可以設(shè)置這個(gè)信號(hào)蜡坊。
???????? 通過(guò)下面的代碼能夠制造一個(gè)EXIT_ZOMBIE狀態(tài)的進(jìn)程:
#include? ? void main() {? ? if (fork())? ? while(1) sleep(100);? ?
}
編譯運(yùn)行,然后ps一下:
kouu@kouu-one:~/test$ ps -ax | grep a\.out?
10410 pts/0??? S+???? 0:00 ./a.out?
10411 pts/0??? Z+???? 0:00 [a.out]??
10413 pts/1??? S+???? 0:00 grep a.out
???????? 只要父進(jìn)程不退出赎败,這個(gè)僵尸狀態(tài)的子進(jìn)程就一直存在秕衙。那么如果父進(jìn)程退出了呢,誰(shuí)又來(lái)給子進(jìn)程“收尸”僵刮? 當(dāng)進(jìn)程退出的時(shí)候灾梦,會(huì)將它的所有子進(jìn)程都托管給別的進(jìn)程(使之成為別的進(jìn)程的子進(jìn)程)。托管給誰(shuí)呢妓笙?可能是退出進(jìn)程所在進(jìn)程組的下一個(gè)進(jìn)程(如果存在的話),或者是1號(hào)進(jìn)程能岩。所以每個(gè)進(jìn)程寞宫、每時(shí)每刻都有父進(jìn)程存在。除非它是1號(hào)進(jìn)程拉鹃。
1號(hào)進(jìn)程辈赋,pid為1的進(jìn)程鲫忍,又稱(chēng)init進(jìn)程。 linux系統(tǒng)啟動(dòng)后钥屈,第一個(gè)被創(chuàng)建的用戶(hù)態(tài)進(jìn)程就是init進(jìn)程悟民。它有兩項(xiàng)使命: 1、執(zhí)行系統(tǒng)初始化腳本篷就,創(chuàng)建一系列的進(jìn)程(它們都是init進(jìn)程的子孫)射亏; 2、在一個(gè)死循環(huán)中等待其子進(jìn)程的退出事件竭业,并調(diào)用waitid系統(tǒng)調(diào)用來(lái)完成“收尸”工作智润; init進(jìn)程不會(huì)被暫停、也不會(huì)被殺死(這是由內(nèi)核來(lái)保證的)未辆。它在等待子進(jìn)程退出的過(guò)程中處于TASK_INTERRUPTIBLE狀態(tài)窟绷,“收尸”過(guò)程中則處于TASK_RUNNING狀態(tài)。
???????? Linux進(jìn)程狀態(tài):X (TASK_DEAD - EXIT_DEAD)咐柜,退出狀態(tài)兼蜈,進(jìn)程即將被銷(xiāo)毀。
???????? 而進(jìn)程在退出過(guò)程中也可能不會(huì)保留它的task_struct拙友。比如這個(gè)進(jìn)程是多線程程序中被detach過(guò)的進(jìn)程(進(jìn)程为狸?線程?參見(jiàn)《linux線程淺析》)献宫≡科剑或者父進(jìn)程通過(guò)設(shè)置SIGCHLD信號(hào)的handler為SIG_IGN,顯式的忽略了SIGCHLD信號(hào)姊途。(這是posix的規(guī)定涉瘾,盡管子進(jìn)程的退出信號(hào)可以被設(shè)置為SIGCHLD以外的其他信號(hào)。) ???????? 此時(shí)捷兰,進(jìn)程將被置于EXIT_DEAD退出狀態(tài)立叛,這意味著接下來(lái)的代碼立即就會(huì)將該進(jìn)程徹底釋放。所以EXIT_DEAD狀態(tài)是非常短暫的贡茅,幾乎不可能通過(guò)ps命令捕捉到秘蛇。
???????? 進(jìn)程的初始狀態(tài)
???????? 進(jìn)程是通過(guò)fork系列的系統(tǒng)調(diào)用(fork、clone顶考、vfork)來(lái)創(chuàng)建的赁还,內(nèi)核(或內(nèi)核模塊)也可以通過(guò)kernel_thread函數(shù)創(chuàng)建內(nèi)核進(jìn)程。這些創(chuàng)建子進(jìn)程的函數(shù)本質(zhì)上都完成了相同的功能——將調(diào)用進(jìn)程復(fù)制一份驹沿,得到子進(jìn)程艘策。(可以通過(guò)選項(xiàng)參數(shù)來(lái)決定各種資源是共享、還是私有渊季。) ???????? 那么既然調(diào)用進(jìn)程處于TASK_RUNNING狀態(tài)(否則朋蔫,它若不是正在運(yùn)行罚渐,又怎么進(jìn)行調(diào)用?)驯妄,則子進(jìn)程默認(rèn)也處于TASK_RUNNING狀態(tài)荷并。 ???????? 另外,在系統(tǒng)調(diào)用調(diào)用clone和內(nèi)核函數(shù)kernel_thread也接受CLONE_STOPPED選項(xiàng)青扔,從而將子進(jìn)程的初始狀態(tài)置為 TASK_STOPPED源织。
???????? 進(jìn)程狀態(tài)變遷
???????? 進(jìn)程自創(chuàng)建以后,狀態(tài)可能發(fā)生一系列的變化赎懦,直到進(jìn)程退出雀鹃。而盡管進(jìn)程狀態(tài)有好幾種,但是進(jìn)程狀態(tài)的變遷卻只有兩個(gè)方向——從TASK_RUNNING狀態(tài)變?yōu)榉荰ASK_RUNNING狀態(tài)励两、或者從非TASK_RUNNING狀態(tài)變?yōu)門(mén)ASK_RUNNING狀態(tài)黎茎。 ???????? 也就是說(shuō),如果給一個(gè)TASK_INTERRUPTIBLE狀態(tài)的進(jìn)程發(fā)送SIGKILL信號(hào)当悔,這個(gè)進(jìn)程將先被喚醒(進(jìn)入TASK_RUNNING狀態(tài))傅瞻,然后再響應(yīng)SIGKILL信號(hào)而退出(變?yōu)門(mén)ASK_DEAD狀態(tài))。并不會(huì)從TASK_INTERRUPTIBLE狀態(tài)直接退出盲憎。
???????? 進(jìn)程從非TASK_RUNNING狀態(tài)變?yōu)門(mén)ASK_RUNNING狀態(tài)嗅骄,是由別的進(jìn)程(也可能是中斷處理程序)執(zhí)行喚醒操作來(lái)實(shí)現(xiàn)的。執(zhí)行喚醒的進(jìn)程設(shè)置被喚醒進(jìn)程的狀態(tài)為T(mén)ASK_RUNNING饼疙,然后將其task_struct結(jié)構(gòu)加入到某個(gè)CPU的可執(zhí)行隊(duì)列中溺森。于是被喚醒的進(jìn)程將有機(jī)會(huì)被調(diào)度執(zhí)行。
???????? 而進(jìn)程從TASK_RUNNING狀態(tài)變?yōu)榉荰ASK_RUNNING狀態(tài)窑眯,則有兩種途徑: 1屏积、響應(yīng)信號(hào)而進(jìn)入TASK_STOPED狀態(tài)、或TASK_DEAD狀態(tài)磅甩; 2炊林、執(zhí)行系統(tǒng)調(diào)用主動(dòng)進(jìn)入TASK_INTERRUPTIBLE狀態(tài)(如nanosleep系統(tǒng)調(diào)用)、或TASK_DEAD狀態(tài)(如exit系統(tǒng)調(diào)用)卷要;或由于執(zhí)行系統(tǒng)調(diào)用需要的資源得不到滿(mǎn)足渣聚,而進(jìn)入TASK_INTERRUPTIBLE狀態(tài)或TASK_UNINTERRUPTIBLE狀態(tài)(如select系統(tǒng)調(diào)用)。? 顯然僧叉,這兩種情況都只能發(fā)生在進(jìn)程正在CPU上執(zhí)行的情況下奕枝。
linux進(jìn)程管理之進(jìn)程創(chuàng)建(三)
在linux系統(tǒng)中,許多進(jìn)程在誕生之初都與其父進(jìn)程共同用一個(gè)存儲(chǔ)空間瓶堕。但是子進(jìn)程又可以建立自己的存儲(chǔ)空間倍权,并與父進(jìn)程“分道揚(yáng)鑣”,成為與父進(jìn)程一樣真正意義上的進(jìn)程。
?linux系統(tǒng)運(yùn)行的第一個(gè)進(jìn)程是在初始化階段“捏造出來(lái)的”薄声。而此后的線程或進(jìn)程都是由一個(gè)已存在的進(jìn)程像細(xì)胞分裂一樣通過(guò)系統(tǒng)調(diào)用復(fù)制出來(lái)的,稱(chēng)為“fork()”或者“clone()”题画。
1.fork()
關(guān)于fork()和exec()的介紹在之前的一篇博文中做了介紹默辨,
一個(gè)現(xiàn)有進(jìn)程可以調(diào)用fork()函數(shù)創(chuàng)建一個(gè)新進(jìn)程。由fork創(chuàng)建的新進(jìn)程被稱(chēng)為子進(jìn)程(child process)苍息。fork函數(shù)被調(diào)用一次但返回兩次缩幸。兩次返回的唯一區(qū)別是子進(jìn)程中返回0值而父進(jìn)程中返回子進(jìn)程ID。
子進(jìn)程是父進(jìn)程的副本竞思,它將獲得父進(jìn)程數(shù)據(jù)空間表谊、堆、棧等資源的副本盖喷。注意爆办,子進(jìn)程持有的是上述存儲(chǔ)空間的“副本”,這意味著父子進(jìn)程間不共享這些存儲(chǔ)空間课梳。
UNIX將復(fù)制父進(jìn)程的地址空間內(nèi)容給子進(jìn)程距辆,因此,子進(jìn)程有了獨(dú)立的地址空間暮刃。在不同的UNIX (Like)系統(tǒng)下跨算,我們無(wú)法確定fork之后是子進(jìn)程先運(yùn)行還是父進(jìn)程先運(yùn)行,這依賴(lài)于系統(tǒng)的實(shí)現(xiàn)椭懊。所以在移植代碼的時(shí)候我們不應(yīng)該對(duì)此作出任何的假設(shè)诸蚕。
由于在復(fù)制時(shí)復(fù)制了父進(jìn)程的堆棧段,所以?xún)蓚€(gè)進(jìn)程都停留在fork函數(shù)中氧猬,等待返回背犯。因此fork函數(shù)會(huì)返回兩次,一次是在父進(jìn)程中返回狂窑,另一次是在子進(jìn)程中返回媳板,這兩次的返回值是不一樣的。
調(diào)用fork之后泉哈,數(shù)據(jù)蛉幸、堆棧有兩份,代碼仍然為一份但是這個(gè)代碼段成為兩個(gè)進(jìn)程的共享代碼段都從fork函數(shù)中返回丛晦,箭頭表示各自的執(zhí)行處奕纫。當(dāng)父子進(jìn)程有一個(gè)想要修改數(shù)據(jù)或者堆棧時(shí),兩個(gè)進(jìn)程真正分裂烫沙。
fork函數(shù)的特點(diǎn)概括起來(lái)就是“調(diào)用一次匹层,返回兩次”,在父進(jìn)程中調(diào)用一次,在父進(jìn)程和子進(jìn)程中各返回一次升筏。
fork的另一個(gè)特性是所有由父進(jìn)程打開(kāi)的描述符都被復(fù)制到子進(jìn)程中撑柔。父、子進(jìn)程中相同編號(hào)的文件描述符在內(nèi)核中指向同一個(gè)file結(jié)構(gòu)體您访,也就是說(shuō)铅忿,file結(jié)構(gòu)體的引用計(jì)數(shù)要增加。
2.vfork()
vfork()會(huì)產(chǎn)生一個(gè)新的子進(jìn)程灵汪。但是vfork創(chuàng)建的子進(jìn)程與父進(jìn)程共享數(shù)據(jù)段,而且由vfork創(chuàng)建的檀训。子進(jìn)程將先于父進(jìn)程運(yùn)行。
vfork()用法與fork()相似.但是也有區(qū)別,具體區(qū)別歸結(jié)為以下幾點(diǎn):
1. fork():子進(jìn)程拷貝父進(jìn)程的數(shù)據(jù)段享言,代碼段. vfork():子進(jìn)程與父進(jìn)程共享數(shù)據(jù)段.
2. fork():父子進(jìn)程的執(zhí)行次序不確定.
vfork():保證子進(jìn)程先運(yùn)行峻凫,在調(diào)用exec或exit之前與父進(jìn)程數(shù)據(jù)是共享的,在它調(diào)用exec或exit之后父進(jìn)程才可能被調(diào)度運(yùn)行。
3. vfork()保證子進(jìn)程先運(yùn)行览露,在她調(diào)用exec或exit之后父進(jìn)程才可能被調(diào)度運(yùn)行荧琼。如果在調(diào)用這兩個(gè)函數(shù)之前子進(jìn)程依賴(lài)于父進(jìn)程的進(jìn)一步動(dòng)作,則會(huì)導(dǎo)致死鎖肛循。
4.當(dāng)需要改變共享數(shù)據(jù)段中變量的值铭腕,則拷貝父進(jìn)程。
從這里可見(jiàn)多糠,vfork()和fork()之間的一個(gè)區(qū)別是:vfork 保證子進(jìn)程先運(yùn)行累舷,在她調(diào)用exec 或exit 之后父進(jìn)程才可能被調(diào)度運(yùn)行。如果在調(diào)用這兩個(gè)函數(shù)之前子進(jìn)程依賴(lài)于父進(jìn)程的進(jìn)一步動(dòng)作夹孔,則會(huì)導(dǎo)致死鎖被盈。
我們來(lái)看下面這段代碼:
#include?
#include? ?
#includeint main()? ? ?
{? ? ?
? ? pid_t pid;? ? ?
? ? intcnt =0;? ? ?
? ? pid = fork();? ?
? ? if(pid<0)? ?
? ? printf("error in fork!\n");? ?
? ? elseif(pid ==0)? ?
? ? {? ?
? ? ? ? cnt++;? ?
? ? ? ? printf("cnt=%d\n",cnt);? ?
? ? ? ? printf("I am the child process,ID is %d\n",getpid());? ?
? ? }? ?
? ? else?
? ? {? ?
? ? ? ? cnt++;? ?
? ? ? ? printf("cnt=%d\n",cnt);? ?
? ? ? ? printf("I am the parent process,ID is %d\n",getpid());? ?
? ? }? ?
? ? return0;?
}
運(yùn)行結(jié)果為:
cnt=1?
I am the child process,ID is5077?
cnt=1?
I am the parent process,ID is5076
為什么不是2 呢?因?yàn)槲覀円淮螐?qiáng)調(diào)fork ()函數(shù)子進(jìn)程拷貝父進(jìn)程的數(shù)據(jù)段代碼段搭伤,所以?
cnt++;? ? ?
printf("cnt= %d\n",cnt);return0
將被父子進(jìn)程各執(zhí)行一次只怎,但是子進(jìn)程執(zhí)行時(shí)使自己的數(shù)據(jù)段里面的(這個(gè)數(shù)據(jù)段是從父進(jìn)程那copy 過(guò)來(lái)的一模一樣)count+1,同樣父進(jìn)程執(zhí)行時(shí)使自己的數(shù)據(jù)段里面的count+1怜俐,? 他們互不影響身堡,與是便出現(xiàn)了如上的結(jié)果。
那么再來(lái)看看vfork ()吧拍鲤。如果將上面程序中的fork ()改成vfork()贴谎,運(yùn)行結(jié)果是什么? 樣子的呢?
cnt=1?
I am the child process,ID is4711?
cnt=1?
I am the parent process,ID is4710?
段錯(cuò)誤
本來(lái)vfock()是共享數(shù)據(jù)段的季稳,結(jié)果應(yīng)該是2擅这,為什么不是預(yù)想的2 呢?
上面程序中的fork ()改成vfork()后景鼠,vfork ()創(chuàng)建子進(jìn)程并沒(méi)有調(diào)用exec 或exit仲翎,? 所以最終將導(dǎo)致死鎖。?
那么,對(duì)程序做下面的修改溯香,
#include? ?
#include? ?
#includeint main()? ? ?
{? ?
? ? pid_t pid;? ? ?
? ? intcnt =0;? ?
? ? pid = vfork();? ?
? ? if(pid<0)? ?
? ? ? ? printf("error in fork!\n");? ?
? ? elseif(pid ==0)? ?
? ? {? ?
? ? ? ? cnt++;? ?
? ? ? ? printf("cnt=%d\n",cnt);? ?
? ? ? ? printf("I am the child process,ID is %d\n",getpid());? ?
? ? ? ? _exit(0);? ?
? ? }? ?
? ? else?
? ? {? ?
? ? ? ? cnt++;? ?
? ? ? ? printf("cnt=%d\n",cnt);? ?
? ? ? ? printf("I am the parent process,ID is %d\n",getpid());? ?
? ? }? ?
? ? return0;? ?
}?
如果沒(méi)有_exit(0)的話鲫构,子進(jìn)程沒(méi)有調(diào)用exec 或exit,所以父進(jìn)程是不可能執(zhí)行的玫坛,在子進(jìn)程調(diào)用exec 或exit 之后父進(jìn)程才可能被調(diào)度運(yùn)行芬迄。?
所以我們加上_exit(0);使得子進(jìn)程退出,父進(jìn)程執(zhí)行昂秃,這樣else 后的語(yǔ)句就會(huì)被父進(jìn)程執(zhí)行,又因在子進(jìn)程調(diào)用exec 或exit之前與父進(jìn)程數(shù)據(jù)是共享的,所以子進(jìn)程退出后把父進(jìn)程的數(shù)據(jù)段count改成1 了杜窄,子進(jìn)程退出后肠骆,父進(jìn)程又執(zhí)行,最終就將count變成了2塞耕。
運(yùn)行結(jié)果:
cnt=1?
I am the child process,ID is4711?
cnt=2?
I am the parent process,ID is4710
3.擴(kuò)展
有這樣一段代碼:
#include
#include
#include intmain(void) {
? ? intvar;
? ? var=88;
? ? if((pid = vfork()) <0) {
? ? ? ? printf("vfork error");
? ? ? ? exit(-1);
? ? } elseif(pid ==0) {/* 子進(jìn)程 */var++;
? ? ? ? return0;
? ? }
? ? printf("pid=%d, glob=%d, var=%d\n", getpid(), glob,var);
? ? return0;
}
上述代碼一運(yùn)行就掛掉了蚀腿,但如果把子進(jìn)程的return改成exit(0)就沒(méi)事。這是為什么呢扫外?
首先說(shuō)一下fork和vfork的差別:
fork 是 創(chuàng)建一個(gè)子進(jìn)程莉钙,并把父進(jìn)程的內(nèi)存數(shù)據(jù)copy到子進(jìn)程中。
vfork是 創(chuàng)建一個(gè)子進(jìn)程筛谚,并和父進(jìn)程的內(nèi)存數(shù)據(jù)share一起用磁玉。
這兩個(gè)的差別是,一個(gè)是copy驾讲,一個(gè)是share蚊伞。
你 man vfork 一下,你可以看到吮铭,vfork是這樣的工作的时迫,
1)保證子進(jìn)程先執(zhí)行。 2)當(dāng)子進(jìn)程調(diào)用exit()或exec()后谓晌,父進(jìn)程往下執(zhí)行掠拳。
那么,為什么要干出一個(gè)vfork這個(gè)玩意纸肉? 原因在man page也講得很清楚了:
Historic Description
Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child.?However, in the bad old days a fork(2) would require making?a complete copy of the caller’s data space, often needlessly, since usually immediately afterwards an exec(3) is done. Thus, for greater efficiency, BSD introduced the vfork() system call, which did not fully copy the address space of the parent process, but borrowed the parent’s memory and thread of control until a call to execve(2) or an exit occurred.?The parent process was suspended while the child was using its resources. The use of vfork() was tricky: for example, not modifying data in the parent process depended on knowing which variables are held in a register.
意思是這樣的——?起初只有fork溺欧,但是很多程序在fork一個(gè)子進(jìn)程后就exec一個(gè)外部程序,于是fork需要copy父進(jìn)程的數(shù)據(jù)這個(gè)動(dòng)作就變得毫無(wú)意了毁靶,這樣干顯得很重(因?yàn)榭截惲怂袃?nèi)容)胧奔。
所以,BSD搞出了個(gè)父子進(jìn)程共享的 vfork预吆,這樣成本比較低龙填。因此,vfork本就是為了exec而生。
為什么return會(huì)掛掉岩遗,exit()不會(huì)扇商?
從上面我們知道,結(jié)束子進(jìn)程的調(diào)用是exit()而不是return宿礁,如果你在vfork中return了案铺,那么,這就意味main()函數(shù)return了梆靖,注意因?yàn)楹瘮?shù)棧父子進(jìn)程共享控汉,所以整個(gè)程序的棧就跪了。
如果你在子進(jìn)程中return返吻,那么基本是下面的過(guò)程:
1)子進(jìn)程的main() 函數(shù) return了姑子,于是程序的函數(shù)棧發(fā)生了變化。
2)而main()函數(shù)return后测僵,通常會(huì)調(diào)用 exit()或相似的函數(shù)(如:_exit()街佑,exitgroup())
3)這時(shí),父進(jìn)程收到子進(jìn)程exit()捍靠,開(kāi)始從vfork返回沐旨,但是父進(jìn)程的棧都被子進(jìn)程給return干廢掉了,父進(jìn)程無(wú)法執(zhí)行
(注:棧會(huì)返回一個(gè)詭異一個(gè)棧地址榨婆,對(duì)于某些內(nèi)核版本的實(shí)現(xiàn)磁携,直接報(bào)“棧錯(cuò)誤”就給跪了,然而纲辽,對(duì)于某些內(nèi)核版本的實(shí)現(xiàn)颜武,于是有可能會(huì)再次調(diào)用main(),于是進(jìn)入了一個(gè)無(wú)限循環(huán)的結(jié)果拖吼,直到vfork 調(diào)用返回 error)
好了鳞上,現(xiàn)在再回到 return 和 exit,return會(huì)釋放局部變量吊档,并彈棧篙议,回到上級(jí)函數(shù)執(zhí)行。exit直接退掉怠硼。如果你用c++ 你就知道鬼贱,return會(huì)調(diào)用局部對(duì)象的析構(gòu)函數(shù),exit不會(huì)香璃。(注:exit不是系統(tǒng)調(diào)用这难,是glibc對(duì)系統(tǒng)調(diào)用 _exit()或_exitgroup()的封裝)
可見(jiàn),子進(jìn)程調(diào)用exit() 沒(méi)有修改函數(shù)棧葡秒,所以姻乓,父進(jìn)程得以順利執(zhí)行嵌溢。
關(guān)于fork的優(yōu)化
很明顯,fork太重蹋岩,而vfork又太危險(xiǎn)赖草,所以,就有人開(kāi)始優(yōu)化fork這個(gè)系統(tǒng)調(diào)用剪个。優(yōu)化的技術(shù)用到了著名的寫(xiě)時(shí)拷貝(COW)秧骑。
也就是說(shuō),對(duì)于fork后并不是馬上拷貝內(nèi)存扣囊,而是只有你在需要改變的時(shí)候乎折,才會(huì)從父進(jìn)程中拷貝到子進(jìn)程中,這樣fork后立馬執(zhí)行exec的成本就非常小了侵歇。所以笆檀,Linux的Man Page中并不鼓勵(lì)使用vfork() ——
“ It is rather unfortunate that Linux revived this specter from the past. The BSD man page states: “This system call will be eliminated when proper system sharing mechanisms are implemented. Users should not depend on the memory sharing semantics of vfork() as it will, in that case, be made synonymous to fork(2).””
于是,從BSD4.4開(kāi)始盒至,他們讓vfork和fork變成一樣的了
但在后來(lái),NetBSD 1.3 又把傳統(tǒng)的vfork給撿了回來(lái)士修,說(shuō)是vfork的性能在 Pentium Pro 200MHz 的機(jī)器(這機(jī)器好古董凹纤臁)上有可以提高幾秒鐘的性能。詳情見(jiàn)——“NetBSD Documentation: Why implement traditional vfork()”
今天的Linux下棋嘲,fork和vfork還是各是各的酒唉,不過(guò),還是建議你不要用vfork沸移,除非你非常關(guān)注性能痪伦。
4.圖說(shuō)
在最后,放兩張fork()和vfork()的圖雹锣,我們自己體會(huì)网沾。。蕊爵。
fork():
linux進(jìn)程管理之輕量級(jí)進(jìn)程(四)
在Linux中辉哥,輕量級(jí)進(jìn)程可以是進(jìn)程,也可以是線程攒射。我們所說(shuō)的線程醋旦,在Linux中,其實(shí)是輕量級(jí)進(jìn)程之間共享代碼段会放,文件描述符饲齐,信號(hào)處理,全局變量時(shí)咧最;
如果不共享捂人,就是我們所說(shuō)的進(jìn)程御雕。
進(jìn)程是資源管理的最小單位,線程是程序執(zhí)行的最小單位先慷。在操作系統(tǒng)設(shè)計(jì)上饮笛,從進(jìn)程演化出線程,最主要的目的就是減小多進(jìn)程上下文切換開(kāi)銷(xiāo)论熙。
最初的進(jìn)程定義都包含程序福青、資源及其執(zhí)行三部分,其中程序通常指代碼脓诡,資源在操作系統(tǒng)層面上通常包括內(nèi)存資源无午、IO資源、信號(hào)處理等部分祝谚,
而程序的執(zhí)行通常理解為執(zhí)行上下文宪迟,包括對(duì)CPU的占用,后來(lái)發(fā)展為線程交惯。在線程概念出現(xiàn)以前次泽,為了減小進(jìn)程切換的開(kāi)銷(xiāo),操作系統(tǒng)設(shè)計(jì)者逐漸修正進(jìn)程
的概念席爽,逐漸允許將進(jìn)程所占有的資源從其主體剝離出來(lái)让网,允許某些進(jìn)程共享一部分資源港令,例如文件、信號(hào),數(shù)據(jù)內(nèi)存咐吼,甚至代碼蕾羊,這就發(fā)展出輕量進(jìn)程的概念疮薇。
Linux內(nèi)核在2.0.x版本就已經(jīng)實(shí)現(xiàn)了輕量進(jìn)程福贞,應(yīng)用程序可以通過(guò)一個(gè)統(tǒng)一的clone()系統(tǒng)調(diào)用接口,用不同的參數(shù)指定創(chuàng)建輕量進(jìn)程還是普通進(jìn)程祖驱。