1. 程式人生 > >程序與程序描述符

程序與程序描述符

一、 程序

程序(Process)
計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。在早期面向程序設計的計算機結構中,程序是程式的基本執行實體;在當代面向執行緒設計的計算機結構中,程序是執行緒的容器。程式是指令、資料及其組織形式的描述,程序是程式的實體。

———————————————————————————————————————————————————————————————

  • 釋義:一段程式的執行過程
  • 特徵:動態、獨立、非同步、併發
  • 結構特徵:程式、資料和程序控制塊
  • 相關概念:執行緒,管程

定義

狹義定義:程序是正在執行的程式的例項(an instance of a computer program that is being executed)。

廣義定義:
程序是一個具有一定獨立功能的程式關於某個資料集合的一次執行活動。
它是是作業系統動態執行的基本單元,在傳統的作業系統中,程序既是基本的分配單元,也是基本的執行單元。

程序的概念主要有兩點:

第一,程序是一個實體

'每一個程序都有它自己的地址空間,一般情況下,包括文字區域(text region)、資料區域(data region)和堆疊(stack region)。文字區域儲存處理器執行的程式碼;資料區域儲存變數和程序執行期間使用的動態分配的記憶體;堆疊區域儲存著活動過程呼叫的指令和本地變數。'
  • 1

第二,程序是一個“執行中的程式”

'程式是一個沒有生命的實體,只有處理器賦予程式生命時(作業系統執行之),它才能成為一個活動的實體,我們稱其為程序。'
  • 1

特徵

動態性:程序的實質是程式在多道程式系統中的一次執行過程,程序是動態產生,動態消亡的。

併發性:任何程序都可以同其他程序一起併發執行

獨立性:程序是一個能獨立執行的基本單位,同時也是系統分配資源和排程的獨立單位;

非同步性:由於程序間的相互制約,使程序具有執行的間斷性,即程序按各自獨立的、不可預知的速度向前推進

結構特徵:程序由程式、資料和程序控制塊三部分組成。
多個不同的程序可以包含相同的程式:一個程式在不同的資料集裡就構成不同的程序,能得到不同的結果;但是執行過程中,程式不能發生改變。

內容

一個計算機系統程序包括(或者說“擁有”)下列資料:
那個程式的可執行機器碼的一個在儲存器的映像。 分配到的儲存器(通常包括虛擬記憶體的一個區域)。儲存器的內容包括可執行程式碼、特定於程序的資料(輸入、輸出)、呼叫堆疊、堆疊(用於儲存執行時運數中途產生的資料)。 分配給該程序的資源的作業系統描述符,諸如檔案描述符(Unix術語)或檔案控制代碼(Windows)、資料來源和資料終端。 安全特性,諸如程序擁有者和程序的許可權集(可以容許的操作)。 處理器狀態(內文),諸如暫存器內容、物理儲存器定址等。當程序正在執行時,狀態通常儲存在暫存器,其他情況在儲存器。

切換

進行程序切換就是從正在執行的程序中收回處理器,然後再使待執行程序來佔用處理器。
這裡所說的從某個程序收回處理器,實質上就是把程序存放在處理器的暫存器中的中間資料找個地方存起來,從而把處理器的暫存器騰出來讓其他程序使用。那麼被中止執行程序的中間資料存在何處好呢?當然這個地方應該是程序的私有堆疊。
讓程序來佔用處理器,實質上是把某個程序存放在私有堆疊中暫存器的資料(前一次本程序被中止時的中間資料)再恢復到處理器的暫存器中去,並把待執行程序的斷點送入處理器的程式指標PC,於是待執行程序就開始被處理器運行了,也就是這個程序已經佔有處理器的使用權了。
這就像多個同學要分時使用同一張課桌一樣,所謂要收回正在使用課桌同學的課桌使用權,實質上就是讓他把屬於他的東西拿走;而賦予某個同學課桌使用權,只不過就是讓他把他的東西放到課桌上罷了。
在切換時,一個程序儲存在處理器各暫存器中的中間資料叫做程序的上下文,所以程序的 切換實質上就是被中止執行程序與待執行程序上下文的切換。在程序未佔用處理器時,程序 的上下文是儲存在程序的私有堆疊中的。

