Linux間的進程通信;以及子進程的創建


  1 "-----第六天-----------------------------------------------------------------------------"
  2 
  3 1.版本控制:svn/git;
  4 
  5 2.進程的概念:
  6     1)程序和進程;
  7         每個進程操作系統會為它分配 0-4G 的虛擬內存空間(32位操作系統); 其中0-3G為用戶內存空間,進程可以對它進行讀寫操作; 3G - 4G 為系統內核空間,進程沒有讀寫權限。
  8         進程只能讀寫用戶空間,沒有權限讀寫內核空間(kernel);
  9     2)內存頁面的概念
 10         操作系統是按頁來管理內存的;每個頁有4096個字節。
 11         還可以設置頁面屬性:如char *str = "hello",字符串在只讀數據段,此時只能讀,不能寫。
 12 
 13     3)進程的4種形態:0-3級; 0 級(內核態),最高; 3級(用戶態);最低。
 14 
 15     2)並發:
 16     3)單道程序設計/多道程序設計
 17     4)CPU/MMU
 18     5)PCB:結構體;其中有一個指針指向文件描述符表;文件描述符表中存儲1024個指針;已經打開的文件結構體。
 19         每個進程有屬於自己的PCB(進程的狀態描述符);但是多個進程只有一個內核區。
 20 3.環境變量控制
 21     1)echo $名稱;    查看該名稱的環境變量。
 22     2)查看系統的環境變量: env;
 23     3)獲取環境變量的值:char *getenv(const char* name);    獲取鍵name環境變量的值。
 24     4)設置環境變量:int    setenv(const char *name, const char *value, int rewrite);    將環境變量name的值設置為 value。
 25                     如果環境變量 name 已存在;則 rewrite 非0,覆蓋原來的環境變量;rewrite為0,則不覆蓋原來的環境變量。
 26     5)刪除環境變量: void unsetenv(const char *name) 刪除 name    的定義;
 27 
 28     6)進程里可以設置自己的環境變量,避免設置在終端,影響他人。
 29 4.環境控制原語:
 30     1)fork:
 31     2)wait/waitpid
 32     3)
 33 
 34 5. printenv.c
 35 
 36 6.進程的狀態:4 種或 5 種,都對。 就緒, 運行, 睡眠, 停止。
 37 
 38 7.CPU的組成狀態: 運算器,寄存器,控制器,譯碼器。
 39 
 40     操作系統完成進程調度。(cpu進行時鍾周期運算)
 41     cpu的分時復用功能,使進程看起來像多進程。
 42     在單核 cpu 上,同一時間只能有一個進程處於運行狀態。那么多核 cpu,就可以有多個進程處於運行狀態。
 43 
 44 8.進程原語:
 45     1)創建子進程。pid_t fork();    調用1次,返回兩次;在父進程返回子進程的 PID, 在子進程返回0.
 46     2) fork()的工作進程:先調用 creat(),創建一個進程, 在調用 clone(),給子進程復制父進程的內容;
 47     3)pid = fork();    //此時父子進程就都出來了。
 48     4)pid_t getpid();  返回調用進程的PID號;
 49        pid_t getppid();    返回調用進程的父進程的PID號
 50        當在子進程中 getpid(),得到的值與 fork()父進程的返回值相等,都是這個子進程的id號
 51     5)父子進程:讀時共享,寫時復制。
 52     6)最大創建進程 個數;
 53 9.    uid_t getuid(void);        //返回實際用戶id;
 54     uid_t geteuid(void);     //返回有效用戶id;
 55     "注意: 文件實際用戶與有效用戶在創建該文件的家目錄下時,如果沒有修改,此時是相通的。
 56             當文件被拷貝到root目錄時,   "    
 57     設置用戶id; chmod 04755;  4 是設置用戶ID。  完后效果為 -rwsr-xr-x;  s表示設置了用戶ID。
 58             作用:當執行此文件時,執行者有效用戶ID變為此文件的所有者。
 59             設置用戶組ID: chmod 06777; 6 是設置用戶ID和組ID; 效果為: -rwsrwsrwx;
 60 
 61 10. exec族函數的使用:
 62     1)功能: 去磁盤中加載另一個可執行程序,用它的代碼段、數據段替換掉當前可執行程序的代碼段、數據段;然后從后加載的這個程序退出,被替換的程序后續代碼就不執行了。(換核不換殼,)
 63     2)一般情況下:與 fork() 函數聯合使用;。  同時exec族函數不會創建新進程, 所以該程序的 ID 不會改變。
 64 
 65     3)函數的返回值: 成功沒有返回值,運行完畢,自己退出。
 66                       失敗返回 -1;
 67     4)exec族函數的規律: l(list):命令行參數列表。  p(path): 搜索file時使用path變量。
 68                           v(vector):使用命令行參數數組。  e(environment):使用環境變量的數組,不使用進程原有的環境變量,設置新加載程序的環境變量;
 69     
 70     5int execl(const char *path, const char *argc, ...); 
 71     例如:execl("./bin/ls", "ls", "-l", "-a", NULL);    這幾個參數的作用:其中第一個參數:"./bin/ls",命令的環境變量(即所在文件),不可以改變,必須完整正確;
 72         "ls":占位參數,可以隨便寫,一般寫成該命令。(不能缺失,如果缺失了,就會把后面的命令參數變為占位參數,輸出結果會出錯。) "-l","-a"該命令的參數。   NULL:衛兵,執行到這里時命令結束。
 73 
 74 11. toupper():將小寫字母變為大寫。
 75 
 76 12.僵屍進程,孤兒進程:
 77     1)S:睡眠狀態;  Z:僵屍狀態;
 78     2)僵屍進程的產生原因:用戶空間釋放,內核空間的PCB沒有釋放,等着父進程回收。(它消耗的是內核當中的內存資源)
 79             即:子進程退出,父進程沒有回收子進程資源(PCB),則子進程變為僵屍進程。
 80     3)子進程的PCB(在內核空間)沒有釋放,是留給父進程回收用的。只有父進程回收后,僵屍進程才會消失。
 81     4)殺死僵屍進程的辦法是殺掉它的父進程。
 82 
 83   孤兒進程:父進程先於子進程結束,則子進程變為孤兒進程,子進程的變為1號進程init進程,由1號進程領養。
 84 
 85   "僵屍進程比孤兒進程更危險, 因為它不會自動關;,而孤兒進程會由 1 號進程領養,當它執行完畢后,會被 1 號進程回收" 86   做開發時主要避免的是僵屍進程的產生。用 wait(); waitpid(); 來避免僵屍進程的產生。
 87 
 88 
 89 13. pid_t wait(int* status);  (阻塞函數,等待回收子進程資源;如果沒有子進程,返回-1);
 90     1)返回值:回收的子進程的ID號,    wait(NULL);不關心子進程如何死亡,直接回收。
 91                                     wait(&st); 用st來保存子進程死亡時的狀態。
 92     
 93     2)    父進程的ID與它的進程組ID相同;子進程的組ID與父進程的ID相同。
 94         kill -9 -父進程的ID(即組進程的ID),這個進程組的所有進程都被殺死。"(注意 - 不能少)"
 95 
 96 14.pid_t waitpid(pid_t pid, int *status, int options);    (設置非阻塞。)
 97     第一個參數 pid的值類型:    1) < -1; 回收指定進程組內的任意子進程。(因為父進程與子進程屬於統一進程組,父進程與孫子進程屬於不同的進程組,但是他們有血緣關系。)
 98                                     -1;回收任意子進程(只要有血緣關系就行。)
 99                                     0; 回收 當前調用 waitpid 一個進程組的所有子進程。
