久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

linux可不可以創(chuàng)建多個進程

172次閱讀
沒有評論

共計 16116 個字符,預計需要花費 41 分鐘才能閱讀完成。

這篇“l(fā)inux 可不可以創(chuàng)建多個進程”文章的知識點大部分人都不太理解,所以丸趣 TV 小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“l(fā)inux 可不可以創(chuàng)建多個進程”文章吧。

linux 可以創(chuàng)建多個進程。linux 支持多進程,可以同時處理多個任務,實現(xiàn)系統(tǒng)資源的最大利用。linux 進程間的通信方式:1、利用無名管道 pipe;2、利用有名管道(FIFO);3、利用信號 single;4、利用共享內(nèi)存;5、利用消息隊列;6、利用信號量。

linux 可以創(chuàng)建多個進程。

linux 支持多進程。多進程系統(tǒng)的一個好處,就是可以同時處理多個任務,實現(xiàn)系統(tǒng)資源的最大利用。

第 1 章 linux 多進程介紹

1.1 概述

1.1.1 進程概念

linux 中把正在運行的程序稱作進程。
程序:靜態(tài)的概念,它是一個編譯好的二進制文件
進程:動態(tài)的概念,當程序運行的時候,系統(tǒng)會自動運行一個對應進程
進程包含了進程控制塊(PCB),代碼段,數(shù)據(jù)段三個部分
進程控制塊:在 linux 中是用一個結(jié)構(gòu)體來表示的,記錄進程的狀態(tài)信息
僵尸進程:父進程優(yōu)先于子進程退出
如果你創(chuàng)建了子進程,但是在父進程中沒有回收該子進程的資源,那么該子進程就會變成僵尸進程,僵尸進程最終會由系統(tǒng)中一個叫做 INIT 的進程回收。
init 進程(1 號進程)是系統(tǒng)啟動的時候運行的第一個進程,是所有進程的祖進程。

1.1.2 進程查看 shell 命令

top 查看動態(tài)的進程信息

ps -ef 查看進程的詳細信息

pstree 以樹狀的形式顯示進程的信息

bg 將掛起的進程放到后臺運行

1.2 進程運行狀態(tài)

1.2.1 運行狀態(tài)

執(zhí)行態(tài)(RUNNING):進程正在占有 CPU。

就緒態(tài)(RUNNING):進程處于等待隊列中等待調(diào)度。

淺睡眠(INTERRUPTABLE):此時進程在等待一個事件的發(fā)生或某種系統(tǒng)資源,可響應信號。

深睡眠(UNINTERRUPTABLE):此時進程在等待一個事件的發(fā)生或某種系統(tǒng)資源,無法響應信號。

停止態(tài)(STOPPED):此時進程被暫停。

僵尸態(tài)(ZOMBIE):此時進程不能被調(diào)度,但是 PCB 未被釋放。

死亡態(tài)(DEAD):這是一個已終止的進程,且 PCB 將會被釋放

1.2.2 用戶態(tài) / 內(nèi)核態(tài)

內(nèi)核態(tài):也叫內(nèi)核空間,是內(nèi)核進程 / 線程所在的區(qū)域。主要負責運行系統(tǒng)、硬件交互。
用戶態(tài):也叫用戶空間,是用戶進程 / 線程所在的區(qū)域。主要用于執(zhí)行用戶程序。

1、區(qū)別
內(nèi)核態(tài):運行的代碼不受任何限制,CPU 可以執(zhí)行任何指令。
用戶態(tài):不能調(diào)度 CPU,不能直接訪問硬件。運行的代碼需要受到 CPU 的很多檢查,不能直接訪問內(nèi)核數(shù)據(jù)和程序,也就是不可以像內(nèi)核態(tài)線程一樣訪問任何有效地址。

操作系統(tǒng)在執(zhí)行用戶程序時,主要工作在用戶態(tài),只有在其執(zhí)行沒有權(quán)限完成的任務時才會切換到內(nèi)核態(tài)。

