進程和程序的區別:
進程: 程序的一次執行過程 動態過程,進程的狀態屬性會發生變化
程序:存放在磁盤上的指令、數據的有序集合 是個文件,可直觀看到
程序program 靜態的概念,本身不會發生變化。指令誰來執行,數據誰來訪問?cpu!
但前提是cpu能夠接觸到,程序執行過程需要cpu、內存、以及相關的資源。
進程是動態的,需要執行時才創建,運行結束要回收,包括創建、調度、執行、消亡的過程。
二者是關系:無程序進程就無意義,是內容與形式的關系。
一個程序的執行,至少創建一個進程。
一個進程的內容 叫進程控制卡,PCB,它是個理論上的東西,不同的系統實現不一樣,Linux裡用task_struct 來描述
進程的特點:
動態性:進程的實質是程序在多道程序系統中的一次執行過程,進程是動態產生,動態消亡的
並發性:任何進程都可以同其他進程一起並發執行
獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位
異步性:由於進程間的相互制約,使進程具有執行的間斷性,即進程按各自獨立的、不可預知的速度向前推。
進程通訊原因:
數據傳輸
資源共享
通知事件
進程控制
進程互斥:多個進程同時要訪問一個共享資源時,同時只允許一個進程訪問,直到訪問結束,釋放後其他進程才能訪問。
臨界資源:同一時刻只允許一個進程訪問的資源,進程中訪問臨界資源的那段程序代碼稱為臨界資源。
進程同步:一組並發的進程按一定的順序執行,具有同步關系的一組並發進程為合作進程,合作進程間互相發送的信號稱為消息或事件。
死鎖:多個進程競爭一種資源而形成的一種僵局,若無外力作用,這些進程無法繼續向前推進。解決方法:主要有預防,使進程訪問資源的順序一致。
進程的類型:
交互進程
批處理
守護進程
進程的狀態
運行態(正在運行和准備運行的)
等待(可中斷等待、不可中斷等待)
僵屍態
停止態
進程的執行模式
用戶模式
內核模式
進程的調度
概念:按一定算法從一組待運行的進程中選出一個來占有CPU運行的。
調度方式:
搶占式
非搶占式
調度算法:
先來先服務
短進程優先
高優先級進程優先
時間片輪轉法
進程命令
ps –aux
top
kill (向特定PID發送信號)
bg/fg
nice/renice
創建進程:
#include<unistd.h>
#include<sys/types.h>
pid_t fork( void )
返回值:
成功返回兩個值,子進程返回0,父進程返回子進程ID;
出錯返回-1
函數說明:
一個現有進程可以調用fork函數創建一個新進程。由fork創建的新進程被稱為子進程(child process)。fork函數被調用一次但返回兩次。兩次返回的唯一區別是子進程中返回0值而父進程中返回子進程ID。子進程是父進程的副本,它將獲得父進程數據空間、堆、棧等資源的副本。
注意:
子進程持有的是上述存儲空間的“副本”,這意味著父子進程間不共享這些存儲空間。linux將復制父進程的地址空間內容給子進程,因此,子進程有了獨立的地址空間。
#include<unistd.h>
pid_t vfork( void )
函數說明
vfork創建的子進程與父進程共享數據段,而且由vfork創建的。子進程將先於父進程運行。
返回值:
如果vfork()成功則在父進程會返回新建立的子進程代碼(PID),而在新建立的子進程中則返回0。如果vfork失敗則直接返回-1,失敗原因存於errno中。
錯誤代碼 :
EAGAIN 進程數已達系統規定上限
ENOMEM 內存不足,無法配置核心所需的數據結構空間。
vfork()用法與fork()異同點:
fork():子進程拷貝父進程的數據段,代碼段. vfork():子進程與父進程共享數據段.
fork():父子進程的執行次序不確定。vfork():保證子進程先運行,在調用exec或exit之前與父進程數據是共享的,在它調用exec,或exit之後父進程才可能被調度運行
vfork()保證子進程先運行,在她調用exec或exit之後父進程才可能被調度運行。
創建子進程,(寫時拷貝)拷貝數據段、代碼 哪些不拷貝?(完整拷貝需要與線程區別)
進程的退出
return 0
void exit( int status )<stdlib.h>
void _exit( int status ) <unistd.h>
參數:
status 傳遞進程結束時的狀態,通常0表示正常結束,非零表示出現錯誤,可以使用wait系統調用來接收子進程的返回值。
exit( )和_exit( )區別
_exit( ) 直接結束進程,清除其使用的內存空間,銷毀其在內核中的數據結構,不刷新緩沖區。
exit( ) 退出時要清理緩存區
exec函數族
找到可執行文件,用它來取代原調用進程的數據段、代碼、棧。進程號除外。
#include <unistd.h> int execl ( const char *path, const char *arg, …… ); int execv ( const char *path, char * const argv[ ] ); int execle ( const char *path, const char *arg, …… , char * const envp[ ] ); int execve( const char *path, char * const arg[ ], …… , char * const envp[ ] ); int execlp( const char *file, const char *arg, …… ); int execvp ( const char *file, char *const arg, …… );
文件查找方式:
p 可以只給出文件名,系統會查找環境變量$PATH所包含的路徑。
參數表傳遞方式:
逐個列舉或者將所有參數通過指針數組傳遞。
l(list) 表示逐個列舉。
v(vertor) 表示將所有參數構造成指針數組傳遞。
環境變量的使用:
e(enviromen) 可以在envp[ ]中傳遞當前進程所使用的環境變量
使用exec函數族時必須加上錯誤判斷語句
常見的錯誤原因:
找不到文件或路徑,errno被設置為ENOENT
數組argv和envp忘記用NULL結束,errno被設置為EFAULT
沒有對應的可執行文件運行權限,errno被設置為EACCESS
進程的回收
<sys/types.h>
<sys/wait.h>
等待回收子進程的退出狀態
pid_t wait(
int *status
);
返回值:
成功 >0 回收子進程的pid
失敗 -1。
pid_t waitpid (
pid_t pid,
int * status,
int options
);
參數:
pid:
pid>0:只等待進程ID等於pid的子進程,不管已經有其他子進程運行結束退出了,只要指定的子進程還沒有結束,waitpid就會一直等下去。
pid=-1:等待任何一個子進程退出,此時和wait作用一樣。
pid=0:等待其組ID等於調用進程的組ID的任一子進程。
pid<-1:等待其組ID等於pid的絕對值的任一子進程。
status
status指向的對象用來保存子進程退出時的狀態
若為空,表示忽略子進程退出時的狀態
若不為空,表示保存子進程退出時的狀態
options
WNOHANG:若由pid指定的子進程並不立即可用,則waitpid不阻塞,此時返回值為0
WUNTRACED:若某實現支持作業控制,則由pid指定的任一子進程狀態已暫停,且其狀態自暫停以來還未報告過,則返回其狀態。
0:同wait,阻塞父進程,等待子進程退出。
三種情況:
沒有子進程或子進程都退出,立即返回-1
沒有任何子進程結束,父進程會阻塞,直到有子進程退出。哪個先結束先回收哪個
有子進程結束,回收返回
wait/waitpid: waitpid(-1, NULL, 0) == wait(NULL)
最低字節的含義:0表示正常結束;非0表示被信號結束,值為信號的類型
第二字節:正常結束:子進程的返回值;非正常結束:值為0
應用:用父子進程實現文件的復制,每個進程各拷貝一份。
需注意:沒打開一個文件,內核空間創建一個struct file結構體,裡面有個成員叫f_ops。
./multi_copy <src_file> <dst_file>
步驟如下:
檢查參數的個數
打開源文件、目標文件(注意打開方式和權限)
創建子進程
子進程中:關源、目標文件, 重新打開後復制後半部分
父進程中:復制前半部分
linux守護進程(Daemon進程)
守護進程:是運行在後台的一種特殊進程。它獨立於控制終端並且周期性地執行某種任務或等待處理某些發生的事件。
守護進程的特點:
是後台服務進程,生存期較長的進程,通常獨立於控制終端,周期性的執行某種任務或等待處理某些發生的事件。
系統啟動時開始運行,在系統關閉時終止
進程查看
ps –axj
父進程ID : PPID
進程ID : PID(標識進程的唯一描述符)
進程組ID : PGID
會話期ID : SID
終端ID : TTY
終端進程組ID : TPGID
狀態 : STAT
用戶 : UID
運行時間 : TIME
指令: COMMAND
守護進程的創建:
fork ()創建子進程,父進程退出。
由於守護進程是脫離控制終端的,因此,完成第一步後就會在shell終端裡造成一程序已經運行完畢的假象。之後的所有後續工作都在子進程中完成,而用戶在shell終端裡則可以執行其他的命令,從而在形式上做到了與控制終端的脫離
由於父進程已經先於子進程退出,會造成子進程沒有父進程,從而變成一個孤兒進程。在Linux中,每當系統發現一個孤兒進程,就會自動由1號進程收養。原先的子進程就會變成init進程的子進程。
setsid()子進程中創建新會話
進程組:
一個或多個進程的集合。進程組由進程組ID來唯一標識。
每個進程組都有一個組長進程,進程組ID就是組長進程的進程號。
會話期:一個或多個進程組的集合
setsid函數用於創建一個新的會話,並使得當前進程成為新會話組的組長
setsid函數能夠使進程完全獨立出來,從而脫離所有其他進程的控制。
#include <unistd.h>
pid_t setsid(void)
chdir(“/”) 改變工作目錄
通常的做法是讓“/”或”/tmp”作為守護進程的當前工作目錄 。
在進程運行過程中,當前目錄所在的文件系統是不能卸載的。
chdir函數可以改變進程當前工作目錄
umask(0) 重設文件權限掩碼
文件權限掩碼是指文件權限中被屏蔽掉的對應位。把文件權限掩碼設置為0,可以增加該守護進程的靈活性。設置文件權限掩碼的
通常方法為umask(0)
close(fd)關閉文件描述符
新建的子進程會從父進程那裡繼承所有已經打開的文件。
在創建完新的會話後,守護進程已經脫離任何控制終端,應當關閉用不到的文件。
fdtablesize = getdtablesize();
for (fd = 0; fd < fdtablesize; fd++)
close(fd);
這些被打開的文件可能永遠不會被守護進程讀或寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法卸載
從終端輸入的字符不可能達到守護進程,守護進程中用常規的方法(如printf)輸出的字符也不可能在終端上顯示出來。所以,文件描述符為0、1和2的三個文件(對應標准輸入、標准輸出和標准錯誤這三個流)已經失去了存在的意義,也應被關閉。
啟動:
在Linux系統啟動時從啟動腳本/etc/rc.d中啟動,由作業規劃進程crond啟動
用戶終端(通常是 shell)執行。
進程間的通信
AT&T的貝爾實驗室,對Unix早期的進程間通信進行了改進和擴充,形成了“system V IPC”,其通信進程主要局限在單個計算機內
BSD(加州大學伯克利分校的伯克利軟件發布中心),跳過了該限制,形成了基於套接字(socket)的進程間通信機制
傳統的進程間通信方式
無名管道(pipe)
有名管道(fifo)
信號(signal)
System V IPC對象
共享內存(share memory)
消息隊列(message queue)
信號燈(semaphore)
BSD
套接字(socket)
UNIX IPC(InterProcess Communication)
無名管道pipe
特點:
只能用在有血緣關系的進程之間的通信、半雙工(單方向的數據)。
類似於文件IO的讀寫,只存在於內存中。
創建:
#include <unistd.h>
int pipe(
int fd[2] //
fd:包含兩個元素的整型數組(
fd[0]固定用於讀管道,而fd[1]固定用於寫管道。)
);
返回值
成功:0,
出錯:-1.
pipe總結:
讀端:
寫端存在
管道中有數據,返回讀取的字節數。
無數據,阻塞到有數據。
寫端不存在
管道中有數據,返回讀取的字節數。
無數據,返回0,所以,read返回0意味著寫端關閉了。
寫端:
讀端存在
空間足,返回寫入的字節數。
空間不足,有多少寫多少,直到寫完返回。
讀端不存在
進程被SIGPIPE信號終止。
有名管道(FIFO)
特點:
類似隊列、全雙工(可讀可寫)
有名管道可以使互不相關的兩個進程互相通信。有名管道可以通過路徑名來指出,並且在文件系統中可見
進程通過文件IO來操作有名管道
有名管道遵循先進先出規則
不支持如lseek() 操作
創建管道:
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
int mkfifo(
const char *filename,//要創建的管道。
mode_t mode //指定創建的管道的訪問權限,一般用8進制數表示.
);
返回值
成功:0
出錯:-1,並設置相應的errno.
errno:
EACCESS參數filename所指定的目錄路徑無可執行的權限
EEXIST參數filename所指定的文件已存在
ENAMETOOLONG參數filename的路徑名稱太長
ENOENT參數filename包含的目錄不存在
ENOSPC文件系統的剩余空間不足
EROFS參數filename指定的文件存在於只讀文件系統內
fifo總結:
第一 O_RDONLY O_WRONLY
讀端open(O_RDONLY…) 寫端 open(O_WRONLY…), 只有讀或只有寫端時,open阻塞。驗證,reader和writer程序,只運行一個。
寫端關閉,只剩下讀端時,read立即返回0.
讀端關閉,只剩下寫端時,寫端被SIGPIPE信號終止。
在一般情況中(沒有說明O_NONBLOCK),只讀打開要阻塞到某個其他進程為寫打開此FIFO。類似,為寫而打開一個FIFO要阻塞到某個其他進程為讀而打開它。
第二 O_NONBLOCK
當打開一個FIFO時,非阻塞標志(O_NONBLOCK)產生下列影響:
如果指定了O_NONBLOCK,則只讀打開立即返回。但是,如果沒有進程已經為讀打開一個FIFO,那麼,只寫打開將出錯返回,其errno是ENXIO。
第三 O_RDWR
讀端和寫端都是O_RDWR方式,兩端都運行,若寫端寫完後退出了(輸入quit)。則讀端是以O_RDWR方式打開的,相當於自己也是一個寫端,寫端存在,又沒東西讀,也會阻塞。
信號
是軟件層面對中斷的一種模擬,唯一的異步方式。
進程隊信號的響應方式:
缺省:Linux對每種信號都規定了默認操作(signal(SIGUSR1,SIG_DFL))。
忽略:但SIGKILL、SIGSTOP不能被忽略,只能按默認操作去執行(signal(SIGUSER1,SIG_IGN))。
捕捉:按指定的操作去執行(signal(SIGUSR1,fun//fun指向用戶自定義函數的指針))。
關閉守護進程可發信號,普通用戶只能給自己創建的進程發信號。
信號的生存周期
信號的使用場合:
後台進程需要使用信號,如xinetd
如果兩個進程沒有親緣關系,無法使用無名管道
如果兩個通信進程之一只能使用標准輸入和標准輸出,則無法使用FIFO
常用信號
信號名:含義(默認操作)
SIGHUP:該信號在用戶終端連接(正常或非正常)結束時發出,通常是在終端的控制進程結束時,通知同一會話內的各個作業與控制終端不再關聯。(終止)
SIGINT:該信號在用戶鍵入INTR字符(通常是Ctrl-C)時發出,終端驅動程序發送此信號並送到前台進程中的每一個進程。(終止)
SIGQUIT:該信號和SIGINT類似,但由QUIT字符(通常是Ctrl-\)來控制。(終止)
SIGILL:該信號在一個進程企圖執行一條非法指令時(可執行文件本身出現錯誤,或者試圖執行數據段、堆棧溢出時)發出。(終止)
SIGFPE:該信號在發生致命的算術運算錯誤時發出。這裡不僅包括浮點運算錯誤,還包括溢出及除數為0等其它所有的算術的錯誤。(終止)
SIGKILL:該信號用來立即結束程序的運行,並且不能被阻塞、處理和忽略。(終止)
SIGALRM: 該信號當一個定時器到時的時候發出。(終止)
SIGSTOP:該信號用於暫停一個進程,且不能被阻塞、處理或忽略。(暫停進程)
SIGTSTP:該信號用於暫停交互進程,用戶可鍵入SUSP字符(通常是Ctrl-Z)發出這個信號。(暫停進程)
SIGCHLD :子進程改變狀態時,父進程會收到這個信號。(忽略)
SIGABORT :該信號用於結束進程。(終止)
SIGUSR1(10) SIGUSR2(12) 保留給用戶使用。
SIGKILL及SIGSTOP不能忽略、不能捕捉。
kill –l 命令查看系統支持的信號列表
信號相關的函數
網絡的服務器模型裡信號捕捉裡將進程回收 waitpid(-1, NULL, WNOHANG);
發送信號
#include <signal.h>
#include <sys/types.h>
int kill(
pid_t pid,
int sig
);
int raise(
int sig
);
參數:
pid:
正數:要接收信號的進程的進程號
0:信號被發送到所有和pid進程在同一個進程組的進程
-1:信號發給所有的進程表中的進程(除了進程號最大的進程外)
sig:信號
返回值
成功:0
出錯:-1
kill和raise的區別
可以發送信號給進程或進程組(實際上,kill系統命令只是kill函數的一個用戶接口)。
raise函數允許進程向自己發送信號。
#include <unistd.h>
unsigned int alarm(
unsigned int seconds //指定秒數
);
返回值
查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/
成功:如果調用此alarm()前,進程中已經設置了鬧鐘時間,則返回上一個鬧鐘時間的剩余時間,否則返回0。
出錯:-1
函數說明:
它在進程中設置一個定時器。當定時器指定的時間到時,內核就向進程發送SIGALARM信號,取消鬧鐘alarm(0);
int pause(void)
返回值
-1,並且把error值設為EINTR。
函數說明:
pause()
讓進程睡眠,將當前進程處於等待態(可中斷)直到有信號。
信號的處理
一個進程可以設定對信號的相應方式,
信號處理的主要方法有兩種。
使用簡單的signal()函數
使用信號集函數組
信號綁定函數:
#include <signal.h>
void (
*signal(
int signum,
void (*handler)(int))
)(int);
等價於:
typedef void (fun)(int);
fun * signal(signum, fun * handler)
參數
signum:指定信號
handler:
SIG_IGN:忽略該信號。
SIG_DFL:采用系統默認方式處理信號。
自定義的信號處理函數指針
返回值
成功:設置之前的信號處理方式
出錯:-1
sigemtpyset(&set)
sigaddset(&set, signum)
sigprocmask(SIG_UNBLOCK|SIG_BLOCK, &set, NULL)
System V IPC對象
IPC對象,都需要key來產生,通過ID來操作對象。
共享內存
共享內存塊提供了在任意數量的進程之間進行高效雙向通信的機制。每個使用者都可以讀取寫入數據,但是所有程序之間必須達成並遵守一定的協議,以防止諸如在讀取信息之前覆寫內存空間等競爭狀態的出現。
Linux無法嚴格保證提供對共享內存塊的獨占訪問,甚至是在您通過使用IPC_PRIVATE創建新的共享內存塊的時候也不能保證訪問的獨占性。 同時,多個使用共享內存塊的進程之間必須協調使用同一個鍵值。
產生一個key值
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(
const char *pathname,
//就是你指定的文件名(已經存在的文件名),一般使用當前目錄'.'。
int proj_id
id//id是子序號。(一般填存任意字母或者數字)
)
返回值:
成功:一個key值,
失敗:-1,並設置errno.
注意:
P1 創建的IPC對象如何讓另一進程知道ID號?ID是系統分配的。但在創建對象時可以指定key,或者可以事先約定好
第一個進程用key來創建IPC對象,第二個進程只需要通過key來打開IPC對象
共享內存的特點:
共享內存是一種最為高效的進程間通信方式,進程可以直接讀寫內存,而不需要任何數據的拷貝。
共享內存的使用包括如下步驟:
創建/打開共享內存 :需要判斷是否已經創建過.
映射共享內存,即把指定的共享內存映射到進程的地址空間用於訪問.
撤銷共享內存映射.
刪除共享內存對象.
調試:
查看系統中的對象ipcs
刪除系統中的對象ipcrm
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
創建/打開 共享內存
int shmget(
key_t key, //
IPC_PRIVATE或
ftok的返回值,
看到很多key的值是0,是系統中用IPC_PRIVATE創建的。
int size, //
共享內存區大小
int shmflg
);
參數:
shmflg:標志位,
同open函數的權限位,也可以用8進制表示法。
用法:
0666 打開一個共享內存,不能創建。如果與key關聯的不存在,會返回-1。
0666|IPC_CREAT 不存在就創建,存在就打開
有時需初始化,創建後要初始化0666|IPC_CREAT|IPC_EXCL 返回-1,並設置相應errno。 errno=EEXIST表示已存在。
例:
int shmid;
if( (shmid = shmget(key, 128, 0666|IPC_CREAT|IPC_EXCL)) < 0)
{ if(errno == EEXIST)
{ shmid = shmget(key, 128, 0666)} //已經存在的情況,打開就可以。
返回值
成功:共享內存段標識符
出錯:-1
映射 共享內存
void *shmat(
int shmid, //
要映射的共享內存區標識符
const void *shmaddr, //
將共享內存映射到指定地址(若為NULL,則表示由系統自動完成映射)
int shmflg
);
參數:
shmflg:
SHM_RDONLY:共享內存只讀
默認0:共享內存可讀寫
返回值
成功:映射後的地址;
出錯:-1。
撤銷共享內存映射
int shmdt(
const void *shmaddr //共享內存映射後的地址
);
返回值:
成功:0,
失敗:返回-1
刪除共享內存對象
int shmctl(
int shmid, //要操作的共享內存標識符
int cmd,
struct shmid_ds *buf // 指定IPC_STAT/IPC_SET時用以保存/設置屬性
);
參數:
cmd :
IPC_STAT (獲取對象屬性)
IPC_SET (設置對象屬性)
IPC_RMID (刪除對象)
返回值:
成功:0,
失敗:返回-1
消息隊列(Message queue)
消息隊列的操作包括創建或打開消息隊列、添加消息、讀取消息和控制消息隊列。
可看成是多個FIFO的集合,消息類型.
消息隊列隨內核持續,除內核重啟或人工刪除。
消息隊列對應唯一的鍵值
消息結構體
struct msgbuf
{
long mtype; //消息類型
char mtext[N]
//消息正文
};
#define LEN (sizeof(MSG)- sizeof(long))
<sys/types.h>
<sys/ipc.h>
<sys/msg.h>
創建/打開消息隊列(
創建的消息隊列的數量會受到系統消息隊列數量的限制
)
int msgget(
key_t key, //
和消息隊列關聯的key值
int flag //消息隊列的訪問權限
);
返回值:
成功:消息隊列的ID
出錯:-1
添加消息(
按照類型把消息添加到已打開的消息隊列末尾
)
int msgsnd(
int msqid, //消息隊列的ID
const void *msgp, //指向消息結構msgbuf.
size_t size, //
發送的消息正文的字節數(LEN)
int flag //IPC_NOWAIT:消息沒有發送完成函數也會立即返回;0:直到發送完成函數才返回。
);
返回值:
成功:0
出錯:-1
讀取消息(
按照類型把消息從消息隊列中取走
)
int msgrcv(
int msgid, //消息隊列的ID
void* msgp, //接收消息的緩沖區
size_t size, //要接收的消息的字節數
long msgtype,
int flag
);
參數:
msgtype:
0:隊列中最早的消息
>0:指定接收類型
<0:按優先級接收 接收消息隊列中類型值不小於msgtyp的絕對值且類型值又最小的消息。
flag:
0:若無消息函數會一直阻塞
IPC_NOWAIT:若沒有消息,進程會立即返回ENOMSG。
返回值:
成功:讀取的消息的長度
出錯:-1
控制消息隊列(
可以完成多項功能
)
int msgctl (
int msgqid, //消息隊列的隊列ID
int cmd,
struct msqid_ds *buf
);
參數:
cmd:
IPC_STAT:讀取消息隊列的屬性,並將其保存在buf指向的緩沖區中。
IPC_SET:設置消息隊列的屬性。這個值取自buf參數。
IPC_RMID:從系統中刪除消息隊列。
buf:消息隊列緩沖區
返回值:
成功:0
出錯:-1
信號燈(semaphore)
信號燈也叫信號量。它是不同進程間或一個給定進程內部不同線程間同步的機制。
信號燈種類:
posix有名信號燈
posix基於內存的信號燈(無名信號燈)
System V信號燈(IPC對象)
二值信號燈:值為0或1。與互斥鎖類似,資源可用時值為1,不可用時值為0。
計數信號燈:值在0到n之間。用來統計資源,其值代表可用資源數。
System V的信號燈是一個或者多個信號燈的一個集合。其中的每一個都是單獨的計數信號燈。System V 信號燈由內核維護
Posix信號燈指的是單個計數信號燈
信號燈的應用:
等待操作是等待信號燈的值變為大於0,然後將其減1;而釋放操作則相反,用來喚醒等待資源的進程或者線程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
獲取信號量標識
int semget(
key_t key, //和信號燈集關聯的key值
int nsems, //信號燈集中包含的信號燈數目
int semflg//信號燈集的訪問權限,通常為IPC_CREAT | 0666
);
返回值:
成功:則返回信號量集的IPC標識符。
失敗,則返回-1,並且設置errno,
EACCES:沒有訪問該信號量集的權限
EEXIST:信號量集已經存在,無法創建
ENOENT:信號量集不存在,同時沒有使用IPC_CREAT
信號操作
int semop(
int semid, //信號燈集ID
struct sembuf *opsptr,
size_t nops //要操作的信號燈的個數
);
參數:
struct sembuf {
short sem_num; //要操作的信號燈的編號
short sem_op; //0:等待,直到信號燈的值變成0
//1:釋放資源,V操作
//-1:分配資源,P操作
short sem_flg; //0,IPC_NOWAIT,SEM_UNDO
};
返回值:
成功:0
出錯:-1
信號控制
int semctl(
int semid, //信號燈集ID
int semnum, //要修改的信號燈編號
int cmd…/*union semun arg*/
);
參數:
cmd:
GETVAL:獲取信號燈的值
SETVAL:設置信號燈的值
IPC_RMID:從系統中刪除信號燈集合
返回值:
成功:0
出錯:-1
進程間通訊方式比較
pipe:具有親緣關系的進程間,單工,數據在內存中
fifo:用於任意進程間,雙工,有文件名,數據在內存
signal:唯一的異步通信方式
msg:常用於cs模式中, 按消息類型訪問 ,可有優先級
shm:效率最高(直接訪問內存) ,需要同步、互斥機制
sem:配合共享內存使用,用以實現同步和互斥