精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深入理解Linux進(jìn)程管理

系統(tǒng) Linux
進(jìn)程是程序的執(zhí)行過程。程序是靜態(tài)的,是存在于外存之中的,電腦關(guān)機(jī)后依然存在。進(jìn)程是動態(tài)的,是存在于內(nèi)存之中的,是程序的執(zhí)行過程,電腦關(guān)機(jī)后就不存在進(jìn)程了。進(jìn)程的內(nèi)容來源于程序,進(jìn)程的啟動過程就是把程序從外存加載到內(nèi)存的過程。

一、進(jìn)程基本概念

進(jìn)程是計算機(jī)里面最重要的概念之一。操作系統(tǒng)的目的就是為了運(yùn)行進(jìn)程。那么到底什么是進(jìn)程,操作系統(tǒng)又是如何實現(xiàn)進(jìn)程和管理進(jìn)程的呢?

1.1 進(jìn)程與程序

進(jìn)程是程序的執(zhí)行過程。程序是靜態(tài)的,是存在于外存之中的,電腦關(guān)機(jī)后依然存在。進(jìn)程是動態(tài)的,是存在于內(nèi)存之中的,是程序的執(zhí)行過程,電腦關(guān)機(jī)后就不存在進(jìn)程了。進(jìn)程的內(nèi)容來源于程序,進(jìn)程的啟動過程就是把程序從外存加載到內(nèi)存的過程。程序文件是有格式的,UNIX-Like操作系統(tǒng)的通用程序文件格式是ELF。程序文件是從源碼文件編譯過來的,源碼文件很多是用C或者C++書寫的。關(guān)于編譯系統(tǒng),請參看《深入理解編譯系統(tǒng)》,關(guān)于C和C++,請參看《深入理解C與C++》。

1.2 進(jìn)程與線程

進(jìn)程是操作系統(tǒng)分配和管理系統(tǒng)資源的基本單位。進(jìn)程本來也是程序執(zhí)行的基本單位,但是自從有了線程之后就不是了?,F(xiàn)在線程是程序執(zhí)行的基本單位,代表一個執(zhí)行流,一個進(jìn)程可以有多個執(zhí)行流。最初的時候,一個進(jìn)程就只有一個執(zhí)行流,也就是主線程,此時進(jìn)程就是線程,線程就是進(jìn)程。當(dāng)程序需要多個執(zhí)行流的時候,采取的都是多進(jìn)程的方式。但是創(chuàng)建一個新進(jìn)程是一個很耗費(fèi)資源的事情,而且多個進(jìn)程之間還要進(jìn)行進(jìn)程間通信也很費(fèi)事。于是人們便想到了開發(fā)進(jìn)程內(nèi)并發(fā)機(jī)制,也就是在一個進(jìn)程內(nèi)能同時存在多個執(zhí)行流(線程)。不同的人設(shè)計的進(jìn)程內(nèi)并發(fā)機(jī)制并不相同。按照線程的管理是否實現(xiàn)在內(nèi)核里,進(jìn)程內(nèi)并發(fā)機(jī)制可以分為兩大類,分別是內(nèi)核級線程(內(nèi)核級線程也被叫做輕量級進(jìn)程)和用戶級線程,注意這兩個名詞都帶個級,它們是進(jìn)程內(nèi)并發(fā)機(jī)制的兩個子類,并不是具體的線程。內(nèi)核級線程下的線程,按照運(yùn)行主體是在內(nèi)核空間還是在用戶空間可以分為內(nèi)核線程和用戶線程。用戶級線程下的線程,按照運(yùn)行主體是在內(nèi)核空間還是在用戶空間也可以分為內(nèi)核線程和用戶線程,但是由于用戶級線程實現(xiàn)在用戶空間,所以它的線程不可能存在于內(nèi)核空間。內(nèi)核級線程下的用戶線程一般被叫做用戶線程,簡稱線程。用戶級線程下的用戶線程如果再叫用戶線程或者線程就會產(chǎn)生混淆,于是就被叫做協(xié)程或者纖程。如下圖所示: 

圖片

這兩種實現(xiàn)多線程的方法各有優(yōu)缺點(diǎn)。在用戶空間實現(xiàn)的話,優(yōu)點(diǎn)是簡單,不用改內(nèi)核,只需要實現(xiàn)一個庫就行了,創(chuàng)建線程開銷小,缺點(diǎn)是線程之間做不到真并發(fā),一個線程阻塞就會阻塞同一進(jìn)程的所有其它線程。在內(nèi)核空間實現(xiàn)的話,缺點(diǎn)是麻煩,需要改內(nèi)核,創(chuàng)建線程開銷大,但是優(yōu)點(diǎn)是能做到真并發(fā),一個進(jìn)程的多個線程可以同時在多個CPU上運(yùn)行,能充分利用CPU。當(dāng)然這兩者并不是對立的,它們可以同時實現(xiàn),一個進(jìn)程可以有多個內(nèi)核級線程,一個內(nèi)核級線程又可以有多個用戶級線程,編程者可以靈活選擇使用哪種多線程方式。

1.3 進(jìn)程與內(nèi)核

進(jìn)程與內(nèi)核在同一個虛擬地址空間中,但是在不同的子空間,進(jìn)程是在用戶空間,內(nèi)核是在內(nèi)核空間。整個系統(tǒng)只有一個內(nèi)核空間,但是卻有很多用戶空間,不過當(dāng)前用戶空間永遠(yuǎn)只有一個(對于一個CPU來說)。雖然內(nèi)核空間和用戶空間在同一個空間中,但是它們的權(quán)限并不相同。內(nèi)核空間處于特權(quán)模式,用戶空間處于非特權(quán)模式。內(nèi)核可以隨意訪問和操作用戶空間,但是用戶空間對內(nèi)核空間卻是看得見摸不著。內(nèi)核空間可以做很多特權(quán)操作,用戶空間沒有權(quán)限做,但是有些時候又需要做,所以內(nèi)核為用戶空間開了一個口子,就是系統(tǒng)調(diào)用,用戶空間可以通過系統(tǒng)調(diào)用來請求內(nèi)核的服務(wù)。關(guān)于系統(tǒng)調(diào)用請參看《深入理解Linux系統(tǒng)調(diào)用與API》。