100                                     > 0; 回收指定ID的子進程。
101     第二個參數:保存子進程的推出狀態。
102 
103     第三個參數:WNOHANG:如果沒有子進程退出,立即返回。(實現了非阻塞的 wait).
104 
105 15. 阻塞函數:非阻塞函數:在文件open(O_NONBLOCK)的時設置宏;讀常規文件不會出現阻塞,當讀偽文件時會出現。
106         例如:阻塞讀終端; 管道; 網絡;
107 
108     設置非阻塞后:應設置輪詢。
109         
110 
111 "=========進程間的通信(IPC)==========================================================================================================="
112 一。IPC方法:Linux環境下,多個進程間的通信,需要通過內核,在內核中開辟一塊緩沖區(賦予他用戶權限);進程1把數據從用戶空間拷貝到內核緩沖區,
113     進程2 把數據從內核緩沖區拷讀走,內核提供的這種機制稱為進程間的通信(IPC)。
114 
115     進程間的通信;四種方法:1.pipe管道 2.fifo有名管道,3.內存共享映射 4.Unix Domain Socket;
116     管道(使用最簡單);信號(開銷最小);共享映射區(速度最快,效率最高); 本地套接字(最穩定);
117 
118 二。pipe管道:
119 1.管道的特性:數據只能一個讀,一個寫,必須是一個方向。
120             半雙工:數據同一時刻只能有一個流向。即只能父進程寫,子進程讀; 或子進程寫, 父進程讀。
121             全雙工:數據同一時刻可以兩個方向。
122             單工:數據只能同一個方向流動。
123             dup2(int oldfd, int newfd);        將第一個參數拷貝給第二個參數。
124             1.其本質是一個偽文件(實為內核緩沖區)
125             2.有兩個文件描述符引用,一個讀端,一個寫端。
126             3.規定數據從管道的寫端流入,從讀端流出。
127   
128   管道的原理:管道實為內核使用環形隊列機制,借助內核緩沖區(4K)實現的。
129 
130   管道的局限性:1.數據不能自己寫,自己讀;
131                   2.管道中的數據不能反復讀取,一旦讀走,管道中不再存在。
132                   3.采用半雙工通信方式,數據只能在單方向上流動。
133                   4.只能在有血緣關系之間的進程使用管道。
134                   5.只能進行單向通信,雙向通信需要建立兩個管道。
135 
136 2. pipe()創建管道,用於有血緣關系之間的通信。(采用環形隊列實現的)
137     管道使用半雙工通信; 創建完管道完后,確定通信方向:父寫子讀,或子寫父讀。
138     如果想創建多條管道,一定要先pipe(),再fork(),使子進程得以繼承管道。
139 
140     使用管道時的四種注意情況:
141     1) 寫段關閉,讀端讀完管道里的內容時;再次讀,返回0,相當於讀到文件末尾EOF;
142     2)寫端未關閉,寫端無數據, 讀端讀完管道里的數據時,再次讀,阻塞。
143     3)讀端關閉, 寫段寫管道,產生SIGPIPE信號,寫進程默認情況下會終止進程。
144     4)讀端未讀管道數據,當寫段寫滿管道后,在此寫,阻塞。
145     5)使用管道,無須open,但需手動close。
146 
147     管道的緩沖區大小:1.函數的方法:fpathconf(int fd, int name);  第一個參數為管道描述符;第二個參數為情況標識符。
148                         2.命令: ulimit -a;
149 
150 3.    總結:
151     1.讀管道:
152         1.管道中有數據,read返回實際讀到的字節數;
153         2.管道中無數據,寫端都被關閉,read返回 0,相當於讀到文件末尾;
154                         寫端未關閉,read函數阻塞等待,(期待不久的將來會有數據到來,但此時會讓出CPU資源)
155     2.寫管道:
156         1.管道讀端全部關閉;進程異常中止(返回一個終止信號);
157         2.管道讀端未關閉:
158             1)管道已寫滿,write阻塞等待
159 
160             2)管道未寫滿,write將數據寫入,返回實際寫入的字節數;
161 
162 
163 "==========================================================================="
164 4.設置非阻塞管道的兩種辦法:
165     1)fcntl函數設置非阻塞管道;
166         int flg = fcntl(fd, F_GETFD);
167         flg |= O_NONBLOCK;
168         fcntl(fd, F_SETFL, flg);
169     2)打開文件時直接設置非阻塞;
170         int fd = open("/dev/tty", O_RDWR | O_NONBLOCK);
171 當讀取一個非阻塞文件,但是此時沒有內容,會出錯,錯誤碼為EAGAIN;
172 5. read() 函數的返回值四種情況;由於非阻塞設置的存在.
173     1.返回值 > 0; 讀到的字節數
174     2.返回值 < 0; 讀到文件末尾
175     3.返回值 == -1;但是errno != EAGAIN  或 EWOULDBLOCK
176         if( ret == -1) {
177         if(errno ==  EAGAIN)
178         {
179             說明文件被設置為非阻塞方式讀取,此時數據沒有到達。
180         }
181         else 
182         {
183             失敗;
184         }
185     }
186     4.返回值 == -1, 但是errno == EAGAIN  或 EWOULDBLOCK;
187         說明此時文件被設置為非阻塞方式讀取,數據還沒有到達。
188 "========================================================================"
189 三. fifo有名管道:解決無血緣關系的進程通信;
190     1.介紹:
191         FIFO文件在磁盤上沒有數據塊,僅僅用來標識內核中的一條管道。各進程可以打開這個文件進行read/write,實際上是在寫內核通道,這樣就實現了進程間的通信。
192     1.創建fifo的方法:
193         1)在終端創建一個有名管道;(不常用)用命令;"mkfifo 管道名";
194         2)在代碼運行時創建一個有名管道;(常用)用函數;int mkfifo(const char*pathname, mode_t mode);成功返回 0,失敗-1195 
196     2.利用fifo實現非血緣關系進程間通信
197         1.進程使用 “同一個fifo” 完成進程間通信。
198         2.一個進程以只讀方式打開read端,一個程序以只寫打開寫段。
199         3.一根管道可以打開多個讀端,多個寫段。(一個寫段多個讀端; 或 一個讀端多個寫段);
200     
201     3.多種特殊情況:
202         當一個寫端多個讀端時: 每個讀端讀取到的數據都不相同,因為從管道中讀數據,讀走之后,管道中的這個數據就不存在了;此時每個讀端讀到的所有內容合在一起為
203                                 管道寫端寫入的數據
204         當多個寫端一個讀端時, 每個寫端寫入的數據都會被這個讀端讀取出來;此時管道讀到的數據為所有寫端寫入的數據。
205                             
206 四。mmap 內存映射:
207 
208         從磁盤映射到kernel區,同時kernel區這段內存(映射占用的)開放了用戶區的權限;所以進程可以進行訪問
209 
210 1.文件是應用於進程間通信
211     1)父子進程之間
212         可以用文件進行通信;
213     2)非血緣關系進程間通信
214         可以用文件進行通信;
215     
216 2.建立映射區,完成進程間通信             
217     1)mmap函數;六個參數的不同意義
218 
219     2)注意事項
220         1.open的時候可以創建一個新文件,但是用此文件創建映射區時,大小不能為0;(可以進行文件拓展后再用來創建映射)
221             mmap使用的時候經常出現總線錯誤,通常是由於共享文件存儲空間大小引起的。
222             
223         2.創建映射區的時候, 隱含着一次對文件的讀操作;所以文件打開的方式只能為"讀寫;或讀"224           當 MAP_SHARED(對內存的修改將反應到磁盤空間)    時,要求 映射區的權限 <= 文件打開的權限。
225           當 MAP_PRIVATE(對內存修改不會反應到磁盤空間)  時,此時則沒有要求。(mmap中的權限是對內存的限制);
226 
227         3.映射區創建完后關閉文件按描述符,對映射區沒有影響。
228         4."最后一個參數,偏移量必須是4K的整數倍。"
229         5.munmap函數傳入的地址一定要是mmap的返回地址。堅決杜絕對mmap函數的返回值進行++操作
230         6.mmap創建映射區出錯的概率非常高,必須進行出錯判斷。
231 
232     3)父子進程間的mmap(用戶空間獨立,內核去各進程共享。)
233         1.先mmap創建映射區
234         2.再fork()共享映射區內存首地址
235         3.指定MAP_SHARED;(必須是 SHARED);
236 
237     4)非血緣關系進程間通信
238         1.使用“同一個文件”創建映射區
239         2.兩個進程,一個以read映射區,一個以write方式打開映射區;
240         3.指定 MAP_SHARED;
241         4)MAP_ANONYMOUS(匿名映射); 不可以用來通信
242         5)/dev/zero(系統的偽文件); 不可以用來通信
243     5)匿名映射
244         linux 創建匿名映射:第四個參數添加 MAP_ANON或MAP__ANONYMOUS;(注意這個匿名宏,只有Linux操作系統可以使用)。
245 
246         UNIX(沒有上述的匿名宏)。使用系統中的特殊文件(/dev/zero;(無限讀,可以用來創建匿名映射))
247 
248 3.數據可以重復讀取;(即多個讀端讀取一個寫端,讀取的內容相同,與fifo不同,取決於讀取的數據)。
249 
250 
251 
252 "====================進程間的通信(IPC)================================================================================================"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM