天天看點

linux核心——3_(程序管理)系統的程序管理

作者:GWD 時間:2019.7.28

一、系統的程序的運轉方式

1、系統時間:(jiffies系統滴答):CPU内部有一個RTC,會在上電的時候調用mktime函數算出從1970年1月1日0時開始到目前開機點所過的秒數給MKTIME函數傳來的時間結構體的指派是由初始化時從RTC(coms)中讀出的參數 轉化為時間存入全局變量中,并且會為JIFFIES所用;

2、 JIFFIES 是一個系統的時鐘滴答 ,一個系統滴答是10ms ,定時器10ms一個系統滴答—>每隔10ms會引發一個定時器中斷(這個中斷服務函數中,首先進行了jiffies的自加;

Jiffies自增後調用do_timer函數

linux核心——3_(程式管理)系統的程式管理

(一)、分析do_timer()函數

1、Sched.c中打開do_timer函數

時鐘中斷C 函數處理程式,在kernel/system_call.s 中的_timer_interrupt(176 行)被調用。
// 參數cpl 是目前特權級0 或3,0 表示核心代碼在執行。
// 對于一個程序由于執行時間片用完時,則進行任務切換。并執行一個計時更新工作。
void do_timer (long cpl)
{
	extern int beepcount;		// 揚聲器發聲時間滴答數(kernel/chr_drv/console.c,697)
	extern void sysbeepstop (void);	// 關閉揚聲器(kernel/chr_drv/console.c,691)

  // 如果發聲計數次數到,則關閉發聲。(向0x61 口發送指令,複位位0 和1。位0 控制8253
  // 計數器2 的工作,位1 控制揚聲器)。
	if (beepcount)
		if (!--beepcount)
			sysbeepstop ();

  // 如果目前特權級(cpl)為0(最高,表示是核心程式在工作),則将超級使用者運作時間stime 遞增;
  // 如果cpl > 0,則表示是一般使用者程式在工作,增加utime。
	if (cpl)
		current->utime++;
	else
		current->stime++;

// 如果有使用者的定時器存在,則将連結清單第1 個定時器的值減1。如果已等于0,則調用相應的處理
// 程式,并将該處理程式指針置為空。然後去掉該項定時器。
	if (next_timer)
	{				// next_timer 是定時器連結清單的頭指針(見270 行)。
		next_timer->jiffies--;
		while (next_timer && next_timer->jiffies <= 0)
		{
			void (*fn) ();	// 這裡插入了一個函數指針定義!!!??

			fn = next_timer->fn;
			next_timer->fn = NULL;
			next_timer = next_timer->next;
			(fn) ();		// 調用處理函數。
		}
	}
// 如果目前軟碟控制器FDC 的數字輸出寄存器中馬達啟動位有置位的,則執行軟碟定時程式(245 行)。
	if (current_DOR & 0xf0)
		do_floppy_timer ();
	if ((--current->counter) > 0)
		return;			// 如果程序運作時間還沒完,則退出。
	current->counter = 0;
	if (!cpl)
		return;			// 對于超級使用者程式,不依賴counter 值進行排程。
	schedule ();
}

           

2、問:current是什麼?

linux核心——3_(程式管理)系統的程式管理

答:是目前程序,為什麼目前程序可以用current一個變量來表示呢?這就是作業系統的,魅力,也是面向對象的思想。它有一個封裝的非常好的結構體叫task_struct。CPL變量是核心中用來訓示被中斷程式的特權,0表示核心程序,1表示被中斷的是使用者程序;

linux核心——3_(程式管理)系統的程式管理

3、task_struct:無論是程序的建立、退出、管理都是在跟這個結構體在打交道。建立程序的時候就是建立一個這個task_struct變量;

// 這裡是任務(程序)資料結構,或稱為程序描述符。
// ==========================
// long state 任務的運作狀态(-1 不可運作,0 可運作(就緒),>0 已停止)。
// long counter 任務運作時間計數(遞減)(滴答數),運作時間片。
// long priority 運作優先數。任務開始運作時counter = priority,越大運作越長。
// long signal 信号。是位圖,每個比特位代表一種信号,信号值=位偏移值+1。
// struct sigaction sigaction[32] 信号執行屬性結構,對應信号将要執行的操作和标志資訊。
// long blocked 程序信号屏蔽碼(對應信号位圖)。
// --------------------------
// int exit_code 任務執行停止的退出碼,其父程序會取。
// unsigned long start_code 代碼段位址。
// unsigned long end_code 代碼長度(位元組數)。
// unsigned long end_data 代碼長度 + 資料長度(位元組數)。
// unsigned long brk 總長度(位元組數)。
// unsigned long start_stack 堆棧段位址。
// long pid 程序辨別号(程序号)。
// long father 父程序号。
// long pgrp 父程序組号。
// long session 會話号。
// long leader 會話首領。
// unsigned short uid 使用者辨別号(使用者id)。
// unsigned short euid 有效使用者id。
// unsigned short suid 儲存的使用者id。
// unsigned short gid 組辨別号(組id)。
// unsigned short egid 有效組id。
// unsigned short sgid 儲存的組id。
// long alarm 報警定時值(滴答數)。
// long utime 使用者态運作時間(滴答數)。
// long stime 系統态運作時間(滴答數)。
// long cutime 子程序使用者态運作時間。
// long cstime 子程序系統态運作時間。
// long start_time 程序開始運作時刻。
// unsigned short used_math 标志:是否使用了協處理器。
// --------------------------
// int tty 程序使用tty 的子裝置号。-1 表示沒有使用。
// unsigned short umask 檔案建立屬性屏蔽位。
// struct m_inode * pwd 目前工作目錄i 節點結構。
// struct m_inode * root 根目錄i 節點結構。
// struct m_inode * executable 執行檔案i 節點結構。
// unsigned long close_on_exec 執行時關閉檔案句柄位圖示志。(參見include/fcntl.h)
// struct file * filp[NR_OPEN] 程序使用的檔案表結構。
// --------------------------
// struct desc_struct ldt[3] 本任務的局部表描述符。0-空,1-代碼段cs,2-資料和堆棧段ds&ss。
// --------------------------
// struct tss_struct tss 本程序的任務狀态段資訊結構。
// ==========================
struct task_struct
{
/* these are hardcoded - don't touch */
	long state;			/* -1 unrunnable, 0 runnable, >0 stopped */
	long counter;
	long priority;
	long signal;
	struct sigaction sigaction[32];
	long blocked;			/* bitmap of masked signals */
/* various fields */
	int exit_code;
	unsigned long start_code, end_code, end_data, brk, start_stack;
	long pid, father, pgrp, session, leader;
	unsigned short uid, euid, suid;
	unsigned short gid, egid, sgid;
	long alarm;
	long utime, stime, cutime, cstime, start_time;
	unsigned short used_math;
/* file system info */
	int tty;			/* -1 if no tty, so it must be signed */
	unsigned short umask;
	struct m_inode *pwd;
	struct m_inode *root;
	struct m_inode *executable;
	unsigned long close_on_exec;
	struct file *filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
	struct desc_struct ldt[3];
/* tss for this task */
	struct tss_struct tss;
};


           

4、接下來看一下next_timer是什麼?

next_timer 是嫁接與jiffies變量的所有定時器的事件連結清單。如果連結清單上有定時器的話就執行這個定時器,若沒有就不執行

linux核心——3_(程式管理)系統的程式管理

next_timer 是嫁接與jiffies變量的所有定時器的事件連結清單,jiffies就好像一根繩子,連結清單是在上邊打的節,比如十分鐘的時候要執行某些程式,第十分鐘就是連結清單。

linux核心——3_(程式管理)系統的程式管理

5、current->counter ---->程序的時間片

linux核心——3_(程式管理)系統的程式管理

單核CPU作業系統是僞并行的,程序排程就是根據時間片來的;

task_struck一個程序,task_struck[]程序向量表counter;

1)、counter–在哪裡用:程序的排程就是task_struck[]程序連結清單的檢索,找時間片最大的那個程序對象(task_struck),然後進行調用,直到時間片為0,退出之後在進行新一輪的調用。