狀態

程序執行時的間斷性,決定了程序可能具有多種狀態。事實上,執行中的程序可能具有以下三種基本狀態。

1)就緒狀態(Ready):
程序已獲得除處理器外的所需資源,等待分配處理器資源;只要分配了處理器程序就可執行。就緒程序可以按多個優先順序來劃分佇列。例如,當一個程序由於時間片用完而進入就緒狀態時,排入低優先順序佇列;當程序由I/O操作完成而進入就緒狀態時,排入高優先順序佇列。

2)執行狀態(Running):
程序佔用處理器資源;處於此狀態的程序的數目小於等於處理器的數目。在沒有其他程序可以執行時(如所有程序都在阻塞狀態),通常會自動執行系統的空閒程序。

3)阻塞狀態(Blocked):
由於程序等待某種條件(如I/O操作或程序同步),在條件滿足之前無法繼續執行。該事件發生前即使把處理器資源分配給該程序,也無法執行。
區別

程式

程式是指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。而程序是程式在處理機上的一次執行過程,它是一個動態的概念。
程式可以作為一種軟體資料長期存在,而程序是有一定生命期的。程式是永久的,程序是暫時的。
程序更能真實地描述併發,而程式不能;
程序是由程序控制塊、程式段、資料段三部分組成;
程序具有建立其他程序的功能,而程式沒有。
同一程式同時運行於若干個資料集合上,它將屬於若干個不同的程序,也就是說同一程式可以對應多個程序。
在傳統的作業系統中,程式並不能獨立執行,作為資源分配和獨立執行的基本單元都是程序。

執行緒

程序和執行緒關係

通常在一個程序中可以包含若干個執行緒,它們可以利用程序所擁有的資源,在引入執行緒的作業系統中,通常都是把程序作為分配資源的基本單位,而把執行緒作為獨立執行和獨立排程的基本單位,由於執行緒比程序更小,基本上不擁有系統資源,故對它的排程所付出的開銷就會小得多,能更高效的提高系統內多個程式間併發執行的程度。
當下推出的通用作業系統都引入了執行緒,以便進一步提高系統的併發性,並把它視為現代作業系統的一個重要指標。

控制

程序控制是程序管理中最基本的功能。它用於建立一個新程序,終止一個已完成的程序,或者去終止一個因出現某事件而使其無法執行下去的程序,還可負責程序執行中的狀態轉換。

建立程序

1.引起建立程序的事件
在多道程式環境中,只有(作為)程序(時)才能在系統中執行。因此,為使程式能執行,就必須為它建立程序。導致一個程序去建立另一個程序的典型事件,可以有以下四類:

1) 使用者登入
在分時系統中,使用者在終端鍵入登入命令後,如果是合法使用者,系統將為該終端建立一個程序,並把它插入到就緒佇列中。

2)作業排程
在批處理系統中,當作業排程程式按照一定的演算法排程到某作業時,便將該作業裝入到記憶體,為它分配必要的資源,並立即為它建立程序,再插入到就緒佇列中。

3) 提供服務
當執行中的使用者程式提出某種請求後,系統將專門建立一個程序來提供使用者所需要的服務,例如,使用者程式要求進行檔案列印,作業系統將為它建立一個列印程序,這樣,不僅可以使列印程序與該使用者程序併發執行,而且還便於計算出為完成列印任務所花費的時間。

4) 應用請求
在上述三種情況中,都是由系統核心為它建立一個新程序,而這一類事件則是基於應用程序的需求,由它建立一個新的程序,以便使新程序以併發的執行方式完成特定任務。
2.程序的建立過程
這裡寫圖片描述
一旦作業系統發現了要求建立新程序的事件後,便呼叫程序建立原語create()按下述步驟建立一個新程序。