2、區(qū)分用戶態(tài)和內(nèi)核態(tài)原因

保護機制,防止用戶進程誤操作或者是惡意破壞系統(tǒng)

保證資源的集中管理,減少資源的使用沖突。

3、用戶態(tài)切換到內(nèi)核態(tài)方式
(1)系統(tǒng)調(diào)用(主動)
系統(tǒng)調(diào)用(system call)是操作系統(tǒng)提供給用戶進程請求操作系統(tǒng)做一些特權(quán)操作的接口,即為用戶進程提供服務的窗口。在 Linux 下可以通過 man syscalls 命令查看 Linux 提供的所有系統(tǒng)調(diào)用 API 接口。
由于用戶態(tài)無法完成某些任務,用戶態(tài)會請求切換到內(nèi)核態(tài),內(nèi)核態(tài)通過為用戶專門開放的中斷完成切換。

(2)外圍設備中斷(被動)
外圍設備發(fā)出中斷信號,當中斷發(fā)生后,當前運行的進程暫停運行,并由操作系統(tǒng)內(nèi)核對中斷進程處理,如果中斷之前 CPU 執(zhí)行的是用戶態(tài)程序,就相當于從用戶態(tài)向內(nèi)核態(tài)的切換。
中斷用于保證 CPU 控制權(quán)交給操作系統(tǒng),從而讓操作系統(tǒng)可以執(zhí)行某些操作。

(3)異常(被動)
在執(zhí)行用戶程序時出現(xiàn)某些不可知的異常,會從用戶程序切換到內(nèi)核中處理該異常的程序,也就是切換到了內(nèi)核態(tài)。

1.3 進程接口函數(shù)

1.3.1 進程創(chuàng)建 fork/vfork

1、fork()、vfork()
(1)新建的進程稱作子進程,它復制了父進程的所有資源(只在創(chuàng)建的那個時間復制一次,以后全局變量值是不同),父子進程誰先誰后是不確定。

#include  unistd.h 
pid_t fork(void);
  返回值:   0 表示處于父進程中   這個時候的返回值就是 ** 子進程進程 id 號 **
 ==0  表示處于子進程中
   0  創(chuàng)建進程出錯
 
#include  sys/types.h 
   #include  unistd.h 
 pid_t vfork(void);

(2)**vfork()** 子進程共享了父進程的所有資源,它一定是子進程先運行,然后才是父進程運行(即使你加上 sleep()人為去干擾也是沒有用的)
(3)注意
子進程中使用了 exit()跟沒有使用結(jié)果完全不一樣
父子進程中是否使用 sleep()來讓出 cpu 時間片也是不一樣的
父進程中是否使用 wait(),waitpid()結(jié)果也是不一樣的

(4)進程切換執(zhí)行

1.3.2 進程的退出 exit/_exit

1、exit()、_exit()

 #include  stdlib.h 
 void exit(int status); 
 void _exit(int status);
 status --- 進程退出時的狀態(tài)
 status 在實際編寫程序中是可以自己約定的:  比如: exit(2)----》出現(xiàn)打開文件錯誤
 exit(3)----》出現(xiàn)段錯誤(邏輯錯誤) exit(0)----》正常退出
   返回: void
 exit()在退出的時候會刷新 IO 緩沖區(qū),然后才退出(負責任的退出) _exit()  直接退出(不負責任的退出)

1.3.3 等待子進程退出(父進程回收資源)wait/waitpid

1、wait()

#include  sys/wait.h 
 pid_t wait(int *stat_loc);
  返回值:你回收的那個子進程的 id 號
  參數(shù):stat_loc --》保存子進程退出時的狀態(tài)信息(不僅僅只是返回值)
   stat_loc 里面不僅僅只是保存了 exit 退出時的數(shù)值,它還保存了子進程退出時是哪個信號讓它退出的,    出錯了是什么原因?qū)е碌摹?/code>

