進程與線程(三)

進程與線程(三)

  • 進程間通信
  • 傳統間的進程間通信機制
    • 無名管道
      • 無名管道的特征
      • 無名管道的創建
      • 父子進程通信
      • 測試管道的大小
      • 管道讀寫易出現的問題
    • 有名管道
      • 創建有名管道
      • 有名管道的寫端代碼
      • 有名管道的讀端代碼
    • 信號
      • 信號的特征
      • 產生信號
        • 硬件來源
        • 軟件來源
          • 發送信號的函數:kill() raise()
          • 定時器信號
      • 響應信號
      • 信號安裝:
        • 忽略信號
        • 執行缺省操作
        • 捕捉信號

進程間通信

在這里插入圖片描述
常用的進程間通信方式
1、傳統的進程間通信方式
無名管道(pipe)、有名管道(fifo)和信號(signal)
2、System V IPC對象
共享內存(share memory)、消息隊列(message queue)和信號燈(semaphore)
3、BSD
套接字(socket)

傳統間的進程間通信機制

無名管道

在這里插入圖片描述

無名管道的特征

這里所說的管道主要指無名管道,它具有如下特點:
1、只能用于具有親緣關系的進程之間的通信
2、半雙工的通信模式,具有固定的讀端和寫端
3、管道可以看成是一種特殊的文件,對于它的讀寫可以使用文件IO如read、write函數。

在這里插入圖片描述

無名管道的創建

在這里插入圖片描述

父子進程通信

(1)創建無名管道
(2)創建子進程
(3)完成父子通信業務
通信流程:子進程發消息給父進程
思路:fd[0]讀端 fd[1]寫端 ----》固定的
子進程:發消息(fd[1]有效) fd[0]關閉掉
父進程:收消息(fd[0]有效) fd[1]關閉掉

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, const char *argv[])
{//1,創建無名管道//定義數組,存儲讀寫的文件描述符int fd[2];if(pipe(fd) < 0){perror("pipe error");return -1;}printf("pipe ok!\n");//2,創建子進程pid_t pid = fork();if(pid < 0){perror("fork error");close(fd[0]);close(fd[1]);return -1;}else if(0 == pid){//子進程的地址空間//關閉讀端close(fd[0]);//發消息(write)//定義消息文本char Message[100] = {0};printf("請輸入需要發送的消息:");fgets(Message, sizeof(Message), stdin);//使用write寫入到管道中write(fd[1], Message, strlen(Message));printf("send ok!\n");}else{//父進程的地址空間//關閉寫端close(fd[1]);//收消息(read)//定義接收消息的地址空間char Message[100] = {0};read(fd[0], Message, sizeof(Message));printf("來自于子進程:%s\n",Message);}return 0;
}

在這里插入圖片描述

測試管道的大小

思路:往管道中不管寫入,直到寫不進去,此時寫入的字節數就是管道容量大小

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main()
{//測試管道大小//創建管道int fd[2];if(pipe(fd) < 0){return -1;}printf("create pipe ok!\n");//寫入數據,不斷寫入//1KB == 1024bytechar buf[1024] = {0};int size = 0;while(1){int wr_count = write(fd[1], buf, 1024);//每次寫入1024個字節 即:1KB大小if(wr_count < 0){perror("write error");break;}else if(0 == wr_count){printf("nothing wai be written...\n");break;}else{size++;printf("%dKB\n",size);}}close(fd[0]);close(fd[1]);return 0;
}

在這里插入圖片描述

管道讀寫易出現的問題

當管道中無數據時,讀操作會阻塞
向管道中寫入數據時,linux將不保證寫入的原子性,管道緩沖區一有空閑區域,寫進程就會試圖向管道寫入數據。如果讀進程不讀走管道緩沖區中的數據,那么寫操作將會一直阻塞。
只有在管道的讀端存在時,向管道中寫入數據才有意義。否則,向管道中寫入數據的進程將收到內核傳來的SIGPIPE信號(通常Broken pipe錯誤)。

在這里插入圖片描述
在這里插入圖片描述

案例:驗證管道斷裂