1) 申請空白PCB。為新程序申請獲得唯一的數字識別符號,並從PCB集合中索取一個空白PCB。

2) 為新程序分配資源。為新程序的程式和資料以及使用者棧分配必要的記憶體空間。顯然,此時作業系統必須知道新程序所需要的記憶體大小。

3) 初始化程序控制塊。PCB的初始化包括:
①初始化標識資訊,將系統分配的識別符號和父程序識別符號,填入新的PCB中。
②初始化處理機狀態資訊,使程式計數器指向程式的入口地址,使棧指標指向棧頂。
③初始化處理機控制資訊,將程序的狀態設定為就緒狀態或靜止就緒狀態,對於優先順序,通常是將它設定為最低優先順序,除非使用者以顯式的方式提出高優先順序要求。

4) 將新程序插入就緒佇列,如果程序就緒佇列能夠接納新程序,便將新程序插入到就緒佇列中。

程序終止
1.引起程序終止的事件

1)正常結束
在任何計算機系統中,都應該有一個表示程序已經執行完成的指示。例如,在批處理系統中,通常在程式的最後安排一條Hold指令或終止的系統呼叫。當程式執行到Hold指令時,將產生一箇中斷,去通知OS本程序已經完成。

2)異常結束
在程序執行期間,由於出現某些錯誤和故障而迫使程序終止。這類異常事件很多,常見的有:越界錯誤,保護錯,非法指令,特權指令錯,執行超時,等待超時,算術運算錯,I/O故障。

3)外界干預
外界干預並非指在本程序執行中出現了異常事件,而是指程序應外界的請求而終止執行。這些干預有:操作員或作業系統干預,父程序請求,父程序終止。

  1. 程序的終止過程
    如果系統發生了上述要求終止程序的某事件後,OS便呼叫程序終止原語,按下述過程去終止指定的程序。

1)根據被終止程序的識別符號,從PCB集合中檢索出該程序的PCB,從中讀出該程序狀態。
2)若被終止程序正處於執行狀態,應立即終止該程序的執行,並置排程標誌為真。用於指示該程序被終止後應重新進行排程。
3)若該程序還有子孫程序,還應將其所有子孫程序予以終止,以防他們成為不可控的程序。
4)將被終止的程序所擁有的全部資源,或者歸還給其父程序,或者歸還給系統。
5)將被終止程序(它的PCB)從所在佇列(或連結串列)中移出,等待其它程式來蒐集資訊。

阻塞喚醒

1.引起程序阻塞和喚醒的事件

1)請求系統服務
當正在執行的程序請求作業系統提供服務時,由於某種原因,作業系統並不立即滿足該程序的要求時,該程序只能轉變為阻塞狀態來等待,一旦要求得到滿足後,程序被喚醒。

2)啟動某種操作
當程序啟動某種操作後,如果該程序必須在該操作完成之後才能繼續執行,則必須先使該程序阻塞,以等待該操作完成,該操作完成後,將該程序喚醒。

3)新資料尚未到達
對於相互合作的程序,如果其中一個程序需要先獲得另一(合作)程序提供的資料才能執行以對資料進行處理,則是要其所需資料尚未到達,該程序只有(等待)阻塞,等到資料到達後,該程序被喚醒。

4)無新工作可做
系統往往設定一些具有某特定功能的系統程序,每當這種程序完成任務後,便把自己阻塞起來以等待新任務到來,新任務到達後,該程序被喚醒。

2.程序阻塞過程

正在執行的程序,當發現上述某事件後,由於無法繼續執行,於是程序便通過呼叫阻塞原語block()把自己阻塞。可見,程序的阻塞是程序自身的一種主動行為。進入block過程後,由於此時該程序還處於執行狀態,所以應先立即停止執行,把程序控制塊中的現行狀態由執行改為阻塞,並將PCB插入阻塞佇列。如果系統中設定了因不同事件而阻塞的多個阻塞佇列,則應將本程序插入到具有相同事件的阻塞(等待)佇列。最後,轉排程程式進行重新排程,將處理機分配給另一就緒程序,並進行切換,亦即,保留被阻塞程序的處理機狀態(在PCB中),再按新程序的PCB中的處理機狀態設定CPU環境。

  1. 程序喚醒過程
    當被阻塞的程序所期待的事件出現時,如I/O完成或者其所期待的資料已經到達,則由有關程序(比如,用完並釋放了該I/O裝置的程序)呼叫喚醒原語wakeup(),將等待該事件的程序喚醒。喚醒原語執行的過程是:首先把被阻塞的程序從等待該事件的阻塞佇列中移出,將其PCB中的現行狀態由阻塞改為就緒,然後再將該PCB插入到就緒佇列中。

排程演算法

程序的排程演算法包括:
實時系統中:FIFO(First Input First Output,先進先出演算法),SJF(Shortest Job First,最短作業優先演算法),SRTF(Shortest Remaining Time First,最短剩餘時間優先演算法)。

互動式系統中:RR(Round Robin,時間片輪轉演算法),HPF(Highest Priority First,最高優先順序演算法),多級佇列,最短程序優先,保證排程,彩票排程,公平分享排程。

階段
程序是由程序控制塊、程式段、資料段三部分組成。一個程序可以包含若干執行緒(Thread),執行緒可以幫助應用程式同時做幾件事(比如一個執行緒向磁碟寫入檔案,另一個則接收使用者的按鍵操作並及時做出反應,互相不干擾),在程式被執行後,系統首先要做的就是為該程式程序建立一個預設執行緒,然後程式可以根據需要自行新增或刪除相關的執行緒。是可併發執行的程式。在一個數據集合上的執行過程,是系統進行資源分配和排程的一個獨立單位,也是稱活動、路徑或任務,它有兩方面性質:活動性、併發性。程序可以劃分為執行、阻塞、就緒三種狀態,並隨一定條件而相互轉化:就緒–執行,執行–阻塞,阻塞–就緒。

程序為應用程式的執行例項,是應用程式的一次動態執行。看似高深,我們可以簡單地理解為:它是作業系統當前執行的執行程式。在系統當前執行的執行程式裡包括:系統管理計算機個體和完成各種操作所必需的程式;使用者開啟、執行的額外程式,當然也包括使用者不知道,而自動執行的非法程式(它們就有可能是病毒程式)。

二、程序描述符

1.概念

在linux中,每一個程序都有一個程序描述符,這個”程序描述符”是一個結構體名字叫做task_struct,在task_struct裡面儲存了許多關於程序控制的資訊。
task_struct是Linux核心的一種資料結構,它會被裝載到RAM裡幷包含程序的資訊。每個程序都把它的資訊放在task_struct這個資料結構裡面,而

2.task_struct內容

標示符:描述本程序的唯一標示符,用來區別其他程序。

狀態:任務狀態,退出程式碼,退出訊號等。

優先順序:相對於其他程序的優先順序。

程式計數器:程式中即將被執行的下一條指令的地址。

記憶體指標:包括程式程式碼和程序相關資料的指標,還有和其他程序共享的記憶體塊的指標。

上下文資料:程序執行時處理器的暫存器中的資料。

I/O狀態資訊:包括顯示的I/O請求,分配給程序的I/O裝置和正在被程序使用的檔案列表。

記賬資訊:可能包括處理器時間總和,使用的時鐘總數,時間限制,記賬號等。

3.task_struct的分類

排程資料成員

(1) volatile long states; //程序狀態
(2) unsigned long flags; //程序標記符
(3) long priority; //用來儲存動態優先順序
(4) unsigned long rt_priority; //用來儲存實時優先順序,取值範圍為0~99
(5) long counter;
(6) unsigned long policy;

訊號處理

(1) unsigned long signal;
(2) unsigned long blocked;
(3) struct signal_struct *sig;

程序佇列指標

(1) struct task_struct *next_task,*prev_task;
(2) struct task_struct *next_run,*prev_run;
(3) struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr,*p_ysptr,*p_osptr;

程序標識

