国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux進(jìn)行時(shí)】進(jìn)程控制

這篇具有很好參考價(jià)值的文章主要介紹了【Linux進(jìn)行時(shí)】進(jìn)程控制。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

1.進(jìn)程創(chuàng)建:

1.1fork函數(shù)

在linux中fork函數(shù)時(shí)非常重要的函數(shù),它從已存在進(jìn)程中創(chuàng)建一個(gè)新進(jìn)程。新進(jìn)程為子進(jìn)程,而原進(jìn)程為父進(jìn)程。

\#include <unistd.h>
pid_t fork(void);
返回值:子進(jìn)程中返回0,父進(jìn)程返回子進(jìn)程id,出錯(cuò)返回-1

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

  • 分配新的內(nèi)存塊和內(nèi)核數(shù)據(jù)結(jié)構(gòu)給子進(jìn)程
  • 將父進(jìn)程部分?jǐn)?shù)據(jù)結(jié)構(gòu)內(nèi)容拷貝至子進(jìn)程
  • 添加子進(jìn)程到系統(tǒng)進(jìn)程列表當(dāng)中
  • fork返回,開始調(diào)度器調(diào)度

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

??當(dāng)準(zhǔn)備返回時(shí),上面三個(gè)工作都有了,父進(jìn)程繼續(xù)執(zhí)行開始 return,子進(jìn)程也可能執(zhí)行 fork 的返回值,然后就會(huì)得到兩次返回。

具體我們可以下面這段代碼:

int main( void )
{
pid_t pid;
printf("Before: pid is %d\n", getpid());
if ( (pid=fork()) == -1 )perror("fork()"),exit(1);
printf("After:pid is %d, fork return %d\n", getpid(), pid);
sleep(1);
return 0;
}

運(yùn)行結(jié)果如下:

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

這里看到了三行輸出,一行before,兩行after。進(jìn)程15256先打印before消息,然后它有打印after。另一個(gè)after

消息有15257打印的。注意到進(jìn)程15257沒有打印before,為什么呢?如下圖所示

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

所以,fork之前父進(jìn)程獨(dú)立執(zhí)行,fork之后,父子兩個(gè)執(zhí)行流分別執(zhí)行。注意,fork之后,誰先執(zhí)行完全由調(diào)度器
決定

fork之后,子進(jìn)程的代碼從fork開始往后執(zhí)行,那OS怎么知道從哪里開始執(zhí)行?

eip程序計(jì)數(shù)器會(huì)出手

創(chuàng)建子進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu):

(struct task_struct + struct mm_struct + 頁表)+ 代碼繼承父進(jìn)程,數(shù)據(jù)以寫時(shí)拷貝的方式來進(jìn)行共享或者獨(dú)立。

fork 之后創(chuàng)建一批結(jié)構(gòu),代碼以共享的方式,數(shù)據(jù)以寫時(shí)拷貝的方式,兩個(gè)進(jìn)程必須保證 “獨(dú)立性”,做到互不影響。在這種共享機(jī)制下子進(jìn)程或父進(jìn)程任何一方掛掉,不會(huì)影響另一個(gè)進(jìn)程。

1.2寫時(shí)拷貝:

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

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

?為什么需要寫時(shí)拷貝呢?

  • 有浪費(fèi)空間之嫌:父進(jìn)程的數(shù)據(jù),子進(jìn)程不一定全用;即便使用,也不一定全部寫入。
  • 最理想的情況,只有會(huì)被父子修改的數(shù)據(jù),進(jìn)行分離拷貝。不需要修改的數(shù)據(jù),共享即可。但是從技術(shù)角度實(shí)現(xiàn)復(fù)雜。
  • 如果 fork 的時(shí)候,就無腦拷貝數(shù)據(jù)給子進(jìn)程,會(huì)增加 fork 的成本(內(nèi)存和時(shí)間)

我們返回去看上圖,修改內(nèi)容前后,代碼是共享同一塊的,但是數(shù)據(jù)是發(fā)生寫時(shí)拷貝的,當(dāng)修改后代碼段會(huì)指向不同的物理內(nèi)存