下面我們用一張圖來總結(jié)內(nèi)核和進(jìn)程之間的關(guān)系: 

圖片

這個圖是在講進(jìn)程調(diào)度的時候畫的,但是用在這里表示進(jìn)程和內(nèi)核的關(guān)系也很合適。

1.4 進(jìn)程與內(nèi)存

對于內(nèi)核來說,內(nèi)存是有虛擬內(nèi)存和物理內(nèi)存之分的。但是對于進(jìn)程來說,這些都是透明的,進(jìn)程只需要知道自己獨(dú)占一個用戶空間的內(nèi)存就可以了,它不知道也不需要知道自己是否運(yùn)行在虛擬內(nèi)存上。如果非要說進(jìn)程知道物理內(nèi)存和虛擬內(nèi)存,那么進(jìn)程也只能分配和管理虛擬內(nèi)存,它沒法分配管理物理內(nèi)存,因為物理內(nèi)存對它來說是透明的。內(nèi)核在合適的時候會為進(jìn)程分配相應(yīng)的物理內(nèi)存,保證進(jìn)程在訪問內(nèi)存的時候一定會有對應(yīng)的物理內(nèi)存,但是進(jìn)程對此毫不知情,也管不了。

進(jìn)程需要內(nèi)存的時候可以通過系統(tǒng)調(diào)用brk、sbrk、mmap來向內(nèi)核申請分配虛擬內(nèi)存。但是直接使用系統(tǒng)調(diào)用來分配管理內(nèi)存顯然很麻煩效率也低,為此libc向進(jìn)程提供了malloc庫,malloc提供了malloc、free等幾個接口供進(jìn)程使用。這樣進(jìn)程需要內(nèi)存的時候就可以直接使用malloc去分配內(nèi)存,使用完了就用free去釋放內(nèi)存,不用考慮分配效率、內(nèi)存碎片等問題了。目前比較流行的malloc庫有ptmalloc、jemalloc、scudo等。

1.5 進(jìn)程運(yùn)行狀態(tài)

很多操作系統(tǒng)的書籍上都會講進(jìn)程的運(yùn)行狀態(tài),有的講的是三態(tài),有的講的是五態(tài)。其實兩者并不矛盾,三態(tài)只有進(jìn)程運(yùn)行時的狀態(tài),五態(tài)把進(jìn)程的新建和死亡狀態(tài)也算上去了,如下圖所示: 

圖片

進(jìn)程剛創(chuàng)建之后處于新建態(tài),但是新建態(tài)不是持久狀態(tài),它會立馬轉(zhuǎn)變?yōu)榫途w狀態(tài)。然后進(jìn)程就會一直處于就緒、執(zhí)行、阻塞三態(tài)的循環(huán)之中。就緒態(tài)會由于進(jìn)程調(diào)度而轉(zhuǎn)為執(zhí)行態(tài);執(zhí)行態(tài)會由于時間片耗盡而轉(zhuǎn)為就緒態(tài),也會由于等待某個事件而轉(zhuǎn)為阻塞態(tài);阻塞態(tài)會由于某個事件的發(fā)生而轉(zhuǎn)為就緒態(tài)。最后進(jìn)程可能會由于主動退出或者發(fā)生異常而死亡。死亡態(tài)也不是一個持久態(tài),進(jìn)程死亡之后就不存在了。

1.6 進(jìn)程親緣關(guān)系

所有進(jìn)程都通過父子關(guān)系連接而構(gòu)成一顆親緣樹,這顆樹的樹根是init進(jìn)程(pid1)。Init進(jìn)程是第一個用戶空間進(jìn)程,所有的用戶空間進(jìn)程都是init進(jìn)程的子孫進(jìn)程。Init進(jìn)程的父進(jìn)程是零號進(jìn)程,零號進(jìn)程是在代碼中通過硬編碼創(chuàng)建的,其它所有的進(jìn)程都是通過fork創(chuàng)建的。這里為什么叫做零號進(jìn)程呢?因為零號進(jìn)程的職責(zé)發(fā)生過變化,在系統(tǒng)剛啟動的時候,零號進(jìn)程是BSP(bootstrapprocess),start_kernel函數(shù)就是在零號進(jìn)程中運(yùn)行的。當(dāng)系統(tǒng)初始化完成的時候,零號進(jìn)程退化為了idle進(jìn)程。當(dāng)我們只強(qiáng)調(diào)零號進(jìn)程的身份而不關(guān)心它的職責(zé)的時候,就叫它零號進(jìn)程。當(dāng)后面我們強(qiáng)調(diào)它的idle職責(zé)的時候,就叫它idle進(jìn)程。

零號進(jìn)程有兩個親兒子,除了init之外,還有一個是kthreadd(pid2)。Kthreadd是一個內(nèi)核線程,它是所有其它內(nèi)核線程的父進(jìn)程。內(nèi)核線程比較特殊的點(diǎn)在于它只運(yùn)行在內(nèi)核空間,所以所有的內(nèi)核線程都可以看做是同一個進(jìn)程下的線程,因為內(nèi)核空間只有一個。但是每個內(nèi)核線程在邏輯意義上又是一個獨(dú)立的進(jìn)程,它們執(zhí)行獨(dú)立的任務(wù),有著獨(dú)立的進(jìn)程人格。所以當(dāng)我們說一個內(nèi)核線程的時候,心里也要明白它是一個單獨(dú)的進(jìn)程,是一個只有主線程的單線程進(jìn)程。

我們來畫一下進(jìn)程的親緣關(guān)系: 

圖片

進(jìn)程除了父子這種血緣關(guān)系之外,還存在著家族關(guān)系。一個是大家族關(guān)系,會話組(session),一個是小家族關(guān)系,進(jìn)程組(process

group)。會話組的產(chǎn)生來源于早期的大型計算機(jī),當(dāng)時一個公司或者一個科研單位只能買得起一臺大型機(jī)。然后每個人都通過一個終端連接到這個大型機(jī),用自己的用戶名和密碼登錄上去。每個用戶都有自己的用戶id,一個用戶運(yùn)行的所有的程序構(gòu)成了一個會話組。有了會話組的概念,就可以方便我們把一個用戶運(yùn)行的所有進(jìn)程作為一個整體進(jìn)行管理。進(jìn)程組的產(chǎn)生來源于命令行操作的作業(yè)管理。什么是作業(yè)管理呢?就是把一行命令的執(zhí)行整體作為一個作業(yè)。一行命令的執(zhí)行不一定只有一個進(jìn)程,比如命令ps -ef | grepbash,就有兩個進(jìn)程,我們需要有個概念把這兩個進(jìn)程作為一個整體來處理,這個概念就是進(jìn)程組。有了進(jìn)程組的概念,作業(yè)管理就比較方便了,比如Ctrl+C就是給當(dāng)前正在執(zhí)行的命令(進(jìn)程組)發(fā)信號,進(jìn)程組中的每個進(jìn)程都會收到信號。

