欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Linux進程控制方式

 更新時間:2025年03月07日 15:22:21   作者:新綠MEHO  
本文詳細介紹了fork函數(shù)、寫時拷貝、進程退出、進程等待以及程序替換等概念,并通過實例代碼和圖解幫助理解這些概念

1.進程創(chuàng)建

fork函數(shù)

#include <unistd.h>
pid_t fork(void);

返回值:自進程中返回 0 ,父進程返回子進程 id ,出錯返回 -1

進程調(diào)用 fork ,當控制轉移到內(nèi)核中的 fork 代碼后,內(nèi)核做:

  • 1.分配新的內(nèi)存塊和內(nèi)核數(shù)據(jù)結構給子進程
  • 2.將父進程部分數(shù)據(jù)結構內(nèi)容拷貝至子進程
  • 3.添加子進程到系統(tǒng)進程列表當中
  • 4.fork返回,開始調(diào)度器調(diào)度

fork之后,誰先執(zhí)行完全由調(diào)度器決定?。?!

fork的常規(guī)用法

1.一個父進程希望復制自己,使父子進程同時執(zhí)行不同的代碼段。例如,父進程等待客戶端請求,生成子進程來處理請求。

2.一個進程要執(zhí)行一個不同的程序。例如子進程從fork返回后,調(diào)用exec函數(shù)。

2.寫時拷貝

通常,父子代碼共享,父子再不寫入時,數(shù)據(jù)也是共享的,當任意一方試圖寫入,便以寫時拷貝的方式各自一份副本。

具體見下圖:

為什么要有寫時拷貝?

1.因為有寫時拷貝技術的存在,父子進程得以徹底分離,保證了進程獨立性。

2.寫時拷貝是一種延遲申請技術,可以提高整機內(nèi)存的使用率。

3.進程終止

進程退出的場景

  • 1.代碼運行完畢,結果正確
  • 2.代碼運行完畢,結果不正確
  • 3.代碼異常終止

進程常見退出方法

1.正常終止(可以通過 echo $? 查看進程退出碼)

  • 從main返回
  • 調(diào)用exit
  • 調(diào)用_exit

2.異常退出

ctrl + c,信號終止

_exit 函數(shù)

#include <unistd.h>
void _exit(int status);

參數(shù): status 定義了進程的終止狀態(tài),父進程通過 wait 來獲取該值

說明:雖然 status 是 int ,但是僅有低 8 位可以被父進程所用。所以 _exit(-1) 時,在終端執(zhí)行 $? 發(fā)現(xiàn)返回值是255 。

exit 函數(shù)

exit 最后也會調(diào)用 _exit , 但在調(diào)用_ exit 之前,還做了其他工作:

  • 1. 執(zhí)行用戶通過 atexit 或 on_exit 定義的清理函數(shù)。
  • 2. 關閉所有打開的流,所有的緩存數(shù)據(jù)均被寫入
  • 3. 調(diào)用 _exit

實例:

int main()
{
printf("hello");
exit(0);
}
運行結果 :
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
------------------------------------------------------------------------
int main()
{
printf("hello");
_exit(0);
}
運行結果 :
[root@localhost linux]# ./a.out
[root@localhost linux]#

通過上邊的例子,我們看到當printf中的字符串后邊沒有加 \n 時,調(diào)用exit函數(shù)會打印出字符串內(nèi)容,但是調(diào)用 _exit 函數(shù)并不會打印任何內(nèi)容,原因就是因為 exit 底層調(diào)用的是 _exit,exit在調(diào)用_exit之前,會刷新緩沖區(qū),表現(xiàn)為原來緩沖區(qū)中的字符串被打印出來,但是 _exit并不會刷新緩沖區(qū)。

那么這個所謂的“緩沖區(qū)”在哪里呢?誰來維護的?

一定不在操作系統(tǒng)內(nèi)部?。∪绻遣僮飨到y(tǒng)維護的,緩沖區(qū)內(nèi)的內(nèi)容也能被 _exit 刷新來。

C標準庫給我們維護的?。?!

退出碼

int main()
{
	int i;
	for(i=0;i<150;i++)
	{
		printf("%d: %s\n",i,strerror(i));
	}

	return 0;
}

通過上圖,可以看出,一共有134個退出碼。

4.進程等待

進程等待的必要性