最終采用寫時(shí)拷貝:只會(huì)拷貝父子修改的、變相的,就是拷貝數(shù)據(jù)的最小成本。拷貝的成本依舊存在。

寫時(shí)拷貝實(shí)際上以一種 延遲拷貝策略,延遲拷貝最大的價(jià)值:只有真正使用的時(shí)候才給你拷。

其最大的意義在于,你想要,但是不立馬使用的空間,先不給你,那么也就意味著可以先給別人。

反正拷貝的成本總是要有,早給你晚給你都是一樣。萬一我現(xiàn)在給你你又不用,那其實(shí)不很浪費(fèi)

所以我選擇暫時(shí)先不給你,等你什么時(shí)候要用什么時(shí)候再給。這就變相的提高了內(nèi)存的使用情況。

舉個(gè)例子

?我們?cè)贑語言的時(shí)候發(fā)現(xiàn)char*類型(字符串)不可被修改

??原因在于在頁表項(xiàng)的位置的時(shí)候設(shè)置只讀屬性

寫時(shí)拷貝:只有當(dāng)子進(jìn)程要進(jìn)行修改的時(shí)候才給子進(jìn)程分配空間,本質(zhì)是一個(gè)資源篩選或者叫做按需申請(qǐng)資源的策略

1.3fork函數(shù)返回值

  • 子進(jìn)程返回0,
  • 父進(jìn)程返回的是子進(jìn)程的pid。

1.4fork常規(guī)用法

一個(gè)父進(jìn)程希望復(fù)制自己,使父子進(jìn)程同時(shí)執(zhí)行不同的代碼段。例如,父進(jìn)程等待客戶端請(qǐng)求,生成子
進(jìn)程來處理請(qǐng)求。
一個(gè)進(jìn)程要執(zhí)行一個(gè)不同的程序。例如子進(jìn)程從fork返回后,調(diào)用exec函數(shù)。

1.5fork調(diào)用失敗的原因

fork函數(shù)創(chuàng)建子進(jìn)程也可能會(huì)失敗,有以下兩種情況:

  1. 系統(tǒng)中有太多的進(jìn)程,內(nèi)存空間不足,子進(jìn)程創(chuàng)建失敗。
  2. 實(shí)際用戶的進(jìn)程數(shù)超過了限制,子進(jìn)程創(chuàng)建失敗。

2.進(jìn)程終止

進(jìn)程退出場(chǎng)景

  • 代碼運(yùn)行完畢,結(jié)果正確
  • 代碼運(yùn)行完畢,結(jié)果不正確
  • 代碼異常終止

崩潰的本質(zhì):進(jìn)程因?yàn)槟承┰?,?dǎo)致進(jìn)程收到了來自操作系統(tǒng)的信號(hào)

return 進(jìn)程的退出碼反映結(jié)果是否正確,可以供用戶進(jìn)行進(jìn)程退出健康狀態(tài)的判斷

int main()
{
   return 0;
}

返回值為 0,表示進(jìn)程代碼跑完,結(jié)果是否正確,我們用 0 表示成功,非 0 表示失敗。

2.1進(jìn)程退出碼

我們都知道m(xù)ain函數(shù)是代碼的入口,但實(shí)際上main函數(shù)只是用戶級(jí)別代碼的入口,main函數(shù)也是被其他函數(shù)調(diào)用的,例如在VS2013當(dāng)中main函數(shù)就是被一個(gè)名為__tmainCRTStartup的函數(shù)所調(diào)用,而__tmainCRTStartup函數(shù)又是通過加載器被操作系統(tǒng)所調(diào)用的,也就是說main函數(shù)是間接性被操作系統(tǒng)所調(diào)用的。

既然main函數(shù)是間接性被操作系統(tǒng)所調(diào)用的,那么當(dāng)main函數(shù)調(diào)用結(jié)束后就應(yīng)該給操作系統(tǒng)返回相應(yīng)的退出信息,而這個(gè)所謂的退出信息就是以退出碼的形式作為main函數(shù)的返回值返回,我們一般以0表示代碼成功執(zhí)行完畢,以非0表示代碼執(zhí)行過程中出現(xiàn)錯(cuò)誤,這就是為什么我們都在main函數(shù)的最后返回0的原因。