#include <stdio.h>
#include <sys/types.h>
#inckude <unistd.h>
#include <string.h>
int main()
{int fd[2];if(pipe(fd) < 0){return -1;}printf("pipe ok!\n");//關閉讀端 (保證寫入管道時,讀端不存在---》才會出現管道斷裂)close(fd[0]);pid_t pid = fork();if(pid < 0){return -1;}else if(0 == pid){//子進程//寫入數據到管道char data[1024];printf("Please input:");fgets(data, sizeof(data), stdin);write(fd[1], data, strlen(data));printf("寫入管道成功!\n");exit(100);}else{//父進程//使用wait阻塞等待子進程退出時回收其資源int wstatus;//保存子進程的退出狀態值wait(&wstatus);//當執行該40行時,說明子進程已經退出(收到信號異常退出還是正常執行exit(100)退出?)//分類討論if(WIFSIGNALED(wstatus)){//收到信號導致子進程退出printf("導致子進程退出的信號是:%d\n",WTERMSIG(wstatus));//SIGPIPE==13號}else if(WIFEXITED(wstatus)){//正常退出---》執行了exit(100)printf("子進程的退出狀態數值為:%d\n",WEXITSTATUS(wstatus));//父進程char buf[1024] = {0};read(fd[0], buf, sizeof(buf));printf("讀取的結果是:%s\n",buf);}}return 0;
}

有名管道

無名管道只能用于具有親緣關系的進程之間,這就限制了無名管道的使用范圍
1、有名管道可以使互不相關的兩個進程互相通信。
2、有名管道可以通過路徑名來指出,并且在文件系統中可見
3、進程通過文件IO來操作有名管道
4、有名管道遵循先進先出規則,不支持如lseek() 操作

在這里插入圖片描述

創建有名管道

在這里插入圖片描述
在這里插入圖片描述

有名管道的寫端代碼

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//創建有名管道(同時也創建好了管道文件)if(mkfifo("./fifo.txt", 0664) < 0 && errno != EEXIST){perror("mkfifo error");return -1;}printf("mkfifo ok!\n");//以只寫方式打開創建好的管道文件int fw = open("./fifo.txt", O_WRONLY);if(fw < 0){perror("open_write error");return -1;}printf("open_write ok!\n");char buf[1024] = {0};while(1){//對于buf做清空bzero(buf, sizeof(buf));printf("客戶端:");fgets(buf, sizeof(buf), stdin);//判斷客戶端是否辦理完畢業務if(0 == strncasecmp("quit", buf, 4)){printf("業務辦理結束!\n");break;}//發送消息int wr_count = write(fw, buf, strlen(buf));if(wr_count < 0){perror("write error");break;}else if(0 == wr_count){printf("未寫入任何內容...\n");break;}else{printf("實際發送%d字節內容!\n",wr_count);printf("send ok!\n");}}//關閉寫端close(fw);return 0;
}

有名管道的讀端代碼

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//創建有名管道(同時也創建好了管道文件)if(mkfifo("./fifo.txt", 0664) < 0 && errno != EEXIST){perror("mkfifo error");return -1;}printf("mkfifo ok!\n");//以只讀方式打開創建好的管道文件int fr = open("./fifo.txt", O_RDONLY);if(fr < 0){perror("open_read error");return -1;}printf("open_read ok!\n");char buf[1024] = {0};while(1){//對于buf做清空bzero(buf, sizeof(buf));//接收消息int rd_count = read(fr, buf, sizeof(buf));if(rd_count < 0){perror("read error");break;}else if(0 == rd_count){printf("客戶端業務處理結束!\n");break;}else{printf("成功接收%d個字節!\n",rd_count);printf("來自于客戶端:%s\n",buf);}}close(fr);return 0;
}

在這里插入圖片描述

信號

信號的特征

信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式
信號可以直接進行用戶空間進程和內核進程之間的交互,內核進程也可以利用它來通知用戶空間進程發生了哪些系統事件。
如果該進程當前并未處于執行態,則該信號就由內核保存起來,直到該進程恢復執行再傳遞給它;如果一個信號被進程設置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消時才被傳遞給進程
同步:發送方發送數據,等待接收方響應之后才發下一個數據包的通訊方式
異步:發送方發送數據,不等待接收方發回響應,接著發送下一個數據包的通訊方式

產生信號

硬件來源

從鍵盤輸入:
ctrl c : SIGINT信號 ,默認處理方式為進程終止
ctrl : SIGQUIT信號,作用與SIGINT類似,默認處理方式為進程終止
ctrl z: SIGTSTP信號,默認處理方式為進程暫停
SIGSTOP信號:只能通過kill產生,默認處理方式進程暫停

軟件來源
發送信號的函數:kill() raise()

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:不僅可以終止進程,也可以向進程發送其他信號
#include <signal.h>
int raise(int sig);
功能:只允許進程向自身發送信號