(1) unsigned short uid,gid;
(2) int groups[NGROUPS];
(3) unsigned short euid,egid;
(4) unsigned short fsuid,fsgid;
(5) unsigned short suid,sgid;
(6) int pid,pgrp,session;
(7) int leader;

時間資料成員

(1) unsigned long timeout;
(2) unsigned long it_real_value,it_real_iner;
(3) struct timer_list real_timer;
(4) unsigned long it_virt_value,it_virt_incr;
(5) unsigned long it_prof_value,it_prof_incr;
(6) long utime,stime,cutime,cstime,start_time;

訊號量資料成員

(1) struct sem_undo *semundo;
(2) struct sem_queue *semsleeping;

程序上下文環境

(1) struct desc_struct *ldt;
(2) struct thread_struct tss;
(3) unsigned long saved_kernel_stack;
(4) unsigned long kernel_stack_page;

檔案系統資料成員

(1) struct fs_struct *fs;
(2) struct files_struct *files;
(3) int link_count;

記憶體資料成員

(1) struct mm_struct *mm;

頁面管理

(1) int swappable:1;
(2) unsigned long swap_address;
(3) unsigned long min_flt,maj_flt;
(4) unsigned long nswap;
(5) unsigned long cmin_flt,cmaj_flt,cnswap;
(6) unsigned long old_maj_flt,dec_flt;
(7) unsigned long swap_cnt;

支援對稱多處理器方式(SMP)時的資料成員

(1) int processor;
(2) int last_processor;
(3) int lock_depth;

其它資料成員

(1) unsigned short used_math;
(2) char comm[16];
(3) struct rlimit rlim[RLIM_NLIMITS];
(4) int errno;
(5) long debugreg[8];
(6) struct exec_domain *exec_domain;
(7) unsigned long personality;
(8) struct linux_binfmt *binfmt;
(9) int exit_code,exit_signal;
(10) int dumpable:1;
(11) int did_exec:1;
(12) int tty_old_pgrp;
(13) struct tty_struct *tty;
(14) struct wait_queue *wait_chldexit;

程序佇列的全域性變數

(1) current;
(2) struct task_struct init_task;
(3) struct task_struct *task[NR_TASKS];
(4) unsigned long volatile jiffies;
(5) int need_resched;
(6) unsigned long intr_count;

task_struct的定義