一個進(jìn)程誕生的時候默認(rèn)繼承父進(jìn)程的會話組和進(jìn)程組,但是進(jìn)程可以通過系統(tǒng)調(diào)用(setsid,setpgrp)創(chuàng)建新的會話組或者進(jìn)程組。會話組的第一個進(jìn)程叫做這個會話組的組長,進(jìn)程組的第一個進(jìn)程叫做這個進(jìn)程組的組長,會話組的id等于會話組組長的pid,進(jìn)程組的id等于進(jìn)程組組長的pid。一個進(jìn)程只有當(dāng)它不是某個進(jìn)程組組長的時候,它才可以調(diào)用setpgrp創(chuàng)建新的進(jìn)程組,同時它也成為了這個新建的進(jìn)程組的組長。這個也很好理解,只有臣子造反當(dāng)皇帝,哪有皇帝自己造自己的反重新創(chuàng)建一個朝代的。同理,只有不是會話組組長的進(jìn)程才能通過setsid創(chuàng)建新的會話組,并成為這個會話組組長。而且在這個新的會話組里也不能沒有進(jìn)程組啊,于是還會創(chuàng)建一個進(jìn)程組,這個會話組組長還會成為這個新建的進(jìn)程組的組長,這也要求了這個進(jìn)程之前不能是進(jìn)程組組長。所以只有既不是進(jìn)程組組長又不是會話組組長的進(jìn)程才能創(chuàng)建新的會話組。

任何一個進(jìn)程,它必然屬于某個進(jìn)程組,而且只能同時屬于一個進(jìn)程組。任何一個進(jìn)程,它必然屬于某個會話組,而且只能屬于一個會話組。任何一個進(jìn)程組,它的所有進(jìn)程必須都屬于同一個會話組。一個進(jìn)程所屬的會話組只有兩種來源,要么是繼承而來的,要么是自己創(chuàng)建的,進(jìn)程是不能轉(zhuǎn)會話組的。不過一個進(jìn)程是可以轉(zhuǎn)進(jìn)程組的,但是只能在同一個會話組中的進(jìn)程組之間轉(zhuǎn)。因此我們可以得出一個結(jié)論,一個會話組的所有進(jìn)程肯定都是其會話組組長的子孫進(jìn)程,一個進(jìn)程組的所有進(jìn)程一般情況下都是其進(jìn)程組組長的子孫進(jìn)程。

我們來畫一下進(jìn)程的家族關(guān)系: 


圖片

二、進(jìn)程的實現(xiàn)

明白了進(jìn)程的基本概念之后,我們來看一看Linux是怎么實現(xiàn)進(jìn)程的。按照標(biāo)準(zhǔn)的操作系統(tǒng)理論,進(jìn)程是資源分配的單位,線程是程序執(zhí)行的單位,內(nèi)核里用進(jìn)程控制塊(PCB Process Control Block)來管理進(jìn)程,用線程控制塊(TCB Thread Control Block)來管理線程。那么Linux是按照這個邏輯來實現(xiàn)進(jìn)程的嗎?我們來看一下。

2.1 基本原理

Linux內(nèi)核并不是按照標(biāo)準(zhǔn)的操作系統(tǒng)理論來實現(xiàn)進(jìn)程的,在內(nèi)核里找不到典型的進(jìn)程控制塊和線程控制塊。內(nèi)核里只有一個task_struct結(jié)構(gòu)體,初學(xué)內(nèi)核的人會很疑惑這是代表進(jìn)程還是代表線程呢。之所以會這樣,是由于歷史原因造成的。Linux最開始的時候是不支持多線程的,也可以認(rèn)為此時一個進(jìn)程只能有一個線程就是主線程,因此線程就是進(jìn)程,進(jìn)程就是線程。所以最初的時候,task_struct既代表進(jìn)程又代表線程,因為進(jìn)程和線程沒有區(qū)別。但是后來Linux也要支持多線程了,我們在1.2節(jié)中討論過,多線程的實現(xiàn)方法可以在內(nèi)核實現(xiàn),也可以在用戶空間實現(xiàn),也可以同時實現(xiàn),Linux選擇的是在內(nèi)核實現(xiàn)。為了最大限度地利用已有的代碼,盡量不對代碼做大的改動,Linux選擇的方法是:task_struct既是線程又是進(jìn)程的代理。注意這句話,task_struct既是線程又是進(jìn)程的代理(不是進(jìn)程本身)。Linux并沒有設(shè)計單獨(dú)的進(jìn)程結(jié)構(gòu)體,而是用task_struct作為進(jìn)程的代理,這是因為進(jìn)程是資源分配的單位,線程是程序執(zhí)行的單位,同一個進(jìn)程的所有線程共享相同的資源,因此我們讓同一個進(jìn)程下的所有線程(task_struct)都指向相同的資源不就可以了嘛。線程在執(zhí)行的時候會通過task_struct里面的指針訪問資源,同一個進(jìn)程下的線程自然就會訪問到相同的資源,而且這么做還有很大的靈活性。

我們下面再來強(qiáng)調(diào)一下這句話,以加深對這句話的理解。

task_struct既是線程又是進(jìn)程的代理(不是進(jìn)程本身)。 

2.2 進(jìn)程結(jié)構(gòu)體