案例:首先使用fork創建子進程,在子進程中使用raise()向自身發送SIGSTOP信號,使子進程暫停;接下來在父進程中當時間戳超過10秒之后,調用kill()向子進程發送SIGKILL信號殺死子進程并回收子進程的退出
資源,且檢查導致子進程退出是否因為收到了信號,如果是,打印出該信號是幾號?如果子進程是正常退出,則打印出子進程退出的狀態值是多少?(模擬正常退出:例如在終端給子進程發送SIGCONT信號來喚醒子進程,此時子進程繼續執行,直到執行了exit()退出)

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{//功能:練習kill()和raise()pid_t pid = fork();if(0 == pid){//子進程printf("child PID = %d\tparent PID = %d\n", \getpid(), getppid());//使用raise()函數給子進程自己發送SIGSTOP,讓其暫停raise(SIGSTOP);printf("I am wakeup...\n");printf("hahahah\n");exit(88);}else{//父進程//定義保存時間的變量time_t start_time, current_time;time(&start_time);int wstatus;//回收子進程退出資源while(1){//獲取當前的系統時間(更新的)time(&current_time);if(current_time - start_time > 10){//使用kill()函數給子進程發送SIGKILL信號,讓其終止kill(pid, SIGKILL);}int exitID = waitpid(-1, &wstatus, WNOHANG);if(exitID < 0){perror("waitpid error");break;}else if(0 == exitID){printf("子進程還未結束...\n");}else{//已成功回收子進程printf("已成功回收子進程的退出資源...\n");if(WIFSIGNALED(wstatus)){printf("異常退出,造成子進程退出的信號是:%d號信號!\n",WTERMSIG(wstatus));}else if(WIFEXITED(wstatus)){printf("子進程正常退出,退出狀態數值為:%d\n",WEXITSTATUS(wstatus));}break;}//每隔1秒 輪詢一次sleep(1);}}return 0;
}

在這里插入圖片描述

定時器信號

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
參數:秒數
當定時器指定的時間到了時,它就向進程發送SIGALARM信號,信號的默認操作是結束進程.
注意:每個進程只能有一個鬧鐘時間。如果在調用alarm時,以前已為該進程設置過鬧鐘時間,而且它還沒有超時,則該鬧鐘時間的剩余時間值作為本次alarm函數調用的值返回,以前登記的鬧鐘時間則被新值代換。如果有以前登記的尚未超過的鬧鐘時間,而新設的鬧鐘時間值為0,則取消以前的鬧鐘時間,其剩余時間值仍作為函數的返回值。
擴充:將調用進程掛起函數:
#include <unistd.h>
int pause(void);
功能:會造成進程主動掛起(處于阻塞狀態,并主動放棄CPU),并且等待信號將其喚醒

案例:使用alarm()和pause()函數

#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{printf("hello!\n");//設定鬧鐘 5秒alarm(5);printf("I will.....\n");//進程掛起pause();printf("java!\n");printf("exit!\n");return 0;
}

在這里插入圖片描述

響應信號

在這里插入圖片描述
在這里插入圖片描述

信號安裝:

signal函數原型:
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
優化原型:看的更清楚。
//給函數指針類型取別名
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能:進行信號安裝
參數:
參數1:需要安裝的信號名稱
參數2:對于該信號的處理函數(該函數是用戶自定義的,且返回值類型void ,形參是一個
int的函數的地址)
PS:參數2的位置可以傳入:忽略(SIG_IGN) 執行缺省操作(SIG_DFL),信號處理函數的地址
返回值:成功安裝完畢的信號處理函數的地址。

信號的處理方式可以有:
1、忽略信號
2、執行缺省操作(默認操作)
3、捕捉信號(自定義信號處理函數)

忽略信號
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//安裝信號signal(SIGINT, SIG_IGN);//忽略信號//執行一段代碼while(1){printf("hello!\n");sleep(1);}return 0;
}

在這里插入圖片描述

執行缺省操作
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//安裝信號//signal(SIGINT, SIG_IGN);//忽略信號signal(SIGINT, SIG_DFL);//缺省操作(信號默認處理方式)//執行一段代碼while(1){printf("hello!\n");sleep(1);}return 0;
}

在這里插入圖片描述

捕捉信號
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler_func(int signum)
{printf("不處理哦!\n");
}
int main(int argc, const char *argv[])
{//安裝信號//signal(SIGINT, SIG_IGN);//忽略信號//signal(SIGINT, SIG_DFL);//缺省操作(信號默認處理方式)signal(SIGINT, &handler_func);//執行一段代碼vwhile(1){printf("hello!\n");sleep(1);}return 0;
}