2、waitpid()

pid_t waitpid(pid_t pid, int *stat_loc, int options);  回收子進程 / 進程組
  參數(shù): pid ----》你指定要回收的那個子進程的 id
  -1  等待進程組號為 -pid 中的某個子進程退出   
     -1  等待任意一個子進程
 ==0  等待本進程組中的某個子進程退出
    0  等待 PID 為 pid 的進程
 stat_loc-----》存放子進程退出狀態(tài)(可為 NULL)  
 options ----》一般設置為 0
 WNOHANG  當沒有子進程時立即返回
 WUNTRACED  當有子進程被暫停時立即返回
 WCONTINUED  當有子進程收到 SIGCONT 立即返回
 返回值:-1  執(zhí)行失敗
  0  成功   返回值為被回收的進程的 PID
 0  指定了 WNOHANG,且沒有已退出的子進程

1.3.4 獲取進程的 id–getpid

(1)獲取自己的 id getpid()#include  unistd.h 
 pid_t getpid(void);  返回值:就是該進程的 id 號(2)  獲取父進程 id getppid() #include  unistd.h 
 pid_t getppid(void);  返回值:就是父進程的 id 號

第 2 章 linux 多進程間通信方式

不管是進程間的通信,還是線程間的通信。無非都是為了解決一個問題:就是共享資源的分配(協(xié)調(diào)不同的進程 / 線程對于共享的資源的訪問)

2.1 進程間的通信方式

1、傳統(tǒng)的進程間通信方式

無名管道

有名管道

信號

2、System V IPC 對象

共享內(nèi)存

消息隊列

信號量

3、BSD

網(wǎng)絡套接字(socket)

2.1.1 無名管道 pipe

1、特點:最原始的進程間的通信方式
它只能在具有親緣關系的進程間通信(父子進程,兄弟進程);
它沒有名字(是存在的);
可以在 linux 和 windows 之間的共享中創(chuàng)建(根本就不會生成管道文件),但是有名管道就不可以了(生成管道文件);
半雙工通信。

2、無名管道的使用
(1)創(chuàng)建 pipe()

 #include  unistd.h 
 int pipe(int fildes[2]);
  參數(shù):fildes[2]里面放的是兩個文件描述符 fildes[0],fildes[1]
 fildes[0]  讀端
 fildes[1]  寫端
  返回值:成功返回 0   失敗返回 -1

(2)pipe 信息收發(fā)

myid = fork(); // 創(chuàng)建子進程
if(myid == 0)
 write(fd[1], dad,thanks! ,20); // 子進程向父進程發(fā)送消息
 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]);
}

2.1.2 有名管道(FIFO)

1、特點:隨便兩個進程之間都行

不能在 linux 和 windows 之間的共享中創(chuàng)建;

保證寫入的原子性(原子性:要么不做,要做就一口氣做完不受外界的干擾);

有名管道不能夠覆蓋著創(chuàng)建(一般代碼中使用 access()函數(shù)來判斷是否存在,如果已經(jīng)存在同名的管道,就不能再次創(chuàng)建);

使用完畢記得關閉;

當管道以只讀的方式打開,會阻塞,直到有另外一個進程以只寫的方式打開這個管道,那么就不阻塞了;如果是以可讀寫的方式打開,就不會阻塞了。

全雙工通信,半雙道。

2、有名管道的使用
(1)創(chuàng)建 mkfifo()

 #include  sys/types.h 
 #include  sys/stat.h 
 int mkfifo(const char *pathname, mode_t mode);
  參數(shù):pathname  有名管道的路徑名
  mode:權(quán)限  0666
   返回值:0  成功
  -1  失敗