??我們可以利用 echo $?來查看最近的進(jìn)程的退出碼,$?只會(huì)保留最近一次執(zhí)行的進(jìn)程的退出碼

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main( void )
{
  return 1;
}

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

我們?cè)俅螆?zhí)行后發(fā)現(xiàn)退出碼變成0了

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

.我們發(fā)現(xiàn)這些錯(cuò)誤碼都是用數(shù)字來表示,我們來查看各種退出碼的含義

C 語言當(dāng)中有個(gè)的 string.h`` 中有一個(gè) strerror 接口,是最經(jīng)典的、將錯(cuò)誤碼表述打印出來的接口,

#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Function not implemented */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale NFS file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* Nomedium found */
#define EMEDIUMTYEP 124 /*Wrongmedium found */
#define ECANCELED 125 /* Operation Canceled */
#define ENOKEY 126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED 129 /* Key was rejected by service */
#define EOWNERDEAD 130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */
#define ERFKILL 132 /* Operation not possible due to RF-kill */
#define EHWPOISON 133 /* Memory page has hardware error */

其中,0 表示 success,1 表示權(quán)限不允許,2 找不到文件或目錄

2.2進(jìn)程終止的方式

exit是C庫的接口,_exit是系統(tǒng)接口,

_exit函數(shù)

#include <unistd.h>
void _exit(int status);
參數(shù):status 定義了進(jìn)程的終止?fàn)顟B(tài),父進(jìn)程通過wait來獲取該值

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

exit函數(shù)

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

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

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

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

驗(yàn)證執(zhí)行_exit之前會(huì)沖刷緩沖區(qū)

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

3.進(jìn)程等待

3.1進(jìn)程等待必要性

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

  • 另外,進(jìn)程一旦變成僵尸狀態(tài),那就刀槍不入,“殺人不眨眼”的kill -9 也無能為力,因?yàn)檎l也沒有辦法

    殺死一個(gè)已經(jīng)死去的進(jìn)程。

  • 最后,父進(jìn)程派給子進(jìn)程的任務(wù)完成的如何,我們需要知道。如,子進(jìn)程運(yùn)行完成,結(jié)果對(duì)還是不對(duì),

    或者是否正常退出。

  • 父進(jìn)程通過進(jìn)程等待的方式,回收子進(jìn)程資源,獲取子進(jìn)程退出信息

3.2進(jìn)程等待的方法

如何獲取status

wait和waitpid,都有一個(gè)status參數(shù),該參數(shù)是一個(gè)輸出型參數(shù),由操作系統(tǒng)填充。
如果傳遞NULL,表示不關(guān)心子進(jìn)程的退出狀態(tài)信息。
否則,操作系統(tǒng)會(huì)根據(jù)該參數(shù),將子進(jìn)程的退出信息反饋給父進(jìn)程。
status不能簡單的當(dāng)作整形來看待,可以當(dāng)作位圖來看待,具體細(xì)節(jié)如下圖(只研究status低16比特
位):

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

在status的低16比特位當(dāng)中,高8位表示進(jìn)程的退出狀態(tài),即退出碼。進(jìn)程若是被信號(hào)所殺,則低7位表示終止信號(hào),而第8位比特位是core dump標(biāo)志。

我們通過一系列位操作,就可以根據(jù)status得到進(jìn)程的退出碼和退出信號(hào)。

exitCode = (status >> 8) & 0xFF; //退出碼
exitSignal = status & 0x7F;      //退出信號(hào)

對(duì)于此,系統(tǒng)當(dāng)中提供了兩個(gè)宏來獲取退出碼和退出信號(hào)。

  • WIFEXITED(status):用于查看進(jìn)程是否是正常退出,本質(zhì)是檢查是否收到信號(hào)。
  • WEXITSTATUS(status):用于獲取進(jìn)程的退出碼。

wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待進(jìn)程pid,失敗返回-1。
參數(shù):
輸出型參數(shù),獲取子進(jìn)程退出狀態(tài),不關(guān)心則可以設(shè)置成為NULL