在這里插入圖片描述
注意:一個信號處理函數響應多個信號

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler_func(int signum)
{//按照信號的類別進行控制switch(signum){case SIGINT:{printf("paly music...\n");break;}case SIGTSTP:{printf("paly vedio...\n");break;}case SIGQUIT:{printf("quit...\n");break;}default:{printf("信號無法響應...\n");}}return;
}
int main(int argc, const char *argv[])
{//安裝信號//signal(SIGINT, SIG_IGN);//忽略信號//signal(SIGINT, SIG_DFL);//缺省操作(信號默認處理方式)//signal(SIGINT, &handler_func);//捕捉信號--》自定義信號處理函數//安裝3個信號signal(SIGINT, &handler_func);//播放音樂signal(SIGTSTP, &handler_func);//播放視頻signal(SIGQUIT, &handler_func);//退出app//執行一段代碼while(1){printf("hello!\n");sleep(1);}return 0;
}

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20660.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20660.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20660.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux chmod 命令

Linux chmod 命令 在 Linux 操作系統中&#xff0c;chmod 命令是非常重要的。它可以用于修改文件和目錄的訪問權限&#xff0c;以及控制用戶對系統資源的訪問。在這篇博客中&#xff0c;我們將深入探討 chmod 命令的使用方法&#xff0c;以及如何使用它來管理文件和目錄的訪問…

什么是主碼,什么是候選碼,主碼的候選碼的區別是什么

目錄 一、候選碼 (Candidate Key) 1.定義 2.特點 二、主碼 (Primary Key) 1.定義 2.特點 三、關系和區別 1.關系 2.區別 一、候選碼 (Candidate Key) 1.定義 候選碼是能夠唯一標識數據庫表中每一條記錄的屬性集合。一個表可以有一個或多個候選碼。 2.特點 每個候選…

【U-Boot 源碼深度解析】000 - 文章鏈接匯總

【U-Boot 源碼深度解析】000 - 文章鏈接匯總 下載鏈接 https://ftp.denx.de/pub/u-boot/ 《【U-Boot 源碼深度解析】001 - Ubuntu 24.04 虛擬機 及 基礎環境搭建》

ORACLE RAC的一些基本理論知識

一 . Oracle RAC 的發展歷程 1. Oracle Parallel Server (OPS) 早期階段&#xff1a;Oracle 6 和 7 Oracle Parallel Server&#xff08;OPS&#xff09;是 Oracle RAC 的前身。 通過多個實例并行訪問同一個數據庫來提高性能。 共享磁盤架構&#xff0c;利用分布式鎖管理&am…

JZ2440筆記:Nor FLASH驅動程序

(1)測試原有的驅動&#xff1a; make menuconfig修改physmap-flash驅動配置&#xff1a;起始0&#xff0c;大小0x100000&#xff0c;位寬2&#xff0c;make modules后拷貝過去測試&#xff1a; # insmod physmap.ko physmap platform flash device: 01000000 at 00000000 phy…

C#面:解釋什么是viewstate,能否禁用?是否所用控件都可以禁用

在C#中&#xff0c;ViewState是 ASP.NET Web Forms中的一個重要概念。它用于在Web應用程序中跨頁面請求之間保存和恢復頁面的狀態數據。ViewState的主要目的是在頁面回發&#xff08;postback&#xff09;期間保持頁面上的控件狀態&#xff0c;以便在頁面重新加載后能夠正確地還…

addEventListener()方法中的幾個參數,以及作用

addEventListener() 方法是 JavaScript 中用于處理指定元素的指定事件的函數。它有三個參數&#xff1a; type&#xff08;必需&#xff09;&#xff1a;一個字符串&#xff0c;指定要監聽的事件名。 listener&#xff08;必需&#xff09;&#xff1a;一個實現了 EventListen…

【第1章】SpringBoot實戰篇之注冊接口

文章目錄 前言一、代碼部分1. User2.UserMapper13. UserSerivce4. UserController15. Result 二、測試1.注冊2.再次注冊 總結 前言 下面介紹用戶注冊接口。 一、代碼部分 1. User package org.example.springboot3.bigevent.entity;import com.baomidou.mybatisplus.annotat…

開發板uboot與virtualbox虛擬機、windows11網絡互通

