網(wǎng)站如何做seo優(yōu)化教程標(biāo)題優(yōu)化
文章目錄
- 進(jìn)程創(chuàng)建
- 初識fork函數(shù)
- fork函數(shù)返回值
- fork常規(guī)用法
- fork調(diào)用失敗的原因
- 寫時拷貝
- 進(jìn)程終止
- 進(jìn)程終止是在做什么?
- 進(jìn)程終止的情況
- 代碼跑完,結(jié)果正確/不正確
- 代碼異常終止
- 如何終止
- 進(jìn)程等待
- 概述
- 進(jìn)程等待方法
- wait方法
- waitpid

進(jìn)程創(chuàng)建
初識fork函數(shù)
在linux中fork函數(shù)時非常重要的函數(shù),它從已存在進(jìn)程中創(chuàng)建一個新進(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,出錯返回-1。
進(jìn)程=內(nèi)核相關(guān)管理數(shù)據(jù)結(jié)構(gòu)(task_struct、mm_struct、頁表)+代碼和數(shù)據(jù)
對于每一個進(jìn)程都需要:
- 分配新的內(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)度
如何理解進(jìn)程具有獨(dú)立性??子進(jìn)程中也有相關(guān)管理數(shù)據(jù)結(jié)構(gòu)也有自己的代碼和數(shù)據(jù),代碼和數(shù)據(jù)雖然和父進(jìn)程共享,但是和父進(jìn)程相互不影響,數(shù)據(jù)的部分是以寫時拷貝時私有,不寫時拷貝相當(dāng)于共享。
當(dāng)一個進(jìn)程調(diào)用fork之后,就有兩個二進(jìn)制代碼相同的進(jìn)程。而且它們都運(yùn)行到相同的地方。但每個進(jìn)程都將可以開始它們自己的旅程。
fork函數(shù)返回值
- 子進(jìn)程返回0
- 父進(jìn)程返回的是子進(jìn)程的pid
如何做到有兩個返回值?
探索父進(jìn)程和子進(jìn)程 文章中有詳細(xì)解釋。
為什么給父進(jìn)程返回的是子進(jìn)程的pid,給子進(jìn)程返回0?
父進(jìn)程必須知道子進(jìn)程的pid,方便后續(xù)對子進(jìn)程進(jìn)行標(biāo)識,進(jìn)而進(jìn)行管理;子進(jìn)程需要通過返回0,來看是否創(chuàng)建成功。
fork常規(guī)用法
- 一個父進(jìn)程希望復(fù)制自己,使父子進(jìn)程同時執(zhí)行不同的代碼段。例如,父進(jìn)程等待客戶端請求,生成子進(jìn)程來處理請求。
- 一個進(jìn)程要執(zhí)行一個不同的程序。例如子進(jìn)程從fork返回后,調(diào)用exec函數(shù)
fork調(diào)用失敗的原因
- 系統(tǒng)中有太多的進(jìn)程
- 實際用戶的進(jìn)程數(shù)超過了限制
寫時拷貝
通常,父子代碼共享,父子再不寫入時,數(shù)據(jù)也是共享的,當(dāng)任意一方試圖寫入,便以寫時拷貝的方式各自一份副本。
進(jìn)程終止
進(jìn)程終止是在做什么?
在進(jìn)程創(chuàng)建的時候,是先有內(nèi)核數(shù)據(jù)結(jié)構(gòu),再有的代碼和數(shù)據(jù)。
終止一個進(jìn)程的本質(zhì)是在釋放曾經(jīng)的代碼和數(shù)據(jù)所占據(jù)的空間,釋放內(nèi)核數(shù)據(jù)結(jié)構(gòu)。
在釋放內(nèi)核數(shù)據(jù)結(jié)構(gòu)時,PCB會延遲釋放。
進(jìn)程終止的情況
代碼跑完,結(jié)果正確/不正確
main函數(shù)的返回值是100,通過echo $?
查詢。在系統(tǒng)中有一個變量叫做?
,查看這個變量使用$?
,訪問變量內(nèi)容都可以使用echo
。echo
是內(nèi)建命令,打印的都是bash內(nèi)部的變量數(shù)據(jù)。
$?
表示父進(jìn)程bash獲取到的最近一個子進(jìn)程退出的退出碼。退出碼為0表示成功,非0表示失敗,不同的非0值一方面表示失敗,另一方面表示失敗原因,每個數(shù)字的錯誤描述都是由操作系統(tǒng)規(guī)定,對應(yīng)的錯誤描述都是一個字符串。因此平時在寫代碼時,main函數(shù)內(nèi)部都是return 0
,我們在編寫C/C++代碼都是默認(rèn)成功的。
#include<stdio.h>
#include<unistd.h>
#include<string.h> int main()
{ for(int errcode=0;errcode<=255;errcode++) { printf("%d:%s\n",errcode,strerror(errcode)); } printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid()); sleep(2); return 100;
}
運(yùn)行結(jié)果:
[gwj@iZf8zhv7mi2thjdxsptkb8Z lesson16]$ make
gcc -o myprocess myprocess.c -std=c99
[gwj@iZf8zhv7mi2thjdxsptkb8Z lesson16]$ ./myprocess
0:Success
1:Operation not permitted
2:No such file or directory
3:No such process
4:Interrupted system call
5:Input/output error
6:No such device or address
7:Argument list too long
8:Exec format error
9:Bad file descriptor
10:No child processes
11:Resource temporarily unavailable
12:Cannot allocate memory
13:Permission denied
14:Bad address
15:Block device required
16:Device or resource busy
17:File exists
18:Invalid cross-device link
19:No such device
20:Not a directory
21:Is a directory
22:Invalid argument
23:Too many open files in system
24:Too many open files
25:Inappropriate ioctl for device
26:Text file busy
27:File too large
28:No space left on device
29:Illegal seek
30:Read-only file system
31:Too many links
32:Broken pipe
33:Numerical argument out of domain
34:Numerical result out of range
35:Resource deadlock avoided
36:File name too long
37:No locks available
38:Function not implemented
39:Directory not empty
40:Too many levels of symbolic links
41:Unknown error 41
42:No message of desired type
43:Identifier removed
44:Channel number out of range
45:Level 2 not synchronized
46:Level 3 halted
47:Level 3 reset
48:Link number out of range
49:Protocol driver not attached
50:No CSI structure available
51:Level 2 halted
52:Invalid exchange
53:Invalid request descriptor
54:Exchange full
55:No anode
56:Invalid request code
57:Invalid slot
58:Unknown error 58
59:Bad font file format
60:Device not a stream
61:No data available
62:Timer expired
63:Out of streams resources
64:Machine is not on the network
65:Package not installed
66:Object is remote
67:Link has been severed
68:Advertise error
69:Srmount error
70:Communication error on send
71:Protocol error
72:Multihop attempted
73:RFS specific error
74:Bad message
75:Value too large for defined data type
76:Name not unique on network
77:File descriptor in bad state
78:Remote address changed
79:Can not access a needed shared library
80:Accessing a corrupted shared library
81:.lib section in a.out corrupted
82:Attempting to link in too many shared libraries
83:Cannot exec a shared library directly
84:Invalid or incomplete multibyte or wide character
85:Interrupted system call should be restarted
86:Streams pipe error
87:Too many users
88:Socket operation on non-socket
89:Destination address required
90:Message too long
91:Protocol wrong type for socket
92:Protocol not available
93:Protocol not supported
94:Socket type not supported
95:Operation not supported
96:Protocol family not supported
97:Address family not supported by protocol
98:Address already in use
99:Cannot assign requested address
100:Network is down
101:Network is unreachable
102:Network dropped connection on reset
103:Software caused connection abort
104:Connection reset by peer
105:No buffer space available
106:Transport endpoint is already connected
107:Transport endpoint is not connected
108:Cannot send after transport endpoint shutdown
109:Too many references: cannot splice
110:Connection timed out
111:Connection refused
112:Host is down
113:No route to host
114:Operation already in progress
115:Operation now in progress
116:Stale file handle
117:Structure needs cleaning
118:Not a XENIX named type file
119:No XENIX semaphores available
120:Is a named type file
121:Remote I/O error
122:Disk quota exceeded
123:No medium found
124:Wrong medium type
125:Operation canceled
126:Required key not available
127:Key has expired
128:Key has been revoked
129:Key was rejected by service
130:Owner died
131:State not recoverable
132:Operation not possible due to RF-kill
133:Memory page has hardware error
134:Unknown error 134
135:Unknown error 135
136:Unknown error 136
137:Unknown error 137
138:Unknown error 138
139:Unknown error 139
140:Unknown error 140
141:Unknown error 141
142:Unknown error 142
143:Unknown error 143
144:Unknown error 144
145:Unknown error 145
146:Unknown error 146
147:Unknown error 147
148:Unknown error 148
149:Unknown error 149
150:Unknown error 150
151:Unknown error 151
152:Unknown error 152
153:Unknown error 153
154:Unknown error 154
155:Unknown error 155
156:Unknown error 156
157:Unknown error 157
158:Unknown error 158
159:Unknown error 159
160:Unknown error 160
161:Unknown error 161
162:Unknown error 162
163:Unknown error 163
164:Unknown error 164
165:Unknown error 165
166:Unknown error 166
167:Unknown error 167
168:Unknown error 168
169:Unknown error 169
170:Unknown error 170
171:Unknown error 171
172:Unknown error 172
173:Unknown error 173
174:Unknown error 174
175:Unknown error 175
176:Unknown error 176
177:Unknown error 177
178:Unknown error 178
179:Unknown error 179
180:Unknown error 180
181:Unknown error 181
182:Unknown error 182
183:Unknown error 183
184:Unknown error 184
185:Unknown error 185
186:Unknown error 186
187:Unknown error 187
188:Unknown error 188
189:Unknown error 189
190:Unknown error 190
191:Unknown error 191
192:Unknown error 192
193:Unknown error 193
194:Unknown error 194
195:Unknown error 195
196:Unknown error 196
197:Unknown error 197
198:Unknown error 198
199:Unknown error 199
200:Unknown error 200
201:Unknown error 201
202:Unknown error 202
203:Unknown error 203
204:Unknown error 204
205:Unknown error 205
206:Unknown error 206
207:Unknown error 207
208:Unknown error 208
209:Unknown error 209
210:Unknown error 210
211:Unknown error 211
212:Unknown error 212
213:Unknown error 213
214:Unknown error 214
215:Unknown error 215
216:Unknown error 216
217:Unknown error 217
218:Unknown error 218
219:Unknown error 219
220:Unknown error 220
221:Unknown error 221
222:Unknown error 222
223:Unknown error 223
224:Unknown error 224
225:Unknown error 225
226:Unknown error 226
227:Unknown error 227
228:Unknown error 228
229:Unknown error 229
230:Unknown error 230
231:Unknown error 231
232:Unknown error 232
233:Unknown error 233
234:Unknown error 234
235:Unknown error 235
236:Unknown error 236
237:Unknown error 237
238:Unknown error 238
239:Unknown error 239
240:Unknown error 240
241:Unknown error 241
242:Unknown error 242
243:Unknown error 243
244:Unknown error 244
245:Unknown error 245
246:Unknown error 246
247:Unknown error 247
248:Unknown error 248
249:Unknown error 249
250:Unknown error 250
251:Unknown error 251
252:Unknown error 252
253:Unknown error 253
254:Unknown error 254
255:Unknown error 255
I am process,pid:32312,ppid:31371
對應(yīng)的錯誤碼都表示一種錯誤。
父進(jìn)程為什么知道子進(jìn)程退出碼?父進(jìn)程要知道子進(jìn)程的退出情況(失敗了還是成功了,失敗的原因是什么),bash會反饋給用戶。
舉個例子:
進(jìn)程的退出碼存在的意義是告訴關(guān)心方(父進(jìn)程),我把任務(wù)執(zhí)行的怎么樣了。既然把子進(jìn)程創(chuàng)建出來,就要讓父進(jìn)程得到信息。
不是說echo $?
保存的是最近一個子進(jìn)程退出的退出碼嗎?那上圖怎么解釋?方框中第一個echo $?
執(zhí)行的命令是查看process
的退出碼,第二個echo $?
查看的是第一個echo $?
的退出碼,雖然echo
是一個內(nèi)建命令,但是也是當(dāng)做進(jìn)程來看待。
進(jìn)程的退出碼可以使用系統(tǒng)官方的定義,你也可以自定義一個退出碼。
代碼異常終止
代碼執(zhí)行時,出現(xiàn)了異常,提前退出,一旦進(jìn)程出現(xiàn)異常,退出碼有沒有意義了
vs編寫程序運(yùn)行時,程序崩潰了,本質(zhì)是操作系統(tǒng)發(fā)現(xiàn)你的程序做了不該做的事情,操作系統(tǒng)殺掉了你的進(jìn)程。
為什么進(jìn)程會出現(xiàn)異常?
本質(zhì)上是因為進(jìn)程收到了操作系統(tǒng)發(fā)出的信號。
段錯誤,操作系統(tǒng)提前終止進(jìn)程。
盡管書寫的代碼進(jìn)程沒有錯誤,但是接收到了信號,就會有段錯誤。
進(jìn)程退出時,我們可以看進(jìn)程退出信號是多少來判斷進(jìn)程為什么異常。
進(jìn)程退出的三種情況:
代碼運(yùn)行完畢,結(jié)果正確
代碼運(yùn)行完畢,結(jié)果不正確
代碼異常終止
因此,衡量一個進(jìn)程退出,我們只需要看兩個數(shù)字:退出碼、退出信號
如何終止
main
函數(shù)中直接return
,表示進(jìn)程終止(非main
函數(shù),return
函數(shù)結(jié)束)- 代碼調(diào)用
exit()
,注意:在代碼任意位置調(diào)用都表示進(jìn)程終止。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{ while(1) { printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid());sleep(2); exit(123); } return 100;
}
#include <unistd.h>
void exit(int status);
exit最后也會調(diào)用exit, 但在調(diào)用exit之前,還做了其他工作:
- 執(zhí)行用戶通過 atexit或on_exit定義的清理函數(shù)。
- 關(guān)閉所有打開的流,所有的緩存數(shù)據(jù)均被寫入
- 調(diào)用_exit
- 調(diào)用
_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)時,在終端執(zhí)行$?
發(fā)現(xiàn)返回值是255。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{ while(1) { printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid());sleep(2); _exit(-1); } return 100;
}
exit
和_exit
區(qū)別:
exit
會在程序退出時,沖刷緩沖區(qū),_exit
不會。
進(jìn)程等待
概述
什么是進(jìn)程等待?
任何子進(jìn)程,在退出的情況下,一般必須要被父進(jìn)程等待。 進(jìn)程在退出的時候,如果父進(jìn)程不管不顧,退出進(jìn)程,處于僵尸狀態(tài)(Z),存在內(nèi)存泄漏。
為什么?
- 父進(jìn)程通過等待,解決子進(jìn)程退出的僵尸問題,回收系統(tǒng)資源(一定要考慮的)
- 父進(jìn)程獲取子進(jìn)程的退出信息,知道子進(jìn)程退出原因(可選的功能)
進(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。
代碼勢力:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt=5;while(cnt){printf("I am child process,pid:%d,ppid:%d,cnt=%d\n",getpid(),getppid(),cnt);sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());pid_t id=fork();if(id==0) {//childChildRun();printf("child process quit...\n");exit(0);}sleep(10);//fatherpid_t rid=wait(NULL);if(rid>0){printf("wait process,rid:%d\n",rid);}sleep(3);printf("father process quit...\n");return 0;
}
運(yùn)行結(jié)果:
在上述代碼中,先進(jìn)入父進(jìn)程,然后子進(jìn)程運(yùn)行五次后子進(jìn)程退出,然后休眠10秒,處于僵尸狀態(tài),緊接著進(jìn)程等待,然后父進(jìn)程退出,程序運(yùn)行結(jié)束。由此可以看出,等待會解決進(jìn)程的僵尸問題。
將上述代碼sleep(10)
代碼注釋掉,子進(jìn)程運(yùn)行5秒后直接退出,立馬執(zhí)行父進(jìn)程等待。如果子進(jìn)程沒有退,其實父進(jìn)程一直在阻塞等待。子進(jìn)程本身是軟件,父進(jìn)程本質(zhì)是在等待某種軟件就緒。
進(jìn)程的等待本質(zhì)是將進(jìn)程的PCB列入等待隊列。那么如何理解父進(jìn)程阻塞等待子進(jìn)程?父進(jìn)程不被調(diào)度,在執(zhí)行wait
發(fā)現(xiàn)子進(jìn)程還沒有退出,父進(jìn)程就不要調(diào)度,實際上就是將父進(jìn)程PCB列入等待隊列,處于S
狀態(tài)(非運(yùn)行狀態(tài)),等到子進(jìn)程退出,喚醒父進(jìn)程。
waitpid
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *status);pid_t waitpid(pid_t pid, int *status, int options);int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
- 返回值:
當(dāng)正常返回的時候waitpid返回收集到的子進(jìn)程的進(jìn)程ID;
如果設(shè)置了選項WNOHANG,而調(diào)用中waitpid發(fā)現(xiàn)沒有已退出的子進(jìn)程可收集,則返回0;
如果調(diào)用中出錯,則返回-1,這時errno會被設(shè)置成相應(yīng)的值以指示錯誤所在;
-
參數(shù):
-
[ ]
-
pid:
-
Pid=-1,等待任一個子進(jìn)程。與wait等效。
pid_t rid=waitpid(-1,NULL,0);
等待任何一個子進(jìn)程退出,哪一個進(jìn)程退了,就對應(yīng)返回哪一個進(jìn)程的pid。等同于pid_t rid=wait(NULL);
-
Pid>0.等待其進(jìn)程ID與pid相等的子進(jìn)程。
-
pid_t rid=waitpid(id,NULL,0);
- status:典型輸出型參數(shù)
WIFEXITED(status)
: 若為正常終止子進(jìn)程返回的狀態(tài),則為真。(查看進(jìn)程是否是正常退出)WEXITSTATUS(status)
: 若WIFEXITED非零,提取子進(jìn)程退出碼。(查看進(jìn)程的退出碼)
以位圖的形式返回
退出碼范圍:0~255
信號終止:128個
代碼:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt=5;while(cnt){printf("I am child process,pid:%d,ppid:%d,cnt=%d\n",getpid(),getppid(),cnt);sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());pid_t id=fork();if(id==0) {//childChildRun();printf("child process quit...\n");exit(1);}sleep(7);//father//pid_t rid=wait(NULL);int status=0;pid_t rid=waitpid(id,&status,0);if(rid>0){printf("wait process,rid:%d\n",rid);}sleep(3);printf("father process quit,status:%d,child quit code:%d,child quit signal:%d\n",status,(status>>8)&0xFF,status & 0x7F);return 0;
}
運(yùn)行結(jié)果:
宏定義方式等待:
等待是必須的,但獲取子進(jìn)程的退出信息不是必須的。
如果子進(jìn)程沒有退出,而父進(jìn)程在執(zhí)行waitpid
進(jìn)行等待,阻塞等待,這本質(zhì)上是進(jìn)程阻塞,waitpid
在等待某種條件發(fā)生(子進(jìn)程退出),在等待期間,父進(jìn)程什么也沒干。
接下來就介紹非阻塞等待!!!
- options:
WNOHANG
: 若pid指定的子進(jìn)程沒有結(jié)束,則waitpid()函數(shù)返回0,不予以等待。若正常結(jié)束,則返回該子進(jìn)程的ID。
pid_t>0
:等待成功,子進(jìn)程退出,并且父進(jìn)程回收成功
pid_t<0
:等待失敗
pid_t==0
:檢測成功,但是子進(jìn)程還沒有退出,需要進(jìn)行下一次重復(fù)等待。
非阻塞等待的時候+循環(huán)=非阻塞輪詢
好處:允許父進(jìn)程做一些其他的事情
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt=5;while(cnt){printf("I am child process,pid:%d,ppid:%d,cnt=%d\n",getpid(),getppid(),cnt);sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());pid_t id=fork();
if(id==0){//childChildRun();printf("child process quit...\n");exit(1);}//fatherwhile(1) {int status=0;pid_t rid=waitpid(id,&status,WNOHANG);if(rid==0){sleep(1);printf("child is running,father check next time!\n");}else if(id>0){if(WIFEXITED(status)){printf("child quit success,child exit code:%d\n",WEXITSTATUS(status));}else {printf("child quit unnormal!\n");}break;}else {printf("waitpid failed!\n");break;}}
}