Linux -- 系統呼叫
-
在Linux裡,建立一個新程序,需要老程序呼叫
fork
來實現 -
老程序叫作父程序
(
Parent Process
),新程序叫作子程序 (Child Process
) -
當父程序呼叫
fork
建立程序的時候,子程序將各個子系統 為父程序建立的資料結構也全部 拷貝一份(包括程式程式碼)- 如果不進行特殊處理,父程序和子程序都按相同的程式碼邏輯進行下去
-
fork
系統呼叫的返回值- 如果當前程序 是子程序 ,就返回0
- 如果當前程序 是父程序 ,返回 子程序的程序號
-
依據
fork
的返回值執行不同的邏輯分支- 如果是父程序 ,執行原始邏輯
-
如果是子程序
,請求系統呼叫
execve
來執行另一個程式- 至此,子程序和父程序就徹底分道揚鑣 了,產生了一個fork(分支)
- 作業系統在啟動 時會先建立所有使用者程序 的 祖宗程序
-
父程序如果需要知道子程序的執行情況,
-
父程序可以請求系統呼叫
waitpid
,將子程序的程序號 作為引數傳給它 - 這樣父程序就知道子程序是否執行成功
-
父程序可以請求系統呼叫
記憶體管理
- 在作業系統中,每個程序都有自己的記憶體,互不干擾 ,有 獨立的程序記憶體空間
-
程式碼段
(
Code Segment
):存放 程式程式碼 -
資料段
(
Data Segment
):存放 程序在執行過程中產生的資料- 其中區域性變數 的部分,在當前函式執行的時候才起作用,當進入另一個函式時,區域性變數就會被釋放
-
也有動態分配
的,會較長時間儲存
,指明才銷燬的,這部分稱為堆
(
Heap
)
-
一個程序的記憶體空間是很大的,32位的是4G,但不可能有那麼多的實體記憶體,到真正需要
的時候再分配
- 程序自己不用的部分就不用管了,只有程序去使用部分記憶體的時候,才會使用記憶體管理的系統呼叫 來登記
-
但這不代表真的就對應到了實體記憶體,只有在真的寫入資料
的時候,發現沒有對應的實體記憶體
- 才會觸發一個中斷 , 現分配實體記憶體
-
在堆裡
分配記憶體的系統呼叫:
brk
和mmap
-
當分配的記憶體數量比較小
的時候,使用
brk
,會和原來的堆的資料連在一起- brk, sbrk - change data segment size
-
當分配到記憶體數量比較大
的時候,使用
mmap
,會重新劃分一塊區域- mmap, munmap - map or unmap files or devices into memory
-
當分配的記憶體數量比較小
的時候,使用
檔案管理
-
系統呼叫
-
對於已經存在的檔案,使用
open
開啟這個檔案,使用close
關閉這個檔案 -
對於不存在的檔案,使用
create
建立檔案 -
開啟檔案以後,使用
lseek
跳到檔案的某個位置 -
可以對檔案的內容進行讀寫,讀的系統呼叫是
read
,寫的系統呼叫是write
-
對於已經存在的檔案,使用
-
一切皆檔案
- 啟動一個程序,需要一個程式檔案,這是一個二進位制檔案
-
啟動的時候,需要載入一些配置檔案,例如yml、properties等,這是文字檔案
- 啟動之後會列印一些日誌,如果寫到硬碟上,也是文字檔案
- 如果把日誌列印到互動控制檯上,也是一個檔案,是stdout檔案
- 這個程序的輸出可以作為另一個程序的輸入,這種方式稱為管道 ,管道也是一個檔案
-
程序之間可以通過網路進行進行通訊,建立
Socket
,Socket
也是一個檔案 - 程序需要訪問外部裝置,裝置也是一個檔案
- 檔案都被儲存在資料夾裡面,資料夾也是一個檔案
-
程序執行起來,程序X的執行情況會在
/proc/${X}
目錄體現出來,該目錄下也是一系列的檔案
-
Linux會為每個檔案
分配一個檔案描述符
(
File Descriptor
,是一個整數 ) - 檔案操作是貫穿始終的,一切皆檔案的優勢: 統一了操作的入口
訊號處理
-
常見訊號
-
在執行一個程式的時候,在鍵盤上輸入
CTRL+C
,這是中斷訊號 ,正在執行的命令就會中止退出 - 非法訪問記憶體
- 硬體故障,裝置出現了問題
-
使用者程序通過
kill
函式,將一個使用者訊號傳送給另一個程序
-
在執行一個程式的時候,在鍵盤上輸入
-
對於一些不嚴重的訊號,可以忽略,但類似
SIGKILL
和SIGSTOP
是不能忽略的,可以執行訊號的預設動作- 每種訊號都定義了預設的動作,例如硬體故障,預設終止
-
也可以提供訊號處理函式
,通過請求系統呼叫
sigaction
,註冊一個訊號處理函式- sigaction, rt_sigaction - examine and change a signal action
程序間通訊
-
訊息佇列
(
Message Queue
)- 兩個程序間互動的資訊較小 ,這個訊息佇列在核心 裡
-
msgget
:建立一個新佇列, get a System V message queue identifier -
msgsnd
:將訊息傳送到訊息佇列, XSI message send operation -
msgrcv
:訊息接收方從佇列中取資料, XSI message receive operation
-
共享記憶體
- 兩個程序間互動的資訊較大
-
shmget
:建立一個共享記憶體塊- shmget - allocates a System V shared memory segment
-
shmat
:將共享記憶體對映 到自己的程序記憶體空間,然後就可以進行讀寫 了- shmat — XSI shared memory attach operation
-
存在競爭
問題,解決方案:
訊號量機制
(
Semaphore
)- 對於只允許一個程序訪問的資源,可以將訊號量設為1
-
當程序A要訪問的時候,會先請求系統呼叫
sem_wait
,如果此時沒有其他程序訪問,則佔用 這個訊號量 -
如果此時程序B要訪問,也會呼叫
sem_wait
,程序B必須等待 -
當程序A訪問完畢,會呼叫
sem_post
將訊號量釋放 ,程序B就可以訪問這個資源了
網路通訊
- 不同機器通過網路相互通訊,需要遵循相同的網路協議,即TCP/IP網路協議棧 ,Linux核心有對網路協議棧的實現
-
網路服務是通過套接字
Socket
來提供服務的,可以通過Socket
系統呼叫來建立一個Socket
-
Socket
是一個檔案 ,也有一個檔案描述符 ,也可以通過讀寫函式 進行通訊
系統呼叫定義
路徑:linux-5.0.7/arch/sh/include/uapi/asm/unistd_64.h
#define __NR_restart_syscall0 #define __NR_exit1 #define __NR_fork2 #define __NR_read3 #define __NR_write4 #define __NR_open5 #define __NR_close6 #define __NR_waitpid7 #define __NR_creat8 ...
glibc
-
glibc
是Linux下開源的標準C庫,由GNU
釋出 -
glibc
提供了豐富的API- 封裝了例如字串處理、數學運算等使用者態服務
- 封裝了作業系統提供的系統服務,即 系統呼叫的封裝
-
每個系統呼叫都對應至少一個
glibc
封裝的庫函式-
開啟檔案系統呼叫,作業系統->
sys_open
,glibc
->open
-
開啟檔案系統呼叫,作業系統->
-
glibc
中一個單獨的API可能會呼叫多個系統呼叫-
glibc
提供的printf
函式就會呼叫如sys_open
、sys_mmap
、sys_write
和sys_close
等系統呼叫
-
-
多個
glibc API
也可能只對應一個系統呼叫-
glibc
的malloc
、calloc
和free
等函式用來分配和釋放記憶體,都呼叫了核心的sys_brk
的系統呼叫
-