(2)FIFO 進程信息收發(fā)

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); // 創(chuàng)建管道 1
 if(my_fd == -1) {
 perror( failed!\n 
 return -1;
 if(access(FIFO2,F_OK)==-1) {my_fd = mkfifo(FIFO2,0664); // 創(chuàng)建管道 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); // 發(fā)送信息給管道 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;
}

2.1.3 信號 single

程序 (進程) 在運行過程中,外界不定時會發(fā)信號給該程序,這個時候該程序面臨著兩種選擇:
不理它(阻塞 / 忽略)
阻塞:是指將信號掛起,等到程序運行完了再去響應
忽略:舍棄這個信號
響應它

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 號信號稱作非實時信號:不支持隊列(如果同時來了多個信號,響應是沒有規(guī)律)
(2)用戶自定義的信號 10) SIGUSR1 12) SIGUSR2
(3)34 到 64 號信號叫做實時信號:支持隊列,是 linux 系統(tǒng)中后面添加進來的信號
信號類似于中斷:硬件 軟件
以上信號有兩個很特殊:SIGKILL,SIGSTOP 不能夠被忽略,也不能被阻塞

2、信號相關的操作函數(shù)
(1)發(fā)送信號 kill()

 #include  signal.h 
 int kill(pid_t pid, int sig);
  參數(shù): pid ----》進程的 id
 正數(shù):要接收信號的進程的進程號
 0:信號被發(fā)送到所有和 pid 進程在同一個進程組的進程
 -1:信號發(fā)給所有的進程表中的進程(除了進程號最大的進程外)
 sig ----》信號名字
  返回值:0  成功
  -1  出錯

(2)信號的捕捉 signal()

 #include  signal.h 
 void (*signal(int sig, void (*func)(int)))(int); // SIGKILL
  參數(shù):sig ----》你需要捕捉的那個信號
 void (*func)(int) ----》函數(shù)指針, 回調(diào)函數(shù),捕捉到對應的信號的時候就調(diào)用該函數(shù);第二個參數(shù)除了可以傳遞一個函數(shù)指針意外,還可以使用以下兩個宏定義: SIG_IGN ---- 你捕捉到的那個信號會被忽略
 SIG_DFL-----》你捕捉的信號會采用系統(tǒng)默認的方式響應
 返回值:成功:設置之前的信號處理方式
 出錯:-1

(3)等待信號 pause()

#include  unistd.h 
int pause(void);
 返回值:-1  把 error 值設為 EINTR
 0  成功

(4)信號的阻塞
每個進程都有屬于它自己的一個信號掩碼(也就是該進程在運行的過程中會阻塞掉的那些信號就被稱作信號掩碼)。
關于信號掩碼操作的一系列函數(shù):

#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):判斷某個信號在不在該掩碼中
 參數(shù):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 ---》你需要捕捉的信號對應的響應函數(shù)就定義在這個結(jié)構(gòu)體
 oact ---》原來的
 struct sigaction
 { void(*) (int) sa_handler ----》  信號的響應函數(shù)
 sigset_t sa_mask ---》信號的掩碼
 int sa_flags ----》 SA_SIGINFO
 void(*) (int, siginfo_t * ,void )---》信號的響應函數(shù)
 }
 sa_flags ---》等于 SA_SIGINFO,那么信號的響應函數(shù)是 void(*) (int, siginfo_t * ,void )  不等于,那么信號的響應函數(shù)是 void(*) (int) 
 siginfo_t---》/usr/include/i386-linux-gnu/bits/siginfo.h  保存的是信號的狀態(tài)信息,信號的標號,發(fā)送該信號的進程的 id 等等這些

2.1.4 共享內(nèi)存

查看共享內(nèi)存: ipcs -m
刪除共享內(nèi)存: ipcrm -m 共享內(nèi)存的 id
SYSTEM-V ipc 通信方式:共享內(nèi)存、信號量、消息隊列。

1、共享內(nèi)存特點:跟 mmap()思想上有些類似

在進程通信方式中共享內(nèi)存是效率最高的,進程可以直接讀寫內(nèi)存,而不需要任何數(shù)據(jù)的拷貝;