父進(jìn)程等待子進(jìn)程

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

這里子進(jìn)程會(huì)跑5s,父進(jìn)程一進(jìn)來就是10s休息,也就是未來會(huì)有有5s子進(jìn)程已經(jīng)退了,父進(jìn)程還在,這里子進(jìn)程的狀態(tài)應(yīng)該是Z,然后5s后父進(jìn)程開始回收,在進(jìn)入5s,我們會(huì)看到第二個(gè)現(xiàn)象,子進(jìn)程要消失,z狀態(tài)要退出來了,父進(jìn)程在跑,再過5s父進(jìn)程退出來

waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
當(dāng)正常返回的時(shí)候waitpid返回收集到的子進(jìn)程的進(jìn)程ID;
如果設(shè)置了選項(xiàng)WNOHANG,而調(diào)用中waitpid發(fā)現(xiàn)沒有已退出的子進(jìn)程可收集,則返回0;
如果調(diào)用中出錯(cuò),則返回-1,這時(shí)errno會(huì)被設(shè)置成相應(yīng)的值以指示錯(cuò)誤所在;
參數(shù):
pid:
Pid=-1,等待任一個(gè)子進(jìn)程。與wait等效。
Pid>0.等待其進(jìn)程ID與pid相等的子進(jìn)程。
status:
WIFEXITED(status): 若為正常終止子進(jìn)程返回的狀態(tài),則為真。(查看進(jìn)程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子進(jìn)程退出碼。(查看進(jìn)程的退出碼)
options:(阻塞,后面會(huì)講)
WNOHANG: 若pid指定的子進(jìn)程沒有結(jié)束,則waitpid()函數(shù)返回0,不予以等待。若正常結(jié)束,則返回該子進(jìn)
程的ID

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

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

所以第二個(gè)參數(shù)要獲取兩個(gè)整數(shù),不要當(dāng)做一個(gè)完整的整數(shù),而應(yīng)該看做位圖

我們可以通過kill -l來觀察所有的信號(hào),我們發(fā)現(xiàn),沒有0號(hào)信號(hào)

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

我們來查看一下退出狀態(tài)和退出信號(hào)

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

?請(qǐng)問父進(jìn)程是如何獲取子進(jìn)程的退出信息的呢???通過子進(jìn)程的pcb信息

說明:父進(jìn)程等待子進(jìn)程,子進(jìn)程也會(huì)執(zhí)行自己的代碼。當(dāng)子進(jìn)程執(zhí)行了 return/exit 退出后,子進(jìn)程會(huì)將自己的退出碼信息寫入自己的進(jìn)程控制塊 () 中。子進(jìn)程退出了,代碼可以釋放,子進(jìn)程退出后變成 Z 狀態(tài),其本質(zhì)上就是將自己的 task_struct 維護(hù)起來(代碼可以釋放,但是 task_struct 必須維護(hù))。所謂的 wait/waitpid 的退出信息,實(shí)際上就是從子進(jìn)程的 task_struct 中拿出來的,即 從子進(jìn)程的 task_struct 中拿出子進(jìn)程退出的退出碼。

所以,我們的父進(jìn)程在等待子進(jìn)程死亡,等子進(jìn)程一死,就直接把子進(jìn)程的退出碼信息拷貝過去,通過 wait/waitpid 傳進(jìn)來的參數(shù)后,父進(jìn)程就拿到了子進(jìn)程的退出結(jié)果。即 子進(jìn)程會(huì)將自己的退出信息寫入 task_struct 。

4.進(jìn)程阻塞

?父進(jìn)程在wait的時(shí)候,如果子進(jìn)程沒有退出,父進(jìn)程在干嘛?

??父進(jìn)程只能一直在調(diào)用waitpid進(jìn)行等待——阻塞等待

子進(jìn)程的task_struct里面有一個(gè)parent指針,子進(jìn)程一旦退出,通過這種指針逆向找回去將父進(jìn)程的狀態(tài)從S變R

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

4.1輪詢檢測(cè)

所謂的阻塞,其實(shí)就是掛起。在上層表現(xiàn)來看,就是進(jìn)程卡住了(比如 scanf,cin 等)。

而非阻塞式等待是 “巧等”,會(huì)做些自己的事,而不是一屁股做那傻等!

多次調(diào)用非阻塞接口,這個(gè)過程我們稱之為 輪詢檢測(cè) (Polling)。

我們上一章中講解 waitpid 時(shí),舉的例子都是 阻塞式 的等待。

如果我們想 非阻塞式 的等,我們可以設(shè)置 options 選項(xiàng)為 WNOHANG 。

這樣一來,等待的子進(jìn)程若是沒有結(jié)束,那么waitpid函數(shù)將直接返回0,不予以等待。而等待的子進(jìn)程若是正常結(jié)束,則返回該子進(jìn)程的pid。

4.2基于非阻塞的輪詢等待

例如,父進(jìn)程可以隔一段時(shí)間調(diào)用一次waitpid函數(shù),若是等待的子進(jìn)程尚未退出,則父進(jìn)程可以先去做一些其他事,過一段時(shí)間再調(diào)用waitpid函數(shù)讀取子進(jìn)程的退出信息。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
	pid_t id = fork();
	if (id == 0){
		//child
		int count = 3;
		while (count--){
			printf("child do something...PID:%d, PPID:%d\n", getpid(), getppid());
			sleep(3);
		}
		exit(0);
	}
	//father
	while (1){
		int status = 0;
		pid_t ret = waitpid(id, &status, WNOHANG);
		if (ret > 0){
			printf("wait child success...\n");
			printf("exit code:%d\n", WEXITSTATUS(status));
			break;
		}
		else if (ret == 0){
			printf("father do other things...\n");
			sleep(1);
		}
		else{
			printf("waitpid error...\n");
			break;
		}
	}
	return 0;
}