當(dāng)我們明白了task_struct既是線程又是進(jìn)程的代理之后,再來理解task_struct就容易多了。task_struct的字段由兩部分組成,一部分是線程相關(guān)的,一部分是進(jìn)程相關(guān)的,線程相關(guān)的一般是直接內(nèi)嵌其它數(shù)據(jù),進(jìn)程相關(guān)的一般是用指針指向其它數(shù)據(jù)。線程代表的是執(zhí)行流,所以task_struct的線程相關(guān)部分是和執(zhí)行有關(guān)的,進(jìn)程代表的是資源分配,所以task_struct的進(jìn)程相關(guān)部分是和資源有關(guān)的。我們可以想一下和執(zhí)行有關(guān)的都有哪些,和資源有關(guān)的都哪些?可以很輕松地想到,和執(zhí)行有關(guān)的肯定是進(jìn)程調(diào)度相關(guān)的數(shù)據(jù)啊(進(jìn)程調(diào)度雖然叫進(jìn)程調(diào)度,但實際上調(diào)度的是線程)。和資源相關(guān)的,最重要的首先肯定是虛擬內(nèi)存啊,其次是文件系統(tǒng)。

下面我們來看一下task_struct的定義: linux-src/include/linux/sched.h

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
struct thread_info thread_info;
#endif
unsigned int __state;
void *stack;
unsigned int flags;
int on_cpu;
unsigned int cpu;
int recent_used_cpu;
int wake_cpu;
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
struct sched_dl_entity dl;
unsigned int policy;
int nr_cpus_allowed;
cpumask_t cpus_mask;
struct sched_info sched_info;
struct list_head tasks;

struct mm_struct *mm;
struct mm_struct *active_mm;

struct vmacache vmacache;

int exit_state;
int exit_code;
int exit_signal;

pid_t pid;
pid_t tgid;

struct task_struct __rcu *real_parent;
struct task_struct __rcu *parent;
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;

unsigned long nvcsw;
unsigned long nivcsw;

u64 start_time;
u64 start_boottime;

unsigned long min_flt;
unsigned long maj_flt;

char comm[TASK_COMM_LEN];

struct fs_struct *fs;
struct files_struct *files;

struct signal_struct *signal;
struct sighand_struct __rcu *sighand;
sigset_t blocked;
sigset_t real_blocked;
sigset_t saved_sigmask;
struct sigpending pending;

struct thread_struct thread;
};

這個結(jié)構(gòu)體定義有700多行,本文把一些暫時用不到的都刪除了,現(xiàn)在還有70多行,我們來看一下大概都有哪些內(nèi)容。先看和進(jìn)程相關(guān)的,首先最重要的是虛擬內(nèi)存空間信息mm、active_mm,這兩個都是指針,對于用戶線程來說兩個指針的值永遠(yuǎn)都是相同的,同一個進(jìn)程的所有線程都指向相同的mm,這個值就表明了同一個進(jìn)程的線程都在同一個用戶空間。其次比較重要的是文件管理相關(guān)的兩個字段fs和files,也都是指針,fs代表的是文件系統(tǒng)掛載相關(guān)的,這個不僅是同進(jìn)程的所有線程都相同,而且整個系統(tǒng)默認(rèn)的值都一樣,除非使用了mount 命名空間,files代表的是打開的文件資源,這個是同進(jìn)程的所有線程都相同。然后我們再來看一下信號相關(guān)的,信號有的數(shù)據(jù)是進(jìn)程全局的,有的是線程私有的,信號的處理是進(jìn)程全局的,所以signal、sighand兩個字段都是指針,同進(jìn)程的所有線程都指向同一個結(jié)構(gòu)體,信號掩碼是線程私有的,所以blocked直接是內(nèi)嵌數(shù)據(jù)。進(jìn)程相關(guān)的數(shù)據(jù)基本就這些,下面我們來看一下線程相關(guān)的數(shù)據(jù)。首先是進(jìn)程的運(yùn)行退出狀態(tài),有幾個字段,__state、on_cpu、cpu、exit_state、exit_code、exit_signal。然后是和線程調(diào)度相關(guān)的幾個字段,有和優(yōu)先級相關(guān)的rt_priority、static_prio、normal_prio、prio,有和調(diào)度信息統(tǒng)計相關(guān)的兩個結(jié)構(gòu)體,se、sched_info。還有兩個非常重要的字段我們下一節(jié)講。

2.3 進(jìn)程標(biāo)識符

task_struct里面有兩個重要的字段pid、tgid。我們在用戶空間的時候也有pid、tid,那么用戶空間的pid是不是就是內(nèi)核的pid呢,那tgid又是啥呢。很多初學(xué)內(nèi)核的人會認(rèn)為用戶空間的pid就是內(nèi)核的pid,剛開始我也是這么認(rèn)為的,給我的內(nèi)核學(xué)習(xí)帶來了很大的困擾。實際上用戶空間的tid是內(nèi)核空間pid,用戶空間的pid是內(nèi)核空間的tgid,內(nèi)核空間的tgid是內(nèi)核里主線程的pid。為什么會這樣呢?主要還是前面講的問題,task_struct既是線程又是進(jìn)程的代理,沒有單獨(dú)的進(jìn)程結(jié)構(gòu)體。當(dāng)進(jìn)程創(chuàng)建時,也就是進(jìn)程的第一個線程創(chuàng)建時,會為task_struct分配一個pid,就是主線程的tid,然后進(jìn)程的pid也就是字段tgid會被賦值為主線程的tid。此后再創(chuàng)建的線程都會繼承父線程的tgid,所以在每個線程中都能直接獲取進(jìn)程的pid。

我們在這里畫個圖總結(jié)一下進(jìn)程與線程的關(guān)系、pid與tgid之間的關(guān)系: 

Linux里面雖然沒有進(jìn)程結(jié)構(gòu)體,但是所有tgid相同、虛擬內(nèi)存等資源相同的線程構(gòu)成一個虛擬的進(jìn)程結(jié)構(gòu)體。創(chuàng)建進(jìn)程的第一個線程(task_struct)就是同時在創(chuàng)建進(jìn)程,其對應(yīng)的mm_struct、files_struct、signal_struct等資源都會被創(chuàng)建出來。創(chuàng)建進(jìn)程的第二個線程那就是純粹地創(chuàng)建線程了。

2.4 進(jìn)程的狀態(tài)

進(jìn)程的狀態(tài)在Linux中是如何表示的呢?task_struct中有兩個字段用來表示進(jìn)程的狀態(tài),__state和exit_state,前者是總體狀態(tài),后者是進(jìn)程在死亡時的兩個子狀態(tài)。

