您好,登錄后才能下訂單哦!
這篇“linux可不可以創建多個進程”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“linux可不可以創建多個進程”文章吧。
linux可以創建多個進程。linux支持多進程,可以同時處理多個任務,實現系統資源的最大利用。linux進程間的通信方式:1、利用無名管道pipe;2、利用有名管道(FIFO);3、利用信號single;4、利用共享內存;5、利用消息隊列;6、利用信號量。
linux可以創建多個進程。
linux 支持多進程。多進程系統的一個好處,就是可以同時處理多個任務,實現系統資源的最大利用。
1.1 概述
linux中把正在運行的程序稱作進程。
程序:靜態的概念,它是一個編譯好的二進制文件
進程:動態的概念,當程序運行的時候,系統會自動運行一個對應進程
進程包含了進程控制塊(PCB),代碼段,數據段三個部分
進程控制塊:在linux中是用一個結構體來表示的,記錄進程的狀態信息
僵尸進程:父進程優先于子進程退出
如果你創建了子進程,但是在父進程中沒有回收該子進程的資源,那么該子進程就會變成僵尸進程,僵尸進程最終會由系統中一個叫做INIT的進程回收。
init進程(1號進程)是系統啟動的時候運行的第一個進程,是所有進程的祖進程。
top 查看動態的進程信息
ps -ef 查看進程的詳細信息
pstree 以樹狀的形式顯示進程的信息
bg 將掛起的進程放到后臺運行
1.2 進程運行狀態
執行態( RUNNING):進程正在占有CPU。
就緒態( RUNNING):進程處于等待隊列中等待調度。
淺睡眠( INTERRUPTABLE):此時進程在等待一個事件的發生或某種系統資源,可響應信號。
深睡眠( UNINTERRUPTABLE): 此時進程在等待一個事件的發生或某種系統資源, 無法響應信號。
停止態( STOPPED): 此時進程被暫停。
僵尸態( ZOMBIE): 此時進程不能被調度,但是PCB未被釋放。
死亡態( DEAD): 這是一個已終止的進程,且PCB將會被釋放
內核態:也叫內核空間,是內核進程/線程所在的區域。主要負責運行系統、硬件交互。
用戶態:也叫用戶空間,是用戶進程/線程所在的區域。主要用于執行用戶程序。
1、區別
內核態:運行的代碼不受任何限制,CPU可以執行任何指令。
用戶態:不能調度CPU,不能直接訪問硬件。運行的代碼需要受到CPU的很多檢查,不能直接訪問內核數據和程序,也就是不可以像內核態線程一樣訪問任何有效地址。
操作系統在執行用戶程序時,主要工作在用戶態,只有在其執行沒有權限完成的任務時才會切換到內核態。
2、區分用戶態和內核態原因
保護機制,防止用戶進程誤操作或者是惡意破壞系統
保證資源的集中管理,減少資源的使用沖突。
3、用戶態切換到內核態方式
(1)系統調用(主動)
系統調用(system call)是操作系統提供給用戶進程請求操作系統做一些特權操作的接口,即為用戶進程提供服務的窗口。在Linux下可以通過man syscalls命令查看Linux提供的所有系統調用API接口。
由于用戶態無法完成某些任務,用戶態會請求切換到內核態,內核態通過為用戶專門開放的中斷完成切換。
(2)外圍設備中斷(被動)
外圍設備發出中斷信號,當中斷發生后,當前運行的進程暫停運行,并由操作系統內核對中斷進程處理,如果中斷之前CPU執行的是用戶態程序,就相當于從用戶態向內核態的切換。
中斷用于保證CPU控制權交給操作系統,從而讓操作系統可以執行某些操作。
(3)異常(被動)
在執行用戶程序時出現某些不可知的異常,會從用戶程序切換到內核中處理該異常的程序,也就是切換到了內核態。
1.3 進程接口函數
1、fork()、vfork()
(1)新建的進程稱作子進程,它復制了父進程的所有資源(只在創建的那個時間復制一次,以后全局變量值是不同),父子進程誰先誰后是不確定。
#include <unistd.h>
pid_t fork(void);
返回值: > 0表示處于父進程中 這個時候的返回值就是**子進程進程id號**
==0 表示處于子進程中
< 0 創建進程出錯
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
(2)**vfork()**子進程共享了父進程的所有資源,它一定是子進程先運行,然后才是父進程運行(即使你加上sleep()人為去干擾也是沒有用的)
(3)注意
子進程中使用了exit()跟沒有使用結果完全不一樣
父子進程中是否使用sleep()來讓出cpu時間片也是不一樣的
父進程中是否使用wait(),waitpid()結果也是不一樣的
(4)進程切換執行
1、exit()、_exit()
#include <stdlib.h>
void exit(int status);
void _exit(int status);
參數:
status --->進程退出時的狀態
status在實際編寫程序中是可以自己約定的:
比如: exit(2)----》出現打開文件錯誤
exit(3)----》出現段錯誤(邏輯錯誤)
exit(0)----》正常退出
返回: void
區別:
exit()在退出的時候會刷新IO緩沖區,然后才退出(負責任的退出)
_exit() 直接退出(不負責任的退出)
1、wait()
#include <sys/wait.h>
pid_t wait(int *stat_loc);
返回值:你回收的那個子進程的id號
參數:stat_loc --》保存子進程退出時的狀態信息(不僅僅只是返回值)
stat_loc里面不僅僅只是保存了exit退出時的數值,它還保存了子進程退出時是哪個信號讓它退出的,
出錯了是什么原因導致的。
2、 waitpid()
pid_t waitpid(pid_t pid, int *stat_loc, int options); 回收子進程/進程組
參數: pid ----》你指定要回收的那個子進程的id
<-1 等待進程組號為-pid中的某個子進程退出
-1 等待任意一個子進程
==0 等待本進程組中的某個子進程退出
> 0 等待PID為pid的進程
stat_loc-----》存放子進程退出狀態(可為NULL)
options ----》一般設置為0
WNOHANG 當沒有子進程時立即返回
WUNTRACED 當有子進程被暫停時立即返回
WCONTINUED 當有子進程收到SIGCONT立即返回
返回值:-1 執行失敗
> 0 成功 返回值為被回收的進程的PID
0 指定了WNOHANG,且沒有已退出的子進程
(1)獲取自己的id getpid()
#include <unistd.h>
pid_t getpid(void); 返回值:就是該進程的id號
(2) 獲取父進程id getppid()
#include <unistd.h>
pid_t getppid(void); 返回值:就是父進程的id號
不管是進程間的通信,還是線程間的通信。無非都是為了解決一個問題:就是共享資源的分配(協調不同的進程/線程對于共享的資源的訪問)
2.1 進程間的通信方式
1、傳統的進程間通信方式
無名管道
有名管道
信號
2、System V IPC對象
共享內存
消息隊列
信號量
3、BSD
網絡套接字(socket)
1、特點:最原始的進程間的通信方式
它只能在具有親緣關系的進程間通信(父子進程,兄弟進程);
它沒有名字(是存在的);
可以在linux和windows之間的共享中創建(根本就不會生成管道文件),但是有名管道就不可以了(生成管道文件);
半雙工通信。
2、無名管道的使用
(1)創建pipe()
#include <unistd.h>
int pipe(int fildes[2]);
參數:fildes[2]里面放的是兩個文件描述符fildes[0],fildes[1]
fildes[0] 讀端
fildes[1] 寫端
返回值:成功返回0 失敗返回-1
(2)pipe信息收發
myid = fork(); //創建子進程
if(myid == 0)
{
write(fd[1],"dad,thanks!",20); //子進程向父進程發送消息
close(fd[1]);
close(fd[0]);
exit(0);
}
else if(myid > 0)
{
read(fd[0],buf,20); //父進程阻塞接受子進程消息
printf("buf is:%s\n",buf);
close(fd[1]);
close(fd[0]);
}
1、特點:隨便兩個進程之間都行
不能在linux和windows之間的共享中創建;
保證寫入的原子性(原子性:要么不做,要做就一口氣做完不受外界的干擾);
有名管道不能夠覆蓋著創建(一般代碼中使用access()函數來判斷是否存在,如果已經存在同名的管道,就不能再次創建);
使用完畢記得關閉;
當管道以只讀的方式打開,會阻塞,直到有另外一個進程以只寫的方式打開這個管道,那么就不阻塞了;如果是以可讀寫的方式打開,就不會阻塞了。
全雙工通信,半雙道。
2、有名管道的使用
(1)創建mkfifo()
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
參數:pathname 有名管道的路徑名
mode:權限 0666
返回值:0 成功
-1 失敗
(2)FIFO進程信息收發
fifo_read.c :-----------》
#define FIFO1 "myfifo1"
#define FIFO2 "myfifo2"
int main(void) {
int my_fd,fd1,fd2;
char r_buff[30];
char w_buff[30];
bzero(r_buff,30);
if(access(FIFO1,F_OK)==-1) {
my_fd = mkfifo(FIFO1,0664); //創建管道1
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
if(access(FIFO2,F_OK)==-1) {
my_fd = mkfifo(FIFO2,0664); //創建管道2
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
fd1 = open(FIFO1,O_RDONLY); //只讀打開管道1,獲取管道文件描述符
if(fd1==-1) {
printf("open fifo1 file failed!\n");
exit(0);
}
fd2 = open(FIFO2,O_WRONLY); //只寫打開管道2,獲取管道文件描述符
if(fd2==-1) {
printf("open fifo2 file failed!\n");
exit(0);
}
while(1) {
bzero(r_buff,30);
read(fd1,r_buff,sizeof(r_buff)); //讀取管道1的消息
printf("client receive message is: %s\n",r_buff);
printf("client please input a message!\n");
fgets(w_buff,30,stdin);
write(fd2,w_buff,30); //發送信息給管道2
}
close(fd2);
close(fd1);
return 0;
}
fifo_write.c :-----------》
#define FIFO1 "myfifo1"
#define FIFO2 "myfifo2"
int main(void)
{
int my_fd,fd1,fd2;
char w_buff[30];
char r_buff[30];
bzero(w_buff,30);
if(access(FIFO1,F_OK)==-1) {
my_fd = mkfifo(FIFO1,0664);
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
if(access(FIFO2,F_OK)==-1) {
my_fd = mkfifo(FIFO2,0664);
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
fd1 = open(FIFO1,O_WRONLY);
if(fd1==-1) {
printf("open fifo1 file failed!\n");
exit(0);
}
fd2 = open(FIFO2,O_RDONLY);
if(fd2==-1) {
printf("open fifo2 file failed!\n");
exit(0);
}
while(1) {
bzero(w_buff,30);
printf("server please input a message!\n");
fgets(w_buff,30,stdin);
write(fd1,w_buff,strlen(w_buff)); //寫入消息到管道1文件
read(fd2,r_buff,30); //讀取信息從管道2
printf("server receive message is:%s\n",r_buff);
}
close(fd1);
close(fd2);
return 0;
}
程序(進程)在運行過程中,外界不定時會發信號給該程序,這個時候該程序面臨著兩種選擇:
不理它(阻塞/忽略)
阻塞:是指將信號掛起,等到程序運行完了再去響應
忽略:舍棄這個信號
響應它
1、linux當中有哪些信號:kill -l 查看
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
(1)1到31號信號稱作非實時信號:不支持隊列(如果同時來了多個信號,響應是沒有規律)
(2)用戶自定義的信號 10) SIGUSR1 12) SIGUSR2
(3) 34到64號信號叫做實時信號:支持隊列,是linux系統中后面添加進來的信號
信號類似于中斷: 硬件 軟件
以上信號有兩個很特殊:SIGKILL,SIGSTOP不能夠被忽略,也不能被阻塞
2、信號相關的操作函數
(1)發送信號kill()
#include <signal.h>
int kill(pid_t pid, int sig);
參數:
pid ----》進程的id
正數:要接收信號的進程的進程號
0:信號被發送到所有和pid進程在同一個進程組的進程
-1:信號發給所有的進程表中的進程(除了進程號最大的進程外)
sig ----》信號名字
返回值:0 成功
-1 出錯
(2)信號的捕捉 signal()
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int); // SIGKILL
參數:sig ----》你需要捕捉的那個信號
void (*func)(int) ----》函數指針,回調函數,捕捉到對應的信號的時候就調用該函數;第二個參數除了可以傳遞一個函數指針意外,還可以使用以下兩個宏定義:
SIG_IGN ---->你捕捉到的那個信號會被忽略
SIG_DFL-----》你捕捉的信號會采用系統默認的方式響應
返回值:成功:設置之前的信號處理方式
出錯:-1
(3)等待信號 pause()
#include <unistd.h>
int pause(void);
返回值:-1 把error值設為EINTR
0 成功
(4)信號的阻塞
每個進程都有屬于它自己的一個信號掩碼(也就是該進程在運行的過程中會阻塞掉的那些信號就被稱作信號掩碼)。
關于信號掩碼操作的一系列函數:
#include <signal.h>
int sigemptyset(sigset_t *set):清空信號掩碼
int sigfillset(sigset_t *set):將所有的信號添加到信號掩碼中
int sigaddset(sigset_t *set, int signum):將特定的信號添加到信號掩碼中
int sigdelset(sigset_t *set, int signum):將特定的信號從掩碼中刪除
int sigismember(const sigset_t *set, int signum):判斷某個信號在不在該掩碼中
參數:sigset_t ----》存儲被進程阻塞的信號
(5)配置信號掩碼 sigprocmask()—阻塞或解除阻塞信號
#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)
參數:
how ---》SIG_BLOCK 將set所包含的信號添加到原來的信號掩碼中
SIG_SETMASK 用set去替換原來的信號掩碼
SIG_UNBLOCK 將set中包含的信號從原來的掩碼中刪除
set ---》新的信號掩碼
oset ---》原本的信號掩碼
原本進程中信號掩碼包含了:SIGINT ,SIGCONT
(6)捕捉指定信號并獲取信號攜帶信息sigaction()
#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
參數:
sig ---》你要捕捉的那個信號
act ---》你需要捕捉的信號對應的響應函數就定義在這個結構體
oact ---》原來的
struct sigaction
{
void(*) (int) sa_handler ----》 信號的響應函數
sigset_t sa_mask ---》信號的掩碼
int sa_flags ----》 SA_SIGINFO
void(*) (int, siginfo_t * ,void )---》信號的響應函數
}
sa_flags ---》等于SA_SIGINFO,那么信號的響應函數是void(*) (int, siginfo_t * ,void )
不等于,那么信號的響應函數是void(*) (int)
siginfo_t---》/usr/include/i386-linux-gnu/bits/siginfo.h 保存的是信號的狀態信息,信號的標號,發送該信號的進程的id等等這些
查看共享內存: ipcs -m
刪除共享內存: ipcrm -m 共享內存的id
SYSTEM-V ipc通信方式:共享內存、信號量、消息隊列。
1、共享內存特點:跟mmap()思想上有些類似
在進程通信方式中共享內存是效率最高的,進程可以直接讀寫內存,而不需要任何數據的拷貝;
如果代碼不人為地刪除共享共享內存,那么程序退出的時候它還在;
多個進程共享一段內存,因此也需要依靠某種同步機制,如互斥鎖和信號量等
2、共享內存對應的一系列操作函數
(1)創建共享內存:shmget()
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
返回值:成功—共享內存對象的mid(標識符) 出錯—-1
參數:key----》創建共享內存需要用到的鍵值
size----》內存空間的大小(字節)
shmflg----》設置屬性 IPC_CREAT IPC_EXCL 0666組合
key鍵值的獲取有兩種方法:
**方法一**:使用ftok()生成鍵值
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
參數:pathname----》 路徑名
proj_id----》整數
ftok(“.” , 11) 生成一個唯一的key值
進程1:ftok(“.” , 11) ----》shmget( 100);.............
進程2:ftok(“/home/gec” , 11) ----》shmget( 106);
無法通信,要確保鍵值一致才能通信
**方法二:**不使用ftok(),程序員自己寫個數字
shmget((key_t)1234, size_t size, int shmflg);
(2) 映射共享內存到用戶空間 shmat()
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
返回值:成功—映射到用戶空間的那片地址的首地址 出錯—-1
參數:shmid ----》使用shmget的返回值
shmaddr----》一般設置為NULL 系統自動分配
shmflg----》 SHM_RDONLY:共享內存只讀
一般設置為0: 共享內存可讀寫
if it is 0 and the calling process has read and write permission, the segment is attached for reading and writing.
(3)解除映射:shmdt()
#include <sys/shm.h>
int shmdt(const void *shmaddr);
參數:shmaddr----》 shmat()共享內存映射后的地址
返回值:成功—0 出錯—-1
(4)刪除共享內存:shmctl()
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數: shmid----》共享內存的id
cmd----》IPC_RMID 刪除共享內存
IPC_STAT (獲取對象屬性)
IPC_SET (設置對象屬性)
*buf----》指定IPC_STAT/IPC_SET時保存共享內存的狀態信息
返回值:成功 失敗—-1
3、共享內存簡單示例
shm_write.c :----------》
int main() {
int shmid;
int *p;
// 創建共享內存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享內存到進程
p = (int *)shmat(shmid,NULL,0);
*p = 10;
// 解除映射
shmdt(p);
// 刪除內存
//shmctl(shmid,IPC_RMID,NULL);
return 0;
}
shm_read.c :----------》
int main() {
int shmid;
int *p;
// 創建共享內存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享內存到進程
p = (int *)shmat(shmid,NULL,0);
printf("p is :%d\n",*p);
// 解除映射
shmdt(p);
// 刪除內存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
消息隊列就是一個消息的列表。用戶可以在消息隊列中添加消息、讀取消息等。
消息隊列由消息隊列ID來唯一標識
消息隊列可以按照類型來發送/接收消息
消息隊列的操作包括創建或打開消息隊列、添加消息、讀取消息和控制消息隊列
1、消息隊列的特點
寫入消息隊列的信息,在編寫程序的時候會人為的去設置消息的類型(用整數來表示),目的是為了其它進程在讀取信息的時候能夠準確地通過消息的類型判斷要讀取的信息。
2、消息隊列操作的系列函數
(1)消息隊列的創建 msgget()
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
(2)消息隊列的收發信息msgsnd()msgrcv()
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
參數:void *msgp ----》你要發送信息就存儲在這個指針中
在實際的編程中我們都是定義一個結構體來存儲信息
struct msgbuf {
long mtype; ----》消息的類型
char mtext[100]; ----》消息的內容
}
msgsz ----》消息的長度,大小
msgflg ----》設置為0 除開以上三種宏定義之外的----阻塞讀寫
(3)消息隊列的刪除 msgctl()
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
3、消息隊列通信簡單示例
pthread1.c :-----------》
#define SIZE 64
//數據接收結構體
struct msg_rv
{
int mtype;
char msg[50];
};
//數據發送結構體
struct msg_snd
{
int mtype;
char msg[50];
};
int main(void) {
int msgid;
struct msg_rv data;
struct msg_snd snddata;
char buff[50];
//獲取msgid
msgid = msgget((key_t)123,IPC_CREAT|0666);
if(msgid == -1) {
printf("msgid failed!\n");
return -1;
}
data.mtype = 88;
snddata.mtype = 89;
while(1) {
bzero(buff,50);
printf("please input data!\n");
fgets(buff,50,stdin);
strcpy(snddata.msg,buff);
if(strncmp(snddata.msg,"end",3)==0) {
break;
}
msgsnd(msgid,(void *)&snddata,strlen(buff)+4,0);//得到的值發送出去
usleep(20);
printf("run here!\n");
if(msgrcv(msgid,(void *)&data,sizeof(struct msg_rv),data.mtype,0)==-1) {
printf("msgsnd failed!\n");
return -1;
}
printf("receive data:%s\n",data.msg);
if(strncmp(data.msg,"end",3)==0) {
break;
}
}
//撤消消息隊列
msgctl(msgid,IPC_RMID,0);
return 0;
}
pthread2.c :------------------------》
#define SIZE 64
//數據接收結構體
struct msg_rv
{
int mtype;
char msg[50];
};
//數據發送結構體
struct msg_snd
{
int mtype;
char msg[50];
};
int main(void)
{
int msgid;
struct msg_rv data;
struct msg_snd snddata;
char buff[50];
data.mtype = 89;
snddata.mtype = 88;
//獲取msgid
msgid = msgget((key_t)123,IPC_CREAT|0666);
if(msgid == -1) {
printf("msgid failed!\n");
return -1;
}
while(1) {
//接受
if(msgrcv(msgid,(void *)&data,sizeof(struct msg_rv),data.mtype,0)==-1)
{
printf("msgsnd failed!\n");
return -1;
}
printf("receive data:%s\n",data.msg);
if(strncmp(data.msg,"end",3)==0) {
break;
}
//發送
printf("please input data:\n");
bzero(buff,50);
fgets(buff,50,stdin);
strcpy(snddata.msg,buff);
printf("data = %s\n",snddata.msg);
if(strncmp(snddata.msg,"end",3)==0) {
break;
}
msgsnd(msgid,(void *)&snddata,strlen(buff)+4,0);//得到的值發送出去
printf("run here!\n");
}
//撤消消息隊列
msgctl(msgid,IPC_RMID,0);
return 0;
}
信號量協調不同進程對于共享資源的訪問,它是不同進程間或一個給定進程內部不同線程間同步的機制。
1、信號量概述
(1)二值信號量
值為0或1。與互斥鎖類似,資源可用時值為1,不可用時值為0
(2)計數信號量
值在0到n之間。用來統計資源,其值代表可用資源數
(3)對信號量的操作
P操作:即申請資源,亦即將信號量值減1,可能引起進程睡眠。
V操作:即釋放資源,亦即將信號量值加1,V操作從不會睡眠。
等0操作:不申請也不釋放資源,而是令進程阻塞直到信號量的值為0為止
2、信號量相關的接口函數
(1) 創建信號量集合semget()
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
參數:key ----》鍵值
nsems----》你創建的信號量集中信號量的個數
semflg----》 IPC_CREAT|0666組合
返回值:成功—信號量ID
出錯—-1
(2)設置/刪除信號量集 semctl()
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
返回值:成功—0 失敗—-1
(3)信號量的PV操作 semop()
核心:信號量為 <=0 時進行p操作,會阻塞程序,直到另一進程中是該信號進行了v操作后,本程序才會繼續運行------》key值相同,信號量共通
p 減一操作
v 加一操作
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
返回值:成功—0 出錯—-1
參數:semid ----》semget的返回值
nsops ---》要操作的信號量的個數(結構體的個數)
sops---》信號量操作結構體
struct sembuf {
short sem_num ;=>> 要操作的信號量的編號(數組下標)
short sem_op; =>> 0 : 等待,直到信號量的值變成0
1 : 釋放資源,V操作
-1 : 分配資源,P操作
short sem_flg; =>> 0/IPC_NOWAIT/SEM_UNDO
SEM_UNDO: 程序結束時(不論正常或不正常),保證信號值會被重設為semop()調用前的值;
IPC_NOWAIT: 對信號的操作不能滿足時,semop()不會阻塞,并立即返回,同時設定錯誤信息;
};
3、信號量協同共享內存示例代碼
pthread1.c :-----------》
int main()
{
int semid;
int shmid;
char *p;
struct sembuf mysembuf1,mysembuf2;
mysembuf1.sem_num = 0;
mysembuf1.sem_flg = SEM_UNDO;
mysembuf1.sem_op = 1;
mysembuf2.sem_num = 1;
mysembuf2.sem_flg = SEM_UNDO;
mysembuf2.sem_op = -1;
// 創建信號量集合
semid = semget((key_t)789,2,IPC_CREAT|0666);
if(semid == -1) {
perror("creat sem failed!\n");
return -1;
}
// 創建共享內存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享內存到進程
p = (char *)shmat(shmid,NULL,0);
while(1) {
semop(semid,&mysembuf2,1); // 對信號量2進行p操作(減一)
printf("the message I recv is:%s\n",p);
printf("please input a message!\n");
scanf("%s",p);
printf("message is %s\n",p);
semop(semid,&mysembuf1,1); // 對信號量1進行v操作(加一)
}
//解除映射
shmdt(p);
//刪除共享內存
shmctl(semid, IPC_RMID, NULL);
}
pthread2.c :-----------》
int main() {
int semid;
int shmid;
char *p;
struct sembuf mysembuf1,mysembuf2;
mysembuf1.sem_num = 0; // 信號集合中的第一個信號
mysembuf1.sem_flg = SEM_UNDO;
mysembuf1.sem_op = -1; //p操作
mysembuf2.sem_num = 1; // 信號集合中的第二個信號
mysembuf2.sem_flg = SEM_UNDO;
mysembuf2.sem_op = 1; // v操作
// 創建信號量集合
semid = semget((key_t)789,2,IPC_CREAT|0666);
if(semid == -1) {
perror("creat sem failed!\n");
return -1;
}
// 設置信號量的值
semctl(semid,0,SETVAL,1); //第一個信號量初值為1
printf("sem num is:%d\n",semctl(semid,0,GETVAL));
semctl(semid,1,SETVAL,0); //第二個信號量初值為0
printf("sem num is:%d\n",semctl(semid,1,GETVAL));
// 創建共享內存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享內存到進程
p = (char *)shmat(shmid,NULL,0);
while(1) {
semop(semid,&mysembuf1,1); // 對信號量1進行p操作(減一)不阻塞,因為初值為1
// 執行完這句話以后信號量的值就立馬變成1
printf("the message I recv is:%s\n",p);
printf("please input a message!\n");
scanf("%s",p);
printf("message is %s\n",p);
semop(semid,&mysembuf2,1); // 對信號量2進行v操作(加一)不阻塞,因為初值為0
}
//解除映射
shmdt(p);
//刪除共享內存
shmctl(semid, IPC_RMID, NULL);
}
2.3 IPC shell命令操作
ipcs -q 查看消息隊列
ipcrm -q MSG_ID 刪除消息隊列
ipcs -m 查看共享內存
ipcrm -m SHM_ID 刪除共享內存
ipcs -s 查看信號量
ipcrm -s SEM_ID 刪除信號量
2.2 進程間通訊方式比較
pipe: 具有親緣關系的進程間,單工,數據在內存中
fifo: 可用于任意進程間,雙工,有文件名,數據在內存
signal: 唯一的異步通信方式
msg:常用于cs模式中, 按消息類型訪問 ,可有優先級
shm:效率最高(直接訪問內存) ,需要同步、互斥機制
sem:配合共享內存使用,用以實現同步和互斥
以上就是關于“linux可不可以創建多個進程”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。