環境&#xff1a;virtualbox中ubuntu22.04.4&#xff0c;開發板通過網線再經過拓展塢usb網卡跟windows11連接。連接如下&#xff1a; 1、關閉windows防火墻(重要) 2、先在VirtualBox的工具選項創建兩個網絡【僅主機(Host-Only)網絡】和【NAT網絡】 僅主機(Host-Only)網絡的ip:…

Linux下Qt Creator無法輸入中文(已解決)

1. 首先確保安裝了搜狗輸入法&#xff0c;且能正常運行。 2.克隆源碼到本地。 git clone https://gitcode.com/fcitx/fcitx-qt5.git 3.檢查Qt Creator版本&#xff0c;如下圖所示&#xff0c;為基于Qt6的。 4. 進入源碼目錄&#xff0c;建立build文件夾&#xff0c;修改CMak…

并發任務的進化之旅

An evolutionary journey of multitasking 多重任務的進化之旅 In the beginning, computers had one CPU that executed a set of instructions written by a programmer one by one. No operating system (OS), no scheduling, no threads, no multitasking. This was how …

js實現基礎購物車的制作

功能需求&#xff1a; 1.點擊添加商品按鈕會出現三個輸入框&#xff08;名稱&#xff0c;價格&#xff0c;數量那三格&#xff0c;以及確認和取消按鈕&#xff09;。 2.點擊確認后所輸入的值會自動形成一行添加到表格中 3.點擊編輯按鈕時&#xff0…

css動態導航欄鼠標懸停特效

charset "utf-8"; /*科e互聯特效基本框架CSS*/ body, ul, dl, dd, dt, ol, li, p, h1, h2, h3, h4, h5, h6, textarea, form, select, fieldset, table, td, div, input {margin:0;padding:0;-webkit-text-size-adjust: none} h1, h2, h3, h4, h5, h6{font-size:12px…

8、資源操作 Resource

目錄 8.1、Spring Resources概述補充&#xff1a;什么是 low-level 資源&#xff1f;1. 文件系統資源2. 類路徑資源3. URL資源4. 內嵌資源5. InputStream資源6. ServletContext資源示例代碼結論 8.2、Resource接口8.3、Resource的實現類8.3.1、UrlResource訪問網絡資源1&#x…

LIO-EKF: 運行數據UrbanNav與mid360設備詳細教程

一、代碼連接 代碼下載連接&#xff1a; YibinWu/LIO-EKF: Maybe the simplest LiDAR-inertial odometry that one can have. (github.com) 編譯步驟&#xff1a; cd srcgit clone gitgithub.com:YibinWu/LIO-EKF.gitcatkin_makesource devel/setup.bash 運行步驟&#xff1a; …

為什么要保持方差為1

1.數值穩定性&#xff1a; 在機器學習和深度學習中&#xff0c;維持激活函數輸入的方差在一個合理范圍內&#xff08;如1&#xff09;是很重要的&#xff0c;這有助于防止在訓練過程中發生梯度消失或梯度爆炸的問題。如果方差過大或過小&#xff0c;經過多層網絡后輸出結果的方…

java并發處理機制

在Java中&#xff0c;并發處理機制主要是通過線程來實現的。Java提供了豐富的類和接口來支持多線程編程&#xff0c;主要集中在 java.util.concurrent 包中。以下是一些關鍵的并發處理機制&#xff1a; 1.線程創建&#xff1a;可以通過繼承 Thread 類或實現 Runnable 接口來創建…

公園【百度之星】/圖論+dijkstra

公園 圖論dijkstra #include<bits/stdc.h> using namespace std; typedef long long ll; typedef pair<ll,ll> pii; vector<ll> v[40005]; //a、b、c分別是小度、度度熊、終點到各個點的最短距離 ll a[40005],b[40005],c[40005],dist[40005],st[40005]; void…

原碼、反碼和真值都不存在!

文章目錄 補碼的理解十進制計算二進制計算 補碼和真值換算數制轉換負數補碼轉真值負數真值轉補碼 注&#xff1a;均來自 做而論道 答主的理解。 補碼的理解 在計算機系統中&#xff0c;根本就沒有原碼和反碼&#xff0c;真值也是不存在的。在計算機系統中&#xff0c;并不使用…

java 遠程調試

1.遠程啟動時 jdk1.8-32\jre\bin\java.exe -Dfile.encodingUTF-8 -Djava.library.pathlib -agentlib:jdwptransportdt_socket,servery,suspendn,address5005 -jar local-com.yuetai.service-0.0.1-SNAPSHOT.jar --spring.config.locationapplication.yml 2.本地調試項目連接遠…