我們來看一下代碼中的定義: linux-src/include/linux/sched.h

/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
#define EXIT_ZOMBIE 0x0020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
#define TASK_WAKEKILL 0x0100
#define TASK_WAKING 0x0200
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800

其中TASK_RUNNING代表的是Runnable和Running狀態(tài)。在Linux中不是用flag直接區(qū)分Runnable和Running狀態(tài)的,它們都用TASK_RUNNING表示,區(qū)分它們的方法是進(jìn)程是否在運(yùn)行隊列的當(dāng)前進(jìn)程字段上。Blocked狀態(tài)有兩種表示,TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE,它們的區(qū)別是前者在睡眠時能被信號喚醒,后者不能被信號喚醒。表示死亡的狀態(tài)是TASK_DEAD,它有兩個子狀態(tài)EXIT_ZOMBIE、EXIT_DEAD,這兩個狀態(tài)在3.6中講解。


二、進(jìn)程的實現(xiàn)

明白了進(jìn)程的基本概念之后,我們來看一看Linux是怎么實現(xiàn)進(jìn)程的。按照標(biāo)準(zhǔn)的操作系統(tǒng)理論,進(jìn)程是資源分配的單位,線程是程序執(zhí)行的單位,內(nèi)核里用進(jìn)程控制塊(PCB Process Control Block)來管理進(jìn)程,用線程控制塊(TCB Thread Control Block)來管理線程。那么Linux是按照這個邏輯來實現(xiàn)進(jìn)程的嗎?我們來看一下。

2.1 基本原理

Linux內(nèi)核并不是按照標(biāo)準(zhǔn)的操作系統(tǒng)理論來實現(xiàn)進(jìn)程的,在內(nèi)核里找不到典型的進(jìn)程控制塊和線程控制塊。內(nèi)核里只有一個task_struct結(jié)構(gòu)體,初學(xué)內(nèi)核的人會很疑惑這是代表進(jìn)程還是代表線程呢。之所以會這樣,是由于歷史原因造成的。Linux最開始的時候是不支持多線程的,也可以認(rèn)為此時一個進(jìn)程只能有一個線程就是主線程,因此線程就是進(jìn)程,進(jìn)程就是線程。所以最初的時候,task_struct既代表進(jìn)程又代表線程,因為進(jìn)程和線程沒有區(qū)別。但是后來Linux也要支持多線程了,我們在1.2節(jié)中討論過,多線程的實現(xiàn)方法可以在內(nèi)核實現(xiàn),也可以在用戶空間實現(xiàn),也可以同時實現(xiàn),Linux選擇的是在內(nèi)核實現(xiàn)。為了最大限度地利用已有的代碼,盡量不對代碼做大的改動,Linux選擇的方法是:task_struct既是線程又是進(jìn)程的代理。注意這句話,task_struct既是線程又是進(jìn)程的代理(不是進(jìn)程本身)。Linux并沒有設(shè)計單獨(dú)的進(jìn)程結(jié)構(gòu)體,而是用task_struct作為進(jìn)程的代理,這是因為進(jìn)程是資源分配的單位,線程是程序執(zhí)行的單位,同一個進(jìn)程的所有線程共享相同的資源,因此我們讓同一個進(jìn)程下的所有線程(task_struct)都指向相同的資源不就可以了嘛。線程在執(zhí)行的時候會通過task_struct里面的指針訪問資源,同一個進(jìn)程下的線程自然就會訪問到相同的資源,而且這么做還有很大的靈活性。

我們下面再來強(qiáng)調(diào)一下這句話,以加深對這句話的理解。

task_struct既是線程又是進(jìn)程的代理(不是進(jìn)程本身)。 

2.2 進(jìn)程結(jié)構(gòu)體

當(dāng)我們明白了task_struct既是線程又是進(jìn)程的代理之后,再來理解task_struct就容易多了。task_struct的字段由兩部分組成,一部分是線程相關(guān)的,一部分是進(jìn)程相關(guān)的,線程相關(guān)的一般是直接內(nèi)嵌其它數(shù)據(jù),進(jìn)程相關(guān)的一般是用指針指向其它數(shù)據(jù)。線程代表的是執(zhí)行流,所以task_struct的線程相關(guān)部分是和執(zhí)行有關(guān)的,進(jìn)程代表的是資源分配,所以task_struct的進(jìn)程相關(guān)部分是和資源有關(guān)的。我們可以想一下和執(zhí)行有關(guān)的都有哪些,和資源有關(guān)的都哪些?可以很輕松地想到,和執(zhí)行有關(guān)的肯定是進(jìn)程調(diào)度相關(guān)的數(shù)據(jù)啊(進(jìn)程調(diào)度雖然叫進(jìn)程調(diào)度,但實際上調(diào)度的是線程)。和資源相關(guān)的,最重要的首先肯定是虛擬內(nèi)存啊,其次是文件系統(tǒng)。

下面我們來看一下task_struct的定義: linux-src/include/linux/sched.h

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
struct thread_info thread_info;
#endif
unsigned int __state;
void *stack;
unsigned int flags;
int on_cpu;
unsigned int cpu;
int recent_used_cpu;
int wake_cpu;
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
struct sched_dl_entity dl;
unsigned int policy;
int nr_cpus_allowed;
cpumask_t cpus_mask;
struct sched_info sched_info;
struct list_head tasks;

struct mm_struct *mm;
struct mm_struct *active_mm;

struct vmacache vmacache;

int exit_state;
int exit_code;
int exit_signal;

pid_t pid;
pid_t tgid;

struct task_struct __rcu *real_parent;
struct task_struct __rcu *parent;
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;

unsigned long nvcsw;
unsigned long nivcsw;

u64 start_time;
u64 start_boottime;

unsigned long min_flt;
unsigned long maj_flt;

char comm[TASK_COMM_LEN];

struct fs_struct *fs;
struct files_struct *files;

struct signal_struct *signal;
struct sighand_struct __rcu *sighand;
sigset_t blocked;
sigset_t real_blocked;
sigset_t saved_sigmask;
struct sigpending pending;

struct thread_struct thread;
};