truct task_struct {
volatile long state;  //說明了該程序是否可以執行,還是可中斷等資訊
unsigned long flags;  //Flage 是程序號,在呼叫fork()時給出
int sigpending;    //程序上是否有待處理的訊號
mm_segment_t addr_limit; //程序地址空間,區分核心程序與普通程序在記憶體存放的位置不同
                        //0-0xBFFFFFFF for user-thead
                        //0-0xFFFFFFFF for kernel-thread
//排程標誌,表示該程序是否需要重新排程,若非0,則當從核心態返回到使用者態,會發生排程
volatile long need_resched;
int lock_depth;  //鎖深度
long nice;       //程序的基本時間片
//程序的排程策略,有三種,實時程序:SCHED_FIFO,SCHED_RR, 分時程序:SCHED_OTHER
unsigned long policy;
struct mm_struct *mm; //程序記憶體管理資訊
int processor;
//若程序不在任何CPU上執行, cpus_runnable 的值是0,否則是1 這個值在執行佇列被鎖時更新
unsigned long cpus_runnable, cpus_allowed;
struct list_head run_list; //指向執行佇列的指標
unsigned long sleep_time;  //程序的睡眠時間
//用於將系統中所有的程序連成一個雙向迴圈連結串列, 其根是init_task
struct task_struct *next_task, *prev_task;
struct mm_struct *active_mm;
struct list_head local_pages;       //指向本地頁面      
unsigned int allocation_order, nr_local_pages;
struct linux_binfmt *binfmt;  //程序所執行的可執行檔案的格式
int exit_code, exit_signal;
int pdeath_signal;     //父程序終止時向子程序傳送的訊號
unsigned long personality;
//Linux可以執行由其他UNIX作業系統生成的符合iBCS2標準的程式
int did_exec:1; 
pid_t pid;    //程序識別符號,用來代表一個程序
pid_t pgrp;   //程序組標識,表示程序所屬的程序組
pid_t tty_old_pgrp;  //程序控制終端所在的組標識
pid_t session;  //程序的會話標識
pid_t tgid;
int leader;     //表示程序是否為會話主管
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
struct list_head thread_group;   //執行緒連結串列
struct task_struct *pidhash_next; //用於將程序鏈入HASH表
struct task_struct **pidhash_pprev;
wait_queue_head_t wait_chldexit;  //供wait4()使用
struct completion *vfork_done;  //供vfork() 使用
unsigned long rt_priority; //實時優先順序,用它計算實時程序排程時的weight值

//it_real_value,it_real_incr用於REAL定時器,單位為jiffies, 系統根據it_real_value
//設定定時器的第一個終止時間. 在定時器到期時,向程序傳送SIGALRM訊號,同時根據
//it_real_incr重置終止時間,it_prof_value,it_prof_incr用於Profile定時器,單位為jiffies。
//當程序執行時,不管在何種狀態下,每個tick都使it_prof_value值減一,當減到0時,向程序傳送
//訊號SIGPROF,並根據it_prof_incr重置時間.
//it_virt_value,it_virt_value用於Virtual定時器,單位為jiffies。當程序執行時,不管在何種
//狀態下,每個tick都使it_virt_value值減一當減到0時,向程序傳送訊號SIGVTALRM,根據
//it_virt_incr重置初值。
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_value;
struct timer_list real_timer;   //指向實時定時器的指標
struct tms times;      //記錄程序消耗的時間
unsigned long start_time;  //程序建立的時間
//記錄程序在每個CPU上所消耗的使用者態時間和核心態時間
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; 
//記憶體缺頁和交換資訊:
//min_flt, maj_flt累計程序的次缺頁數(Copy on Write頁和匿名頁)和主缺頁數(從對映檔案或交換
//裝置讀入的頁面數); nswap記錄程序累計換出的頁面數,即寫到交換裝置上的頁面數。
//cmin_flt, cmaj_flt, cnswap記錄本程序為祖先的所有子孫程序的累計次缺頁數,主缺頁數和換出頁面數。
//在父程序回收終止的子程序時,父程序會將子程序的這些資訊累計到自己結構的這些域中
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1; //表示程序的虛擬地址空間是否允許換出
//程序認證資訊
//uid,gid為執行該程序的使用者的使用者識別符號和組識別符號,通常是程序建立者的uid,gid
//euid,egid為有效uid,gid
//fsuid,fsgid為檔案系統uid,gid,這兩個ID號通常與有效uid,gid相等,在檢查對於檔案
//系統的訪問許可權時使用他們。
//suid,sgid為備份uid,gid
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;
int keep_capabilities:1;
struct user_struct *user;
struct rlimit rlim[RLIM_NLIMITS];  //與程序相關的資源限制資訊
unsigned short used_math;   //是否使用FPU
char comm[16];   //程序正在執行的可執行檔名
 //檔案系統資訊
int link_count, total_link_count;
//NULL if no tty 程序所在的控制終端,如果不需要控制終端,則該指標為空
struct tty_struct *tty;
unsigned int locks;
//程序間通訊資訊
struct sem_undo *semundo;  //程序在訊號燈上的所有undo操作
struct sem_queue *semsleeping; //當程序因為訊號燈操作而掛起時,他在該佇列中記錄等待的操作
//程序的CPU狀態,切換時,要儲存到停止程序的task_struct中
struct thread_struct thread;
  //檔案系統資訊
struct fs_struct *fs;
  //開啟檔案資訊
struct files_struct *files;
  //訊號處理函式
spinlock_t sigmask_lock;
struct signal_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;
u32 parent_exec_id;
u32 self_exec_id;

spinlock_t alloc_lock;
void *journal_info;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113