如果代碼不人為地刪除共享共享內(nèi)存,那么程序退出的時候它還在;

多個進程共享一段內(nèi)存,因此也需要依靠某種同步機制,如互斥鎖和信號量等

2、共享內(nèi)存對應的一系列操作函數(shù)
(1)創(chuàng)建共享內(nèi)存:shmget()

#include  sys/shm.h  
int shmget(key_t key, size_t size, int shmflg);
返回值:成功—共享內(nèi)存對象的 mid(標識符)  出錯—-1
參數(shù):key----》創(chuàng)建共享內(nèi)存需要用到的鍵值
 size----》內(nèi)存空間的大小(字節(jié)) 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);
  參數(shù):pathname----》  路徑名
 proj_id----》整數(shù)
 ftok(“.” , 11)  生成一個唯一的 key 值
  進程 1:ftok(“.” , 11) ----》shmget( 100);.............
  進程 2:ftok(“/home/gec” , 11) ----》shmget( 106); 
   無法通信,要確保鍵值一致才能通信
 
 ** 方法二:** 不使用 ftok(),程序員自己寫個數(shù)字  
 shmget((key_t)1234, size_t size, int shmflg);

(2)映射共享內(nèi)存到用戶空間 shmat()

#include  sys/shm.h 
void *shmat(int shmid, const void *shmaddr, int shmflg);
  返回值:成功—映射到用戶空間的那片地址的首地址   出錯—-1
  參數(shù):shmid ----》使用 shmget 的返回值
 shmaddr----》一般設置為 NULL  系統(tǒng)自動分配
 shmflg----》 SHM_RDONLY:共享內(nèi)存只讀
   一般設置為 0:  共享內(nèi)存可讀寫  
  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);
參數(shù):shmaddr----》 shmat()共享內(nèi)存映射后的地址
返回值:成功—0  出錯—-1

(4)刪除共享內(nèi)存:shmctl()

 #include  sys/shm.h 
 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  參數(shù): shmid----》共享內(nèi)存的 id
 cmd----》IPC_RMID  刪除共享內(nèi)存
 IPC_STAT (獲取對象屬性)
   IPC_SET (設置對象屬性)
 *buf----》指定 IPC_STAT/IPC_SET 時保存共享內(nèi)存的狀態(tài)信息
 返回值:成功 失敗—-1

3、共享內(nèi)存簡單示例