這個結(jié)構(gòu)體定義有700多行,本文把一些暫時用不到的都刪除了,現(xiàn)在還有70多行,我們來看一下大概都有哪些內(nèi)容。先看和進(jìn)程相關(guān)的,首先最重要的是虛擬內(nèi)存空間信息mm、active_mm,這兩個都是指針,對于用戶線程來說兩個指針的值永遠(yuǎn)都是相同的,同一個進(jìn)程的所有線程都指向相同的mm,這個值就表明了同一個進(jìn)程的線程都在同一個用戶空間。其次比較重要的是文件管理相關(guān)的兩個字段fs和files,也都是指針,fs代表的是文件系統(tǒng)掛載相關(guān)的,這個不僅是同進(jìn)程的所有線程都相同,而且整個系統(tǒng)默認(rèn)的值都一樣,除非使用了mount 命名空間,files代表的是打開的文件資源,這個是同進(jìn)程的所有線程都相同。然后我們再來看一下信號相關(guān)的,信號有的數(shù)據(jù)是進(jìn)程全局的,有的是線程私有的,信號的處理是進(jìn)程全局的,所以signal、sighand兩個字段都是指針,同進(jìn)程的所有線程都指向同一個結(jié)構(gòu)體,信號掩碼是線程私有的,所以blocked直接是內(nèi)嵌數(shù)據(jù)。進(jìn)程相關(guān)的數(shù)據(jù)基本就這些,下面我們來看一下線程相關(guān)的數(shù)據(jù)。首先是進(jìn)程的運(yùn)行退出狀態(tài),有幾個字段,__state、on_cpu、cpu、exit_state、exit_code、exit_signal。然后是和線程調(diào)度相關(guān)的幾個字段,有和優(yōu)先級相關(guān)的rt_priority、static_prio、normal_prio、prio,有和調(diào)度信息統(tǒng)計相關(guān)的兩個結(jié)構(gòu)體,se、sched_info。還有兩個非常重要的字段我們下一節(jié)講。

2.3 進(jìn)程標(biāo)識符

task_struct里面有兩個重要的字段pid、tgid。我們在用戶空間的時候也有pid、tid,那么用戶空間的pid是不是就是內(nèi)核的pid呢,那tgid又是啥呢。很多初學(xué)內(nèi)核的人會認(rèn)為用戶空間的pid就是內(nèi)核的pid,剛開始我也是這么認(rèn)為的,給我的內(nèi)核學(xué)習(xí)帶來了很大的困擾。實際上用戶空間的tid是內(nèi)核空間pid,用戶空間的pid是內(nèi)核空間的tgid,內(nèi)核空間的tgid是內(nèi)核里主線程的pid。為什么會這樣呢?主要還是前面講的問題,task_struct既是線程又是進(jìn)程的代理,沒有單獨(dú)的進(jìn)程結(jié)構(gòu)體。當(dāng)進(jìn)程創(chuàng)建時,也就是進(jìn)程的第一個線程創(chuàng)建時,會為task_struct分配一個pid,就是主線程的tid,然后進(jìn)程的pid也就是字段tgid會被賦值為主線程的tid。此后再創(chuàng)建的線程都會繼承父線程的tgid,所以在每個線程中都能直接獲取進(jìn)程的pid。

我們在這里畫個圖總結(jié)一下進(jìn)程與線程的關(guān)系、pid與tgid之間的關(guān)系: 

Linux里面雖然沒有進(jìn)程結(jié)構(gòu)體,但是所有tgid相同、虛擬內(nèi)存等資源相同的線程構(gòu)成一個虛擬的進(jìn)程結(jié)構(gòu)體。創(chuàng)建進(jìn)程的第一個線程(task_struct)就是同時在創(chuàng)建進(jìn)程,其對應(yīng)的mm_struct、files_struct、signal_struct等資源都會被創(chuàng)建出來。創(chuàng)建進(jìn)程的第二個線程那就是純粹地創(chuàng)建線程了。

2.4 進(jìn)程的狀態(tài)

進(jìn)程的狀態(tài)在Linux中是如何表示的呢?task_struct中有兩個字段用來表示進(jìn)程的狀態(tài),__state和exit_state,前者是總體狀態(tài),后者是進(jìn)程在死亡時的兩個子狀態(tài)。

我們來看一下代碼中的定義: linux-src/include/linux/sched.h

/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
#define EXIT_ZOMBIE 0x0020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
#define TASK_WAKEKILL 0x0100
#define TASK_WAKING 0x0200
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800

其中TASK_RUNNING代表的是Runnable和Running狀態(tài)。在Linux中不是用flag直接區(qū)分Runnable和Running狀態(tài)的,它們都用TASK_RUNNING表示,區(qū)分它們的方法是進(jìn)程是否在運(yùn)行隊列的當(dāng)前進(jìn)程字段上。Blocked狀態(tài)有兩種表示,TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE,它們的區(qū)別是前者在睡眠時能被信號喚醒,后者不能被信號喚醒。表示死亡的狀態(tài)是TASK_DEAD,它有兩個子狀態(tài)EXIT_ZOMBIE、EXIT_DEAD,這兩個狀態(tài)在3.6中講解。

責(zé)任編輯:武曉燕 來源: Linux閱碼場
相關(guān)推薦

2013-06-20 10:25:56

2021-07-26 07:47:36

數(shù)據(jù)庫

2021-07-20 08:02:41

Linux進(jìn)程睡眠

2023-02-10 08:11:43

Linux系統(tǒng)調(diào)用

2025-10-28 04:25:00

2020-09-28 08:44:17

Linux內(nèi)核

2025-06-16 05:10:00

2021-08-31 10:32:11

LinuxPage Cache命令

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2010-06-01 15:25:27

JavaCLASSPATH

2020-07-21 08:26:08

SpringSecurity過濾器

2021-05-19 07:56:26

Linux內(nèi)核搶占

2017-01-12 19:34:58

2023-10-19 11:12:15

Netty代碼

2009-09-25 09:14:35

Hibernate日志

2013-09-22 14:57:19

AtWood

2021-02-17 11:25:33

前端JavaScriptthis

2019-06-25 10:32:19

UDP編程通信

2017-08-15 13:05:58

Serverless架構(gòu)開發(fā)運(yùn)維

2025-05-06 00:43:00

MySQL日志文件MIXED 3
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

国产国语videosex另类| 亚洲电影免费观看高清完整版在线 | 国产一区二区三区在线观看视频| 少妇人妻在线视频| 国产一区精品| 国产精品1区2区3区| 555www成人网| 色婷婷在线视频观看| 亚洲精品无吗| 日韩一区二区免费高清| 成人综合视频在线| a级网站在线播放| 久久久精品一品道一区| 亚洲最大的成人网| 五月天激情四射| 欧美激情一级片一区二区| 亚洲欧美在线免费观看| 一区二区在线免费观看视频| 国产综合色区在线观看| 亚洲午夜三级在线| 伊人av成人| 欧美偷拍视频| 粉嫩高潮美女一区二区三区| 国产日韩视频在线观看| 黄色片网站在线免费观看| 重囗味另类老妇506070| 在线看国产精品| 精品久久久久久中文字幕人妻最新| 日韩欧国产精品一区综合无码| 欧美日韩一区二区三区在线免费观看 | 国产免费观看av| 五月久久久综合一区二区小说| 亚洲精品在线免费播放| 亚洲精品成人在线播放| 欧美日韩尤物久久| 日本久久一区二区三区| 免费无码毛片一区二三区| 性网站在线观看| 综合电影一区二区三区 | 成人免费在线观看| 337p粉嫩大胆色噜噜噜噜亚洲| 99国产超薄丝袜足j在线观看| 国产精品第6页| 日韩专区一卡二卡| 国内免费精品永久在线视频| 欧美精品xxxxx| 久久久久亚洲| 久久影院在线观看| 国产精品久久久免费看| 波多野结衣在线播放一区| 亚洲欧美日韩精品久久奇米色影视| 美女搡bbb又爽又猛又黄www| 一区二区三区亚洲变态调教大结局 | 精品国内片67194| 亚洲天堂一区二区在线观看| 日日夜夜综合| 欧美一区日本一区韩国一区| 91欧美一区二区三区| 久久69av| 精品国产一二三区| jizz日本免费| 竹菊久久久久久久| 亚洲天堂av高清| 欧美亚洲色综久久精品国产| 成人精品电影| 久久九九有精品国产23| 亚洲色婷婷一区二区三区| 午夜激情一区| 海角国产乱辈乱精品视频| 天海翼在线视频| 亚洲激情久久| 欧美精品aaa| 男女啊啊啊视频| 久久av最新网址| 国产精品香蕉av| 国产v片在线观看| 99re热这里只有精品视频| 蜜桃91精品入口| www.中文字幕久久久| 亚洲欧洲三级电影| 又大又硬又爽免费视频| 久草在线资源站手机版| 欧美性色黄大片手机版| 日韩av影视大全| 国产精品videossex| 亚洲欧美国产日韩中文字幕| 亚洲熟女少妇一区二区| 狠狠入ady亚洲精品| 欧美一区视频在线| 一二三四区视频| 国产ts人妖一区二区| 欧美日韩一区二区三| 91欧美在线视频| 亚洲成人自拍偷拍| 黄色三级视频片| 中文字幕一区二区三区日韩精品| 日韩高清人体午夜| 熟女少妇a性色生活片毛片| 亚洲手机视频| 国产精品福利在线| 亚洲国产www| 国产欧美精品国产国产专区| 黄色一级大片免费| 色婷婷综合久久久中字幕精品久久| 欧美久久久一区| jizz日本免费| 午夜精品电影| 国产精品嫩草视频| 亚洲av电影一区| 亚洲欧美日韩一区二区 | 人狥杂交一区欧美二区| 69堂国产成人免费视频| 中文字幕丰满孑伦无码专区| 欧美精品麻豆| 国产啪精品视频| 日本私人网站在线观看| 一区二区三区色| 亚洲第一中文av| 亚洲第一福利专区| 欧美激情xxxx性bbbb| 亚洲天堂网视频| 久久精品日产第一区二区三区高清版| 久久综合亚洲精品| 欧美一级二级视频| 国产视频自拍一区| 日韩免费视频网站| 国产精品1区二区.| 91xxx视频| 青青青国产精品| 亚洲一区二区久久久| 天堂在线免费观看视频| 福利一区二区在线观看| 国产av第一区| 99er精品视频| 最近2019中文免费高清视频观看www99| 久久久久久久黄色片| 国产成都精品91一区二区三| 天天爱天天做天天操| 日韩精品第二页| zzijzzij亚洲日本成熟少妇| 亚洲图片小说视频| 中文av一区特黄| av五月天在线| 日韩精品电影| 国产在线高清精品| 日韩在线免费电影| 欧美日韩亚洲丝袜制服| 欧洲性xxxx| 精品一区二区三区视频| 亚洲欧洲精品一区二区三区波多野1战4| 色老太综合网| 在线播放日韩欧美| 亚洲自拍第二页| 中文字幕一区二区不卡| 污色网站在线观看| 欧美韩日高清| 91色p视频在线| www国产在线观看| 欧美videos中文字幕| 久久精品国产av一区二区三区| 成人美女视频在线看| 一二三四视频社区在线| 校园春色另类视频| 国产精品wwww| 日本三级在线视频| 日韩欧美激情在线| 91香蕉在线视频| 久久精品在这里| 999在线观看| 午夜精品久久99蜜桃的功能介绍| 国产99视频精品免费视频36| 国产美女精品写真福利视频| 亚洲欧美在线一区| 国产一区二区在线不卡| 亚洲一区二区三区在线看| 少妇饥渴放荡91麻豆| 日韩精品三区四区| 成人短视频在线看| 精品伊人久久久| 国产精品91久久久久久| а√中文在线8| 亚洲黄页视频免费观看| 夜夜躁日日躁狠狠久久av| 亚洲欧美日韩国产中文在线| 亚洲啪av永久无码精品放毛片 | 黄色片免费网址| 亚洲国产黄色| 亚洲国产精品久久久久婷婷老年| 精品三级国产| 日本精品va在线观看| 欧美精品电影| 日韩精品极品在线观看播放免费视频| 中国女人真人一级毛片| 一区二区三区成人| 丰满的亚洲女人毛茸茸| 国产成人精品亚洲午夜麻豆| 日韩av资源在线| 欧美精品播放| 亚洲视频在线二区| 秋霞蜜臀av久久电影网免费| 国产有码一区二区| 中文字幕色婷婷在线视频| 超碰精品一区二区三区乱码| 欧美xxx.com| 欧美成人乱码一区二区三区| 中文字幕乱码人妻无码久久| 性做久久久久久久免费看| 成人无码精品1区2区3区免费看| 成人福利视频网站| 最新免费av网址| 日韩中文字幕av电影| 国产中文字幕乱人伦在线观看| 日本a级不卡| 欧美日韩国产免费一区二区三区| 99re91这里只有精品| 国产美女直播视频一区| 成人av观看| 国模视频一区二区| 性xxxfreexxxx性欧美| 色天天综合狠狠色| 国产美女性感在线观看懂色av| 精品三级在线观看| 国产精品爽爽久久久久久| 91成人看片片| 日本特级黄色片| 亚洲成人av电影在线| 中文字幕av免费在线观看| 国产精品亲子伦对白| 欧洲美一区二区三区亚洲| av不卡一区二区三区| 国产精久久久久| 国产精品一卡二卡| 国产三级精品三级在线| 男人的天堂久久精品| 欧美国产日韩在线播放| 水蜜桃久久夜色精品一区的特点| 日本在线xxx| 亚洲第一精品影视| 日韩中文字幕在线免费| 激情视频一区| 色欲色香天天天综合网www| 国产一区清纯| 国产日韩欧美精品在线观看| 欧美日韩国产高清| 日韩a级黄色片| 亚洲经典自拍| 日本xxxxxxxxxx75| 国产欧美日本| 99精品免费在线观看| 久久在线精品| 美女喷白浆视频| 老鸭窝一区二区久久精品| 欧美成年人视频在线观看| 麻豆91在线观看| 97超碰人人看| 成人午夜视频福利| 亚洲国产精品无码久久久久高潮| 99精品在线观看视频| 黄色工厂在线观看| 欧美激情一区在线观看| 国产极品视频在线观看| 亚洲人精品一区| 免费在线黄色片| 欧美日韩中文字幕| 亚洲男人天堂网址| 91精品国产综合久久久久久| 亚洲精品视频专区| 国产视频丨精品|在线观看| 国产免费av高清在线| 久久久国产精品一区| 黄色小说在线播放| 国产不卡av在线| 色婷婷成人网| 国产三级精品在线不卡| 亚洲最好看的视频| 亚洲午夜精品一区二区| 欧美日韩国产欧| 国产淫片av片久久久久久| 美女mm1313爽爽久久久蜜臀| 欧美人与性动交α欧美精品| av一区二区久久| 中文字幕黄色网址| 亚洲一区二区三区美女| 日批视频免费在线观看| 欧美高清你懂得| 日韩中文字幕观看| 中日韩美女免费视频网址在线观看| 国产剧情在线| 欧美中文字幕在线播放| 日本中文字幕视频一区| 国产一区免费在线| 久久亚洲国产| 欧美国产激情视频| 国产精品99久久久久久久vr| 泷泽萝拉在线播放| 樱花草国产18久久久久| 久久久精品毛片| 精品成人一区二区| 在线观看免费黄视频| 午夜精品一区二区三区在线视频| 欧美黄色成人| 精品国产一区二区三区四区vr| 清纯唯美日韩| 日韩网址在线观看| 国产成a人亚洲精品| 69xxx免费| 一本久久精品一区二区| 超碰在线观看av| 色午夜这里只有精品| 亚洲男人av| 国产一区免费在线观看| 亚洲综合五月| 久久久精品三级| 欧美激情视频一区二区三区在线播放 | 久久影视电视剧免费网站| 色www永久免费视频首页在线| 日韩av免费在线播放| 91蝌蚪精品视频| 日日噜噜噜夜夜爽爽| 日日噜噜夜夜狠狠视频欧美人| 一级黄色片毛片| 一区二区三区**美女毛片| 在线观看国产成人| 亚洲人a成www在线影院| 99thz桃花论族在线播放| 亚洲一区二区久久久久久久| 久久精品99久久无色码中文字幕| 成年人视频网站免费观看| 成人午夜电影久久影院| 青草草在线视频| 69堂成人精品免费视频| 日本中文字幕在线观看| 国产精品久久久久久久久久99| 三级精品视频| 成人免费在线小视频| 成人久久视频在线观看| 国产亚洲精品码| 精品久久久久久久久久久院品网 | 啊啊啊国产视频| 国产拍欧美日韩视频二区| 欧美一区免费看| 国产亚洲精品久久久久久| xx欧美视频| 日韩精品伦理第一区| 日韩成人免费电影| 成人在线观看免费高清| 欧美日韩一区二区三区四区五区 | 妺妺窝人体色www在线小说| av电影天堂一区二区在线观看| 国产无套内射又大又猛又粗又爽| 精品国产一区二区三区av性色| 国产羞羞视频在线播放| 国产一区二区高清不卡| 国产日韩1区| 久久国产柳州莫菁门| 欧美日免费三级在线| 午夜视频在线免费观看| 亚洲va男人天堂| 激情久久久久久| 精品人妻无码一区二区三区 | 成人在线观看黄| 亚洲国产精品传媒在线观看| 亚洲中文字幕在线一区| 欧美区在线播放| 精品亚洲自拍| 五月婷婷深爱五月| 成人欧美一区二区三区黑人麻豆| 国产手机av在线| 国内精品久久久久久中文字幕| 美国成人xxx| 91人人澡人人爽人人精品| 亚洲免费观看视频| 色婷婷在线视频| 国产精品久久一区| 午夜电影亚洲| 免费黄色在线视频| 日韩一区二区三区四区| 中文在线а√在线8| 在线观看日韩羞羞视频| 丁香桃色午夜亚洲一区二区三区| 91视频免费网址| 久久精品夜夜夜夜夜久久| 国产精品videossex| 色婷婷狠狠18| 亚洲亚洲人成综合网络| 欧美色图另类| 7777精品久久久大香线蕉小说| 亚洲影视在线| 国产成人自拍网站| 亚洲乱码av中文一区二区| 亚洲精品aaa| 欧美极品欧美精品欧美图片| 国产精品电影一区二区| 天堂在线视频免费观看| 成人国内精品久久久久一区| 夜夜精品视频| 外国一级黄色片| 亚洲三级免费看| 久久精品亚洲成在人线av网址| 色悠悠久久综合网|