1.之前講過,子進程退出,父進程如果不管不顧,就可能造成‘僵尸進程’的問題,進而造成內(nèi)存泄漏。

2.另外,進程一旦變成僵尸狀態(tài),那就刀槍不入,“殺.人不眨眼”的kill -9 也無能為力,因為誰也沒有辦法殺死一個已經(jīng)死去的進程。

3.最后,父進程派給子進程的任務完成的如何,我們需要知道。如,子進程運行完成,結果對還是不對,或者是否正常退出。

4.父進程通過進程等待的方式,回收子進程資源,獲取子進程退出信息

進程等待的方法

wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);

返回值:

  • 成功返回被等待進程pid,失敗返回-1。

參數(shù):

  • 輸出型參數(shù),獲取子進程退出狀態(tài),不關心則可以設置成為NULL

waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);

返回值:

  • 當正常返回的時候waitpid返回收集到的子進程的進程ID;
  • 如果設置了選項WNOHANG,而調(diào)用中waitpid發(fā)現(xiàn)沒有已退出的子進程可收集,則返回0;
  • 如果調(diào)用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;

參數(shù):

pid

  • Pid=-1,等待任一個子進程。與wait等效。
  • Pid>0.等待其進程ID與pid相等的子進程。

status:

  • WIFEXITED(status): 若為正常終止子進程返回的狀態(tài),則為真。(查看進程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子進程退出碼。(查看進程的退出碼)

options:

  • WNOHANG: 若pid指定的子進程沒有結束,則waitpid()函數(shù)返回0,不予以等待。若正常結束,則返回該子進程的ID。

注意:

  • 1.如果子進程已經(jīng)退出,調(diào)用wait/waitpid時,wait/waitpid會立即返回,并且釋放資源,獲得子進程退出信息。
  • 2.如果在任意時刻調(diào)用wait/waitpid,子進程存在且正常運行,則進程可能阻塞。
  • 3.如果不存在該子進程,則立即出錯返回。

說明:

  • 1.wait和waitpid是系統(tǒng)調(diào)用?。?!
  • 2. 父進程等待子進程,當子進程執(zhí)行 return / exit / _exit / 因為某種原因異常崩潰退出 后,子進程會將自己的退出碼信息寫入自己的進程控制塊(task_struct)中,此時,退出后的子進程處于Z狀態(tài),此時代碼可以釋放,但是task_struct必須維護,直到父進程讀取信息完畢后,子進程的task_struct才能釋放。
  • 3.父進程通過 wait / waitpid 傳遞的參數(shù),就可以拿到子進程的退出結果(退出碼等信息)。

驗證上邊說到的 “子進程會將自己的退出碼信息寫入到自己的進程控制塊中” :

獲取子進程status

1.wait和waitpid,都有一個status參數(shù),該參數(shù)是一個輸出型參數(shù),由操作系統(tǒng)填充。

2.如果傳遞NULL,表示不關心子進程的退出狀態(tài)信息。

3.否則,操作系統(tǒng)會根據(jù)該參數(shù),將子進程的退出信息反饋給父進程。

4.status不能簡單的當作整形來看待,可以當作位圖來看待,具體細節(jié)如下圖(只研究status低16比特位):

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
	pid_t id=fork();
	if(id==0)
	{
		int cnt=5;
		while(cnt--)
		{
			printf("我是子進程,pid: %d,ppid(): %d\n",getpid(),getppid());
		}
		exit(100);
	}
	else if(id>0)
	{
			printf("我是父進程,pid: %d,ppid: %d\n",getpid(),getppid());
		int status=0;
		pid_t result = waitpid(-1,&status,0);
		if(result>0)
		{
            //printf("等待子進程成功,status: %d\n",status>>8);
			printf("等待子進程成功,status: %d\n",status>>8 & 0xFFFF);
		}
	}
	else
	{
		//fork error
	}
	return 0;
}

注意:獲取status時,一定要先右移8位再按位與0xFFFF,如果不這么做的話,結果為:

顯然與100不符,結果錯誤。(這就是為什么要將status右移8位再按位與0xFFFF)

將status右移8位再按位與0xFFFF后,結果為:

與100符合,結果正確。

當然,還有別的方法,將 status>>8 & 0xFFFF 改為 WEXITSTATUS(status)。

5.進程程序替換

替換原理