運(yùn)行結(jié)果就是,父進(jìn)程每隔一段時(shí)間就去查看子進(jìn)程是否退出,若未退出,則父進(jìn)程先去忙自己的事情,過一段時(shí)間再來查看,直到子進(jìn)程退出后讀取子進(jìn)程的退出信息。

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

5.進(jìn)程的程序替換

創(chuàng)建子進(jìn)程的目的是什么?就是為了讓子進(jìn)程幫我執(zhí)行待定的任務(wù)

  1. 讓子進(jìn)程執(zhí)行父進(jìn)程的一部分代碼
  2. 如果子進(jìn)程執(zhí)行一個(gè)全新的程序代碼?可以進(jìn)行進(jìn)程的程序替換

我們?nèi)绾巫屪舆M(jìn)程執(zhí)行一個(gè)新的代碼呢?

之前我們通過寫時(shí)拷貝,讓子進(jìn)程和父進(jìn)程在數(shù)據(jù)上互相解耦,保證獨(dú)立性。如果想讓子進(jìn)程和父進(jìn)程徹底分開,讓子進(jìn)程徹徹底底地執(zhí)行一個(gè)全新的程序,我們就需要 進(jìn)程的程序替換。

若想讓子進(jìn)程執(zhí)行另一個(gè)程序,往往需要調(diào)用一種exec函數(shù)。

當(dāng)進(jìn)程調(diào)用一種exec函數(shù)時(shí),該進(jìn)程的用戶空間代碼和數(shù)據(jù)完全被新程序替換,并從新程序的啟動(dòng)例程開始執(zhí)行。

5.1程序替換原理

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

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

  • 將磁盤中的內(nèi)存,加載入內(nèi)存結(jié)構(gòu)。
  • 重新建立頁表映射,設(shè)執(zhí)行程序替換,就重新建立誰的映射(下圖為子進(jìn)程建立)。
  • 效果:讓父進(jìn)程和子進(jìn)程徹底分離,并讓子進(jìn)程執(zhí)行一個(gè)全新的程序!

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

這個(gè)過程有沒有創(chuàng)建新的進(jìn)程呢?沒有!根本就沒有創(chuàng)建新的進(jìn)程!

因?yàn)樽舆M(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)根本沒變,只是重新建立了虛擬的物理地址之間的映射關(guān)系罷了。

