20145231 《信息安全系统设计基础》第11周学习总结
20145231《信息安全系統設計基礎》第11周學習總結
教材學習內容總結
異常
異常是異常控制流的一種形式,由硬件和操作系統實現。簡單來說,就是控制流中的突變。
出現異常的處理方式:
1.處理器檢測到有異常發生
2.通過異常表,進行間接過程調用,到達異常處理程序
3.完成處理后:①返回給當前指令②返回給下一條指令③終止
1、異常處理
異常號:系統為每種類型的異常分配的唯一的非負整數。
異常表:系統啟動時操作系統就會初始化一張條轉變,使得條目k包含異常k的處理程序的地址。
異常號是到異常表中的索引,異常表的起始地址放在異常表基址寄存器。
異常類似于過程調用,區別在:
1.處理器壓入棧的返回地址,是當前指令地址或者下一條指令地址。
2.處理器也把一些額外的處理器狀態壓到棧里
3.如果控制一個用戶程序到內核,所有項目都壓到內核棧里。
4.異常處理程序運行在內核模式下,對所有的系統資源都有完全的訪問權限。
2、異常的類別
故障指令:執行當前指令導致異常(陷阱、故障、終止)
中斷處理程序:硬件中斷的異常處理程序(中斷)
異常的類別如下圖。異步異常時有處理器外部的I/O設備中的事件產生的,同步異常時執行一條指令的直接產物
陷阱是有意的異常,是執行一條指令的結果,最重要的用途——系統調用
故障是由錯誤狀況引起,可能能夠被故障處理程序修正。結果要么重新執行指令(就是返回當前指令地址),要么終止。典型示例:缺頁異常
3、Linux/IA32系統中的異常
IA32系統中的異常列表如下
每一個系統調用都有一個唯一的整數號,對應于一個到內核中跳轉表的偏移量
在IA32中,系統調用通過一條陷阱指令提供:
int n;//n為異常號
所有的到Linux系統調用的參數都是通過寄存器傳遞的。慣例如下:
%eax:包含系統調用號
%ebx,%ecx,%edx,%esi,%edi,%ebp:包含最多六個任意參數
%esp:棧指針,不能使用
進程
進程的經典定義:一個執行中的程序的實例。
系統中的每個程序都是運行在某個進程的上下文中的。
上下文:由程序正確運行所需的狀態組成的。
進程提供給應用程序的關鍵抽象:
一個獨立的邏輯控制流:獨占的使用處理器
一個私有的地址空間:獨占的使用存儲器系統
1、邏輯控制流
一系列的程序計數器PC的值,分別唯一的對應于包含子啊程序的可執行目標文件中的指令,或者是包含在運行時動態鏈接到程序的共享對象中的指令,這個PC值的序列就叫做邏輯控制流。
進程是輪流使用處理器的。每個進程執行它的流的一部分,然后被搶占,然后輪到其他進程。但是進程可以向每個程序提供一種假象,好像它在獨占的使用處理器。
邏輯流示例:異常處理程序、進程、信號處理程序、線程、Java進程
2、并發流
一個邏輯流的執行在時間上與另一個流重疊。(與是否在同一處理器無關)
兩個流并發的運行在不同的處理機核或者計算機上。
并行流并行的運行,并行的執行。
3、私有地址空間
進程為程序提供的假象,好像它獨占的使用系統地址空間。一般而言,和這個空間中某個地址相關聯的那個存儲器字節是不能被其他進程讀寫的。
4、用戶模式和內核模式
用戶模式和內核模式的區別就在于用戶的權限上,權限指的是對系統資源使用的權限。
具體的區別是有無模式位,有的話就是內核模式,可以執行指令集中的所有指令,訪問系統中任何存儲器位置;沒有就是用戶模式。
進程從用戶模式變為內核模式的唯一方法是通過異常——中斷,故障,或者陷入系統調用。
Linux的聰明機制——/proc文件系統,將許多內核數據結構的內容輸出為一個用戶程序可以讀的文本文件的層次結構。
5、上下文切換
操作系統內核使用上下文切換這種較高層形式的異常控制流來實現多任務。上下文切換機制建立在較底層異常機制之上。
上下文:內核重新啟動一個被搶占的進程所需的狀態。由一些對象的值組成:
?通用目的寄存器
?浮點寄存器
?程序計數器
?用戶棧
?狀態寄存器
?內核棧
?內核數據結構:頁表、進程表、文件表
上下文切換機制:
1.保存當前進程的上下文2.恢復某個先前被搶占的進程被保存的上下文3.將控制傳遞給這個新恢復的進程。
可能發生上下文切換的原因:?內核代表用戶執行系統調用時?中斷
系統調用錯誤處理
系統會使用錯誤處理包裝函數,系統級函數是小寫,他們的包裝函數名大寫,包裝函數調用基本函數,有任何問題就終止,如果沒有問題和基本函數是一樣的。
進程控制
1、獲取進程ID
每個進程都有一個唯一的正數進程ID(PID)。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); 返回調用進程的PID
pid_t getppid(void); 返回父進程的PID(創建調用進程的進程)
2、創建和終止進程
進程總是處于下面三種狀態之一:運行;停止:被掛起且不會被調度;終止:永遠停止
終止的原因:
1.收到信號,默認行為為終止進程
2.從主程序返回
3.調用exit函數
父進程通過調用fork函數來創建一個新的運行子進程。fork函數定義如下:#include <sys/types.h>
#include <unistd.h>pid_t fork(void);
fork函數只被調用一次,但是會返回兩次:父進程返回子進程的PID,子進程返回0.如果失敗返回-1
調用fork函數n次,產生2的n次方個進程。
終止進程用exit函數:
#include <stdlib.h>void exit(int status);
exit函數以status退出狀態來終止進程
3、回收子進程
進程終止后還要被父進程回收,否則處于僵死狀態。
如果父進程沒有來得及回收,內核會安排init進程來回收他們。init進程的PID為1.
一個進程可以通過調用waitpid函數來等待它的子進程終止或停止。waitpid函數的定義如下:
#include <sys/types.h>
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
成功返回子進程PID,如果WNOHANG,返回0,其他錯誤返回-1.
判斷等待集合的成員——pid:
?pid>0:等待集合是一個單獨子進程,進程ID等于pid
?pid=-1:等待集合是由父進程所有的子進程組成
?其他
修改默認行為——options:
檢查已回收子進程的退出狀態——status,在wait.h頭文件中定義了解釋status參數的幾個宏:
?WIFEXITED:如果子進程通過調用exit或一個返回正常終止,就返回真
?WEXITSTATUS:返回一個正常終止的子進程的退出狀態。只有在WIFEXITED返回為真時,才會定義這個狀態
?WIFSIGNALED:如果子進程是因為一個未被捕獲的信號終止的,那么返回真
?WTERMSIG:返回導致子進程終止的信號的編號。只有在WIFSIGNALED返回為真時才定義這個狀態
?WIFSTOPPED:如果引起返回的子進程當前是被停止的,那么返回真
?WSTOPSIG:返回引起子進程停止的信號的數量。只有在WIFSTOPPED返回為真時才定義這個狀態
錯誤條件:如果調用進程沒有子進程,那么waitpid返回-1,并且設置errno為ECHILD。
如果waitpid被一個信號中斷,那么他返回-1,并且設置errno為EINTR。
wait函數是waitpid函數的簡單版本,wait(&status)等價于waitpid(-1,&status,0).成功返回子進程pid,出錯返回-1:
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *status);
4、讓進程休眠
sleep函數使一個進程掛起一段指定的時間。定義如下:
#include <unistd.h>signed int sleep(unsigned int secs);
返回值是剩下還要休眠的秒數,如果到了返回0.
pause函數讓調用函數休眠,直到該進程收到一個信號:
#include <unistd.h>int pause(void);
5、加載并運行程序——execve函數
execve函數調用一次,從不返回:
#include <unistd.h>int execve(const char *filename, const char *argv[], const char *envp[]);
成功不返回,失敗返回-1.
getnev函數在環境數組中搜尋字符串"name=value",如果找到了就返回一個指向value的指針,否則返回null:
#include <stdlib.h>char *getenv(const char *name);
若存在則為指向name的指針,無匹配是null
setenv和unsetenv函數:如果環境數組包含"name=oldvalue"的字符串,unsetenv會刪除它,setenv會用newvalue代替oldvalue,只有在overwrite非零時成立。
如果name不存在,setenv會將"name=newvalue"寫進數組。
#include <stdlib.h>int setenv(const char *name, const char *newvalue, int overwrite);
若成功返回0,錯誤返回-1 void unsetenv(const char *name);
無返回值
fork函數和execve函數的區別
?fork函數是創建新的子進程,是父進程的復制體,在新的子進程中運行相同的程序,父進程和子進程有相同的文件表,但是不同的PID
?execve函數在當前進程的上下文中加載并運行一個新的程序,會覆蓋當前進程的地址空間,但是沒有創建一個新進程,有相同的PID,繼承文件描述符。
信號
1、信號術語
傳遞一個信號到目的進程的兩個步驟:發送信號和接收信號。
發送信號的原因:
1.內核檢測到一個系統事件2.一個進程調用了kill函數,顯式的要求內核發送一個信號給目的進程。
一個進程可以發送信號給它自己。
接收信號:
1.忽略2.終止3.執行信號處理程序,捕獲信號
待處理信號:
?只發出沒有被接收的信號
?任何時刻,一種類型至多只會有一個待處理信號,多的會被直接丟棄
?一個進程可以選擇性的阻塞接受某種信號,被阻塞仍可以被發送,但是不會被接收
?一個待處理信號最多只能被接收一次。
?pending:待處理信號集合
?blocked:被阻塞信號集合。
2、發送信號——基于進程組
進程組:
?每個進程都只屬于一個進程組。
?進程組ID:正整數
?一個子進程和他的父進程屬于同一進程組。
?查看進程組id:getpgrp
?修改進程組:setpgid
/bin/kill程序可以向另外的進程發送任意的信號,格式是:
/bin/kill -n m
n是信號,m是進程或進程組
當n>0時,發送信號n到進程m
當n<0時,使信號|n|發送到進程組m中的所有進程。
進程通過調用kill函數發送信號給其他進程。
進程可以通過調用alarm函數向它自己發送SIGALRM信號
#include <unistd.h>unsigned int alarm(unsigned int secs);
返回前一次鬧鐘剩余的秒數,若沒有返回0.
非本地跳轉
c語言中,用戶級的異常控制流形式,通過setjmp和longjmp函數提供。
setjump函數在env緩沖區中保存當前調用環境,以供后面longjmp使用,并返回0.
longjmp函數從env緩沖區中恢復調用環境,然后觸發一個從最近一次初始化env的setjmp調用的返回。
然后setjmp返回,并帶有非零的返回值retval
注:setjmp函數只被調用一次,但返回多次;longjmp函數被調用一次,但從不返回。
操作進程的工具
STRACE:打印一個正在運行的程序和他的子程序調用的每個系統調用的痕跡
PS:列出當前系統中的進程,包括僵死進程
TOP:打印出關于當前進程資源使用的信息
PMAP:顯示進程的存儲器映射
實踐學習總結
數組指針、指針數組、函數指針、指針函數的區別
1.指針數組是數組,數組里的元素是指針
int *daytab[13]
2.數組指針是指針,指向一個類型和元素個數都固定的數組
int (*daytab1)[13]
3.指針函數是函數,返回值類型是指針
int *comp()
4.函數指針是指針,指向函數的指針,函數名就是函數指針
int (*comp1)()
關于exec.1
exec1.c代碼運行如下
exec1.c中execvp()會從PATH 環境變量所指的目錄中查找符合參數file 的文件名,找到后便執行該文件,然后將第二個參數argv傳給該欲執行的文件
如果執行成功則函數不會返回,執行失敗則直接返回-1,失敗原因存于errno中
exevp函數調用成功沒有返回,所以沒有打印出“* * * ls is done. bye”這句話
想要更深入的理解這段代碼,于是將ls -l變換成man -k,修改代碼
重新編譯運行結果如下
結果同樣也是執行了man -k語句,還是沒有返回“* * * man is done. bye”
關于exec2.c
exec2.c代碼運行如下
exec2與exec1的區別就在于exevp函數的第一個參數,exec1傳的是ls,exec2直接用的arglist[0],不過由定義可得這兩個等價,所以運行結果是相同的
若將exevp函數傳入的arglist[0]改為arglist[1],此時exevp函數沒有調用成功,于是打印出“* * * ls is done. bye”這句話
關于exec3.c
exec3.c代碼運行如下
函數中execlp()會從PATH 環境變量所指的目錄中查找符合參數file的文件名,找到后便執行該文件,然后將第二個以后的參數當做該文件的argv[0]、argv[1]……最后一個參數必須用空指針(NULL)作結束
以下四個函數中任意一個的運行結果都與其他三個完全一致。
execv()``execvp()``execvpe()``execlp()
關于forkdemo1.c
forkdemo1.c代碼先是打印進程pid,然后調用fork函數生成子進程,休眠一秒后再次打印進程id,這時父進程打印子進程pid,子進程返回0
運行結果如下
關于forkdemo2.c
這個代碼調用兩次fork,一共產生四個子進程,所以會打印四個aftre輸出
代碼運行如下
關于forkdemo3.c
fork產生子進程,父進程返回子進程pid,不為0,所以輸出父進程的那句話,子進程返回0,所以會輸出子進程那句話
運行如下
關于forkdemo4.c
先打印進程pid,然后fork創建子進程,父進程返回子進程pid,所以輸出parent一句,休眠十秒;子進程返回0,所以輸出child與之后一句
運行如下
關于forkgdb.c
父進程打印是先打印兩句,然后休眠一秒,然后打印一句,子進程先打印一句,然后休眠一秒,然后打印兩句。并且這兩個線程是并發的,所以可以看到在一個線程休眠的那一秒,另一個線程在執行,并且線程之間相互獨立互不干擾
運行如下
關于waitdemo1.c
waitdemo1.c的功能是如果有子進程,則終止子進程,成功返回子進程pid。運行如下
關于waitdemo2.c
waitdemo2.c比起1來就是多了一個子進程的狀態區分,把狀態拆分成三塊,exit,sig和core
運行如下
關于testbuf
testbuf1.c代碼運行如下
testbuf2.c代碼運行如下
testbuf1.c和testbuf2.c代碼運行結果一致,因為fflush(stdout)的效果和換行符\n是一樣的
testbuf3.c將內容格式化輸出到標準錯誤、輸出流中
testpid.c代碼輸出當前進程pid和當前進程的父進程的pid
testsystem.c代碼中system()——執行shell命令,也就是向dos發送一條指令。這里是后面可以跟兩個參數,然后向dos發送這兩個命令,分別執行
如下圖,輸入ls和dir兩個指令后,可以看到分別執行了
關于psh1.c
psh1.c代碼的效果是輸入要執行的指令,回車表示輸入結束,然后輸入的每個參數對應到函數中,再調用對應的指令
運行結果如下
關于psh2.c
psh2.c比起1來,多了循環判斷,不退出的話就會一直要你輸入指令,并且對于子程序存在的狀態條件
運行如下
關于signal
sigdemo1.c程序連續輸出五個hello,每兩個之間的間隔時間為2秒,且在此期間輸入的Ctrl+C都被處理成打印OUCH
sigdemo2.c一直輸出haha,按Ctrl+C不能停止
sigactdemo2.c休息seconds秒后返回;或者被信號中斷且信號處理函數返回后sleep()返回0。所以如果不計較返回值的話,pause()的功能相當于無限期的sleep()
sigdemo2.c中:
SIG_DFL,SIG_IGN 分別表示無返回值的函數指針,指針值分別是0和1,這兩個指針值邏輯上講是實際程序中不可能出現的函數地址值。
SIG_DFL:默認信號處理程序
SIG_IGN:忽略信號的處理程序
sigdemo3.c根據代碼,在read函數不發生錯誤的情況下輸入什么,就輸出什么,輸入的Ctrl+C也無法終止程序,只有輸入quit的時候才會退出
sigactdemo.c中sigaction()會依參數signum指定的信號編號來設置該信號的處理函數。參數signum可以指定SIGKILL和SIGSTOP以外的所有信號
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
SA_RESETHAND:當調用信號處理函數時,將信號的處理函數重置為缺省值SIG_DFL
SA_RESTART:如果信號中斷了進程的某個系統調用,則系統自動啟動該系統調用
SA_NODEFER :一般情況下, 當信號處理函數運行時,內核將阻塞該給定信號。但是如果設置SA_NODEFER標記, 那么在該信號處理函數運行時,內核將不會阻塞該信號
學習過程中的問題和解決過程
教材學習中遇到的問題
對于教材中信號方面的代碼不太理解。
實踐中遇到的問題
編譯forkdemo1.c的時候出錯,根據提示修改源代碼,加入#include <stdio.h>重新編譯即可
代碼托管截圖
代碼統計
代碼鏈接:(https://git.oschina.net/xzhkuma1128/CSAPP2E)
心得體會
本周內容比較多,不僅是課本知識更要花時間實踐,對于代碼的理解還有些問題,看了書對于信號這部分真的不太理解,對于fork()函數因為老師上課時有講解,所以再看書就覺得好理解多了,希望老師能再講講信號這部分知識。
學習進度條
| 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
|---|---|---|---|
| 目標 | 30篇 | 400小時 | |
| 第一周 | 1/1 | 20/20 | 學習了Linux核心命令 |
| 第二周 | 1/2 | 21/41 | 學習了vim、gcc、gdb指令 |
| 第三周 | 1/3 | 20/61 | 學習了信息的表示和處理,了解了二進制在計算機系統中的重要性 |
| 第五周 | 1/4 | 20/81 | 學習了機器級程序,讀懂匯編代碼 |
| 第六周 | 1/5 | 19/100 | 了解了處理器對于指令的處理過程 |
| 第七周 | 1/6 | 18/118 | 了解了存儲器層次結構及存儲技術 |
| 第八周 | 2/8 | 15/133 | 對前幾周內容進行復習 |
| 第十周 | 3/12 | 10/148 | 學習Linux重要命令 |
| 第十一周 | 2/14 | 15/163 | 學習異常控制相關知識 |
轉載于:https://www.cnblogs.com/xzh20145231/p/6106980.html
總結
以上是生活随笔為你收集整理的20145231 《信息安全系统设计基础》第11周学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea上实现github代码同步
- 下一篇: 货车营运证年审多少钱