用 fork 創(chuàng)建子進程后執(zhí)行的是和父進程相同的程序 ( 但有可能執(zhí)行不同的代碼分支 ), 子進程往往要調(diào)用一種 exec 函數(shù)以執(zhí)行另一個程序。當進程調(diào)用一種exec 函數(shù)時 , 該進程的用戶空間代碼和數(shù)據(jù)完全被新程序替換 , 從新程序的啟動例程開始執(zhí)行。調(diào)用exec 并不創(chuàng)建新進程 , 所以調(diào)用 exec 前后該進程的 id 并未改變。

程序替換,上邊紫色方框這一部分沒有發(fā)生改變,改變的只是,把要替換的進程加載到物理內(nèi)存,頁表重新建立了映射關系。

替換函數(shù)

其實有六種以 exec 開頭的函數(shù) , 統(tǒng)稱 exec 函數(shù):

execl,execlp,execle,execv,execvp,execvpe這六個函數(shù)是系統(tǒng)提供的基本封裝,底層調(diào)用的都是execve這個系統(tǒng)調(diào)用接口。

函數(shù)返回值問題

  • 這些函數(shù)如果調(diào)用成功則加載新的程序從啟動代碼開始執(zhí)行 , 不再返回。
  • 如果調(diào)用出錯則返回 -1。
  • 假設調(diào)用成功有返回值,但是這個返回值也是返回給原來調(diào)用這個exec*系列函數(shù)的進程,但是原來的這個進程已經(jīng)被程序替換掉了,所以也沒辦法返回,方法行不通。
  • 所以 exec 函數(shù)只有出錯的返回值而沒有成功的返回值。

命名理解

這些函數(shù)原型看起來很容易混 , 但只要掌握了規(guī)律就很好記。

  • l(list) : 表示參數(shù)采用列表
  • v(vector) : 參數(shù)用數(shù)組
  • p(path) : 有p自動搜索環(huán)境變量PATH
  • e(env) : 表示自己維護環(huán)境變量

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 使用Hyper-v虛擬機安裝Centos7

    使用Hyper-v虛擬機安裝Centos7

    本文詳細講解了使用Hyper-v虛擬機安裝Centos7的方法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-12-12
  • Linux報錯cannot?open?shared?object?file問題及解決

    Linux報錯cannot?open?shared?object?file問題及解決

    這篇文章主要介紹了Linux報錯cannot?open?shared?object?file問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • linux服務器上安裝jdk的兩種方法(yum+下載包)

    linux服務器上安裝jdk的兩種方法(yum+下載包)

    這篇文章主要給大家介紹了關于在linux服務器上安裝jdk的兩種方法,分別是利用yum安裝和從官網(wǎng)下載包安裝,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧
    2018-05-05
  • 深入理解Linux負載均衡LVS

    深入理解Linux負載均衡LVS

    LVS是Linux Virtual Server 的簡稱,也就是linux虛擬服務器。LVS 是一個實現(xiàn)負載均衡集群的開源軟件項目,LVS架構從邏輯上可分為調(diào)度層、Server集群層和共享存儲
    2021-06-06
  • Linux進程間通信--使用信號

    Linux進程間通信--使用信號

    本篇文章主要介紹了Linux進程間通信--使用信號的相關知識。具有很好的參考價值。下面跟著小編一起來看下吧
    2017-04-04
  • Centos7服務器下啟動jar包項目的最佳方法

    Centos7服務器下啟動jar包項目的最佳方法

    這篇文章主要給大家分享介紹了關于Centos7服務器下啟動jar包項目的最佳方法,文中通過示例代碼以及圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-03-03
  • Win10安裝Linux系統(tǒng)的教程圖解

    Win10安裝Linux系統(tǒng)的教程圖解

    這篇文章主要介紹了Win10安裝Linux系統(tǒng)的教程,本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • Linux如何啟動SELinux

    Linux如何啟動SELinux

    這篇文章主要介紹了Linux如何啟動SELinux問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • ubuntu端向日葵鍵盤輸入卡頓問題及解決

    ubuntu端向日葵鍵盤輸入卡頓問題及解決

    這篇文章主要介紹了ubuntu端向日葵鍵盤輸入卡頓問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • linux Vim基本操作方法

    linux Vim基本操作方法

    Ubuntu的Vi也不好用,搞了個Vim用來學習了,簡單記錄下它的基本操作。還有一本相關的電子書可下載。
    2013-11-11

最新評論