內(nèi)核數(shù)據(jù)結(jié)構(gòu)沒有發(fā)生任何變化! 包括子進(jìn)程的 , 都不變,說明壓根沒有創(chuàng)建新進(jìn)程。exec函數(shù)

5.2替換函數(shù)execl簇

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

#include <unistd.h>`
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execle(const char *path, const char *arg, …,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

見見豬跑execl函數(shù)

int execl(const char* path, const char& arg, ...);
  • 它的第一個(gè)參數(shù)是 path,屬于路徑。
  • 參數(shù) const char* arg, … 中的 … 表示可變參數(shù),命令行怎么寫(ls, -l, -a) 這個(gè)參數(shù)就怎么填。ls, -l, -a 最后必須以 NULL 結(jié)尾,表示 “如何執(zhí)行程序的” 參數(shù)傳遞完畢

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

ls就是一個(gè)可執(zhí)行程序,讓一個(gè)進(jìn)程去執(zhí)行一個(gè)在磁盤的程序

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

站在程序的角度:

它就是被動(dòng)的加載到內(nèi)存,這個(gè)程序被加載了,那以后這些函數(shù)被稱為加載器

既然我自己寫的代碼能加載新的程序,那么操作系統(tǒng)呢?

?當(dāng)創(chuàng)建進(jìn)程的時(shí)候,先有進(jìn)程的數(shù)據(jù)結(jié)構(gòu),還是先加載代碼和數(shù)據(jù)?

??先有數(shù)據(jù)結(jié)構(gòu),再把外部的代碼弄進(jìn)去,把這個(gè)行為叫做加載

?為什么上述測(cè)試代碼我們只看到begin沒有看到end呢?

??執(zhí)行程序替換,新的代碼和數(shù)據(jù)就被加載了,后續(xù)的代碼屬于老代碼,直接被替換了,沒有機(jī)會(huì)執(zhí)行了

程序替換是整體替換,不能局部替換

程序替換只會(huì)影響調(diào)用進(jìn)程,進(jìn)程具有獨(dú)立性!

今天子進(jìn)程加載新程序的時(shí)候,是需要進(jìn)行程序替換,發(fā)生寫時(shí)拷貝(子進(jìn)程執(zhí)行的可是全新的程序,新的代碼!寫時(shí)拷貝在代碼區(qū)也可以發(fā)生?。?/p>

?execl是函數(shù)嗎?函數(shù)調(diào)用可能會(huì)失敗嗎?

??是函數(shù),可能會(huì)失敗,失敗了就不會(huì)調(diào)用新程序運(yùn)行就會(huì)執(zhí)行后面的程序

execl:如果替換成功,不會(huì)有返回值,如果替換失敗,一定有返回值——如果失敗了,必定返回——只要有返回值,就失敗了

因此不用對(duì)該函數(shù)進(jìn)行返回值判斷,只要繼續(xù)向后運(yùn)行一定是失敗的

execv函數(shù)

int execv(const char *path, char *const argv[]);

這里的v代表的是vector,這里傳參要傳一個(gè)數(shù)組

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

execlp函數(shù)

int execlp(const char *file, const char *arg, ...);

帶p的意義:當(dāng)我們執(zhí)行指定程序的時(shí)候,只需要指定程序名即可,系統(tǒng)會(huì)自動(dòng)在環(huán)境變量path中盡顯查找

【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器

?這兩個(gè)ls一樣嗎???不一樣,第一個(gè)參數(shù)是 “供系統(tǒng)去找你是誰的”,后面的一坨代表的是 “你想怎么去執(zhí)行它”

exevp函數(shù)

int execvp(const char *file, char *const argv[]);

這個(gè)其實(shí)就是前面兩個(gè)的結(jié)合

execle函數(shù)

  int execle(const char *path, const char *arg,  ..., char * const envp[]);

envp是自定義的環(huán)境變量

execvpe函數(shù)

int execvpe(const char* file, char* const argv[], char* const envp[]);

事實(shí)上,只有execve才是真正的系統(tǒng)調(diào)用,其它五個(gè)函數(shù)最終都是調(diào)用的execve,所以execve在man手冊(cè)的第2節(jié),而其它五個(gè)函數(shù)在man手冊(cè)的第3節(jié),也就是說其他五個(gè)函數(shù)實(shí)際上是對(duì)系統(tǒng)調(diào)用execve進(jìn)行了封裝,以滿足不同用戶的不同調(diào)用場(chǎng)景的。
char *file, const char *arg, …);
【Linux進(jìn)行時(shí)】進(jìn)程控制,Linux,操作系統(tǒng),linux,運(yùn)維,服務(wù)器文章來源地址http://www.zghlxwxcb.cn/news/detail-713912.html

到了這里,關(guān)于【Linux進(jìn)行時(shí)】進(jìn)程控制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Linux進(jìn)行時(shí)】磁盤文件結(jié)構(gòu)

    【Linux進(jìn)行時(shí)】磁盤文件結(jié)構(gòu)

    上篇文章,我們提及文件是存放在磁盤當(dāng)中,本篇文件我們來了解一下磁盤的結(jié)構(gòu)?。?! ?什么是磁盤? ??磁盤(disk)是指利用磁記錄技術(shù)存儲(chǔ)數(shù)據(jù)的存儲(chǔ)器。 磁盤是計(jì)算機(jī)主要的存儲(chǔ)介質(zhì),可以存儲(chǔ)大量的二進(jìn)制數(shù)據(jù),并且斷電后也能保持?jǐn)?shù)據(jù)不丟失。早期計(jì)算機(jī)使用

    2024年02月05日
    瀏覽(23)
  • Linux 使用 PTP 進(jìn)行時(shí)間同步

    PTP(精確時(shí)間協(xié)議)是一種用于在網(wǎng)絡(luò)中進(jìn)行時(shí)鐘同步的協(xié)議。當(dāng)與硬件支持結(jié)合使用時(shí),PTP 能夠達(dá)到亞微秒的精度,這種精度遠(yuǎn)高于 NTP 協(xié)議。 PTP 時(shí)間同步協(xié)議的支持分為內(nèi)核空間和用戶空間兩部分。在 Linux 系統(tǒng)中,PTP 協(xié)議的實(shí)際實(shí)現(xiàn)稱為 LinuxPTP,它是 PTPv2 根據(jù) Linux 的

    2024年04月11日
    瀏覽(24)
  • 句子時(shí)態(tài)四:完成進(jìn)行態(tài)(現(xiàn)在完成進(jìn)行時(shí)、過去完成進(jìn)行時(shí)、將來完成進(jìn)行時(shí)、過去將來完成進(jìn)行時(shí))
  • MySQL:查詢時(shí)進(jìn)行時(shí)間比較

    在 MySQL 中查數(shù)據(jù)的時(shí)候,往往需要對(duì)記錄的創(chuàng)建時(shí)間進(jìn)行篩選,比如只需要查詢今年1-5月份的,或者查詢距離當(dāng)前時(shí)間多久以前的。 本文介紹了在 MySQL 中查詢記錄時(shí)如何進(jìn)行時(shí)間比較 解決辦法:使用 TIMESTAMPDIFF 函數(shù) 查詢特定時(shí)間范圍的記錄有多種方法,這里介紹幾種 查詢

    2024年02月16日
    瀏覽(19)
  • Arrow:在項(xiàng)目中進(jìn)行時(shí)間處理的強(qiáng)大工具

    Arrow:在項(xiàng)目中進(jìn)行時(shí)間處理的強(qiáng)大工具

    目錄 一、Arrow簡介 二、安裝與配置 三、基礎(chǔ)功能與使用 1. 日期和時(shí)間格式轉(zhuǎn)換 2. 時(shí)區(qū)處理 3. 時(shí)間序列分析 四、進(jìn)階應(yīng)用與案例分析 五、性能與優(yōu)化 六、最佳實(shí)踐與經(jīng)驗(yàn)分享 七、總結(jié)與展望 在處理日期和時(shí)間時(shí),我們經(jīng)常需要一個(gè)精確、可靠的庫來幫助我們。Python的Ar

    2024年02月02日
    瀏覽(21)
  • 【數(shù)據(jù)挖掘】使用 LSTM 進(jìn)行時(shí)間和序列預(yù)測(cè)

    【數(shù)據(jù)挖掘】使用 LSTM 進(jìn)行時(shí)間和序列預(yù)測(cè)

    ????????每天,人類在執(zhí)行諸如過馬路之類的任務(wù)時(shí)都會(huì)做出被動(dòng)預(yù)測(cè),他們估計(jì)汽車的速度和與汽車的距離,或者通過猜測(cè)球的速度并相應(yīng)地定位手來接球。這些技能是通過經(jīng)驗(yàn)和實(shí)踐獲得的。然而,由于涉及眾多變量,預(yù)測(cè)天氣或經(jīng)濟(jì)等復(fù)雜現(xiàn)象可能很困難。在這種情

    2024年02月15日
    瀏覽(28)
  • ESP8266調(diào)用NTP服務(wù)器進(jìn)行時(shí)間校準(zhǔn)

    ESP8266調(diào)用NTP服務(wù)器進(jìn)行時(shí)間校準(zhǔn)

    NTP是網(wǎng)絡(luò)時(shí)間協(xié)議(Network Time Protocol,簡稱NTP),是一種用于同步計(jì)算機(jī)時(shí)間的協(xié)議。NTP服務(wù)器指的是提供NTP服務(wù)的計(jì)算機(jī)或設(shè)備。NTP服務(wù)器的主要功能是保證網(wǎng)絡(luò)上的所有設(shè)備的時(shí)間同步,以確保各個(gè)設(shè)備相互之間的時(shí)間協(xié)調(diào)一致。NTP服務(wù)器通常連接到具有高度精確時(shí)間源

    2024年02月08日
    瀏覽(25)
  • Mybatis-Plus如何進(jìn)行時(shí)間日期的比較

    獲取數(shù)據(jù)庫中跟當(dāng)前日期相等的記錄時(shí),還是調(diào)用eq方法,所以實(shí)體類包括數(shù)據(jù)庫中的類型是Date,而不是DateTime,否則只能獲取相同時(shí)刻的數(shù)據(jù)。 Mybatis-Plus的時(shí)間比較是基于數(shù)據(jù)庫的函數(shù)進(jìn)行的,而不是字符串的比較。在Mybatis-Plus中,可以使用Wrapper對(duì)象的 ge、gt、le、lt 方法

    2024年02月11日
    瀏覽(20)
  • CentOS上如何配置手動(dòng)和定時(shí)任務(wù)自動(dòng)進(jìn)行時(shí)間同步

    CentOS上如何配置手動(dòng)和定時(shí)任務(wù)自動(dòng)進(jìn)行時(shí)間同步

    Linux(Centos)上使用crontab實(shí)現(xiàn)定時(shí)任務(wù)(定時(shí)執(zhí)行腳本): Linux(Centos)上使用crontab實(shí)現(xiàn)定時(shí)任務(wù)(定時(shí)執(zhí)行腳本)_centos 定時(shí)任務(wù)-CSDN博客 Winserver上如何配置和開啟NTP客戶端進(jìn)行時(shí)間同步: Winserver上如何配置和開啟NTP客戶端進(jìn)行時(shí)間同步_配置windows ntp客戶端-CSDN博客 在Centos上如何進(jìn)行

    2024年02月20日
    瀏覽(24)
  • 2.電賽進(jìn)行時(shí)......(AD9833(DDS)模塊的學(xué)習(xí)使用)

    2.電賽進(jìn)行時(shí)......(AD9833(DDS)模塊的學(xué)習(xí)使用)

    如果是玫瑰,它總會(huì)開花的。——歌德 直接數(shù)字合成是生成模擬信號(hào)的一種常用方法,簡單意義上的DDS,主要由相位累加器、相位幅度轉(zhuǎn)換、數(shù)模轉(zhuǎn)換器組成。 因?yàn)锳D9833的輸入時(shí)鐘頻率最高是25MHz,由于奈奎斯特采樣定律的限制,輸出波形的每個(gè)周期內(nèi)至少要2個(gè)點(diǎn)才能還原

    2024年02月10日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包