shm_write.c :----------》int main() {
 int shmid;
 int *p;
 //  創(chuàng)建共享內(nèi)存
 shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
 if((shmid == -1) (errno == EEXIST)) {shmid = shmget((key_t)456,1024,0666);
 //  映射共享內(nèi)存到進程
 p = (int *)shmat(shmid,NULL,0);
 *p = 10;
 //  解除映射
 shmdt(p);
 //  刪除內(nèi)存
 //shmctl(shmid,IPC_RMID,NULL);
 return 0;
shm_read.c :----------》int main() {
 int shmid;
 int *p;
 //  創(chuàng)建共享內(nèi)存
 shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
 if((shmid == -1) (errno == EEXIST)) {shmid = shmget((key_t)456,1024,0666);
 //  映射共享內(nèi)存到進程
 p = (int *)shmat(shmid,NULL,0);
 printf(p is :%d\n ,*p);
 //  解除映射
 shmdt(p);
 //  刪除內(nèi)存
 shmctl(shmid,IPC_RMID,NULL);
 return 0;
}

2.1.5 消息隊列

消息隊列就是一個消息的列表。用戶可以在消息隊列中添加消息、讀取消息等。
消息隊列由消息隊列 ID 來唯一標識
消息隊列可以按照類型來發(fā)送 / 接收消息
消息隊列的操作包括創(chuàng)建或打開消息隊列、添加消息、讀取消息和控制消息隊列

1、消息隊列的特點
寫入消息隊列的信息,在編寫程序的時候會人為的去設置消息的類型(用整數(shù)來表示),目的是為了其它進程在讀取信息的時候能夠準確地通過消息的類型判斷要讀取的信息。

2、消息隊列操作的系列函數(shù)
(1)消息隊列的創(chuàng)建 msgget()

 #include  sys/msg.h 
 int msgget(key_t key, int msgflg);

(2)消息隊列的收發(fā)信息 msgsnd()msgrcv()

 #include  sys/msg.h 
 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 參數(shù):void *msgp ----》你要發(fā)送信息就存儲在這個指針中
  在實際的編程中我們都是定義一個結(jié)構(gòu)體來存儲信息
 struct msgbuf {
 long mtype; ----》消息的類型
 char mtext[100]; ----》消息的內(nèi)容
 }
 msgsz ----》消息的長度,大小
 msgflg ----》設置為 0   除開以上三種宏定義之外的 ---- 阻塞讀寫

linux 可不可以創(chuàng)建多個進程

(3)消息隊列的刪除 msgctl()

#include  sys/msg.h 
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

linux 可不可以創(chuàng)建多個進程

3、消息隊列通信簡單示例

pthread1.c :-----------》#define SIZE 64
// 數(shù)據(jù)接收結(jié)構(gòu)體
struct msg_rv
 int mtype;
 char msg[50];
// 數(shù)據(jù)發(fā)送結(jié)構(gòu)體
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);// 得到的值發(fā)送出去
 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
// 數(shù)據(jù)接收結(jié)構(gòu)體
struct msg_rv
 int mtype;
 char msg[50];
// 數(shù)據(jù)發(fā)送結(jié)構(gòu)體
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;
 // 發(fā)送
 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);// 得到的值發(fā)送出去
 printf( run here!\n 
 // 撤消消息隊列
 msgctl(msgid,IPC_RMID,0);
 return 0;
}

2.1.6 信號量

信號量協(xié)調(diào)不同進程對于共享資源的訪問,它是不同進程間或一個給定進程內(nèi)部不同線程間同步的機制。

linux 可不可以創(chuàng)建多個進程

1、信號量概述
(1)二值信號量
值為 0 或 1。與互斥鎖類似,資源可用時值為 1,不可用時值為 0
(2)計數(shù)信號量
值在 0 到 n 之間。用來統(tǒng)計資源,其值代表可用資源數(shù)
(3)對信號量的操作
P 操作:即申請資源,亦即將信號量值減 1,可能引起進程睡眠。
V 操作:即釋放資源,亦即將信號量值加 1,V 操作從不會睡眠。
等 0 操作:不申請也不釋放資源,而是令進程阻塞直到信號量的值為 0 為止

2、信號量相關的接口函數(shù)
(1)創(chuàng)建信號量集合 semget()

#include  sys/sem.h 
int semget(key_t key, int nsems, int semflg);
  參數(shù):key ----》鍵值
 nsems----》你創(chuàng)建的信號量集中信號量的個數(shù)
 semflg----》 IPC_CREAT|0666 組合
 返回值:成功—信號量 ID
 出錯—-1

(2)設置 / 刪除信號量集 semctl()

 #include  sys/sem.h 
 int semctl(int semid, int semnum, int cmd, ...);
   返回值:成功—0  失敗—-1

linux 可不可以創(chuàng)建多個進程

(3)信號量的 PV 操作 semop()
核心:信號量為 =0 時進行 p 操作,會阻塞程序,直到另一進程中是該信號進行了 v 操作后,本程序才會繼續(xù)運行 ——》key 值相同,信號量共通
p 減一操作
v 加一操作

 #include  sys/sem.h 
 int semop(int semid, struct sembuf *sops, size_t nsops);
  返回值:成功—0  出錯—-1
  參數(shù):semid ----》semget 的返回值
 nsops ---》要操作的信號量的個數(shù)(結(jié)構(gòu)體的個數(shù))
  sops---》信號量操作結(jié)構(gòu)體
 struct sembuf {
 short sem_num ;=   要操作的信號量的編號(數(shù)組下標)short sem_op; =  0 :  等待,直到信號量的值變成 0
  1 :  釋放資源,V 操作
  -1 :  分配資源,P 操作  
 short sem_flg; =  0/IPC_NOWAIT/SEM_UNDO
 SEM_UNDO:  程序結(jié)束時 (不論正常或不正常),保證信號值會被重設為 semop() 調(diào)用前的值;
 IPC_NOWAIT:  對信號的操作不能滿足時,semop()不會阻塞,并立即返回,同時設定錯誤信息;};

3、信號量協(xié)同共享內(nèi)存示例代碼

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;
 //  創(chuàng)建信號量集合
 semid = semget((key_t)789,2,IPC_CREAT|0666);
 if(semid == -1) {
 perror( creat sem failed!\n 
 return -1;
 //  創(chuàng)建共享內(nèi)存
 shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
 if((shmid == -1) (errno == EEXIST)) {shmid = shmget((key_t)456,1024,0666);
 //  映射共享內(nèi)存到進程
 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);
 // 刪除共享內(nèi)存
 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 操作
 //  創(chuàng)建信號量集合
 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));
 //  創(chuàng)建共享內(nèi)存
 shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
 if((shmid == -1) (errno == EEXIST)) {shmid = shmget((key_t)456,1024,0666);
 //  映射共享內(nèi)存到進程
 p = (char *)shmat(shmid,NULL,0);
 while(1) {semop(semid, mysembuf1,1); //  對信號量 1 進行 p 操作(減一)不阻塞,因為初值為 1
  //  執(zhí)行完這句話以后信號量的值就立馬變成 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);
 // 刪除共享內(nèi)存
 shmctl(semid, IPC_RMID, NULL);
}

2.3 IPC shell 命令操作

ipcs -q 查看消息隊列

ipcrm -q MSG_ID 刪除消息隊列

ipcs -m 查看共享內(nèi)存

ipcrm -m SHM_ID 刪除共享內(nèi)存

ipcs -s 查看信號量

ipcrm -s SEM_ID 刪除信號量

2.2 進程間通訊方式比較

pipe: 具有親緣關系的進程間,單工,數(shù)據(jù)在內(nèi)存中

fifo: 可用于任意進程間,雙工,有文件名,數(shù)據(jù)在內(nèi)存

signal: 唯一的異步通信方式

msg:常用于 cs 模式中,按消息類型訪問,可有優(yōu)先級

shm:效率最高(直接訪問內(nèi)存),需要同步、互斥機制

sem:配合共享內(nèi)存使用,用以實現(xiàn)同步和互斥

以上就是關于“l(fā)inux 可不可以創(chuàng)建多個進程”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望丸趣 TV 小編分享的內(nèi)容對大家有幫助,若想了解更多相關的知識內(nèi)容,請關注丸趣 TV 行業(yè)資訊頻道。

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-07-12發(fā)表,共計16116字。
轉(zhuǎn)載說明:除特殊說明外本站除技術相關以外文章皆由網(wǎng)絡搜集發(fā)布,轉(zhuǎn)載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 津南区| 桃园市| 长海县| 锡林郭勒盟| 梁河县| 都安| 视频| 两当县| 福贡县| 萍乡市| 阜南县| 乌海市| 大埔县| 淮南市| 贵州省| 山丹县| 永春县| 定陶县| 房产| 鞍山市| 徐汇区| 苍山县| 青神县| 兰坪| 永济市| 尚志市| 新闻| 白山市| 五原县| 古浪县| 酉阳| 綦江县| 嘉祥县| 大连市| 昌都县| 曲阜市| 固安县| 大姚县| 伊春市| 双桥区| 如东县|