2)、counter–在哪裡被設定:當全部的 task_struck[](task[])所有的程序的counter都為0,就進行新一輪的時間片配置設定;

3)、配置設定方式:優先級時間片輪轉排程算法,可以看出把優先級加到時間片上了,優先級越高時間片越大,就越先執行(*p)->counter = ((*p)->counter >> 1) +(*p)->priority;

linux核心——3_(程式管理)系統的程式管理

(二)、分析mktime.c

1、首先打開Mktime.c

linux核心——3_(程式管理)系統的程式管理

我們看看誰調用了kernel_mktime這個函數就知道誰在一開始就給他1970的時間了,搜尋一下發現有兩個函數調用了這個

linux核心——3_(程式管理)系統的程式管理

這裡可以看出時間是從cmos中讀出的,這個do…while用的很好,先讀一次,若第二次讀的和第一次不一樣就再讀一次,以防讀的過程中過去了1秒。然後kernel_mktime(&time)把時間傳回給全局變量start_time

linux核心——3_(程式管理)系統的程式管理

再看一下startup_time這個全局變量是幹什麼用的

linux核心——3_(程式管理)系統的程式管理

其中jiffies/HZ是開機用的秒數

linux核心——3_(程式管理)系統的程式管理
linux核心——3_(程式管理)系統的程式管理

注:linux一開始就很注重面向對象的程式設計,tm結構體就是一個對象,在系統中也不斷地在進行建立對象

繼續閱讀