目录
一,进程创建
写时拷贝
二,进程终止
三,进程等待
获取子进程status
一,进程创建
- 命令行启动命令(程序、指令等);
- 通过程序自身fork创建;
#include<unistd.h>
//子进程返回0,父进程返回子进程的ID,出错返回-1
pid_t fork(void);
进程调用fork,当控制转移到内核中的fork代码后,内核将会:
- 分配新的内存块和内核数据结构给子进程;
- 拷贝父进程部分数据结构内容给子进程;
- 添加子进程到系统进程列表中;
- fork返回,开始调度器调度;
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
printf("Before: pid is %d\n", getpid());
pid = fork();
if(pid == -1)
{
perror("fork");
exit(-1);
}
printf("After: pid is %d, fork return %d\n", getpid(), pid);
sleep(1);
return 0;
}
[wz@192 Desktop]$ ./target
Before: pid is 21739
After: pid is 21739, fork return 21740
After: pid is 21740, fork return 0
- fork前,父进程独立执行;
- fork后,父子进程分流执行;父子谁先执行完全由调度器决定;
写时拷贝
通常父子代码共享,数据也共享(父子在不写入时);当有任意一方写入时,便以写实拷贝的方式各自一份副本,从而保证父子进程的独立性;
二,进程终止
- 代码运行结束,结果正确,退出码为0;
- 代码运行结束,结果不正确,退出码非0;
- 代码异常终止;
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int i=0;
for(i;i<200;i++)
{
printf("%d:%s\n",i,strerror(i));
}
return 0;
}
[wz@192 Desktop]$ ./target
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
进程常见退出方法:
- 正常终止,可通过 echo $? 查看最近一次执行程序退出码;
- main返回,return 0代表进程退出,0退出码表示成功;给系统查看,以确认进程是否正确;非main函数的return不受终止进程,是结束函数;
- exit,任意位置调用此函数,都会直接终止进程;
- _exit,与exit类型,但此函数不会刷新缓冲区等处理工作,直接终止进程;
- 异常退出;
- ctrl + C,信号终止;
退出码可自定义,也可使用系统错误码;
#include <unistd.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
//status 定义了进程终止状态,父进程通过wait来获取该值;
//虽然status为int,但仅低8位可被父进程使用,_exit(-1)执行echo $?结果为255;
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello");
exit(0);
return 0;
}
[wz@192 Desktop]$ ./target
hello[wz@192 Desktop]$
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello");
_exit(0);
return 0;
}
[wz@192 Desktop]$ ./target
[wz@192 Desktop]$
return退出
一种常见的退出进程方法,执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当作exit的参数;
进程终止的核心思想,就是归还资源;
- 释放曾经为管理进程所维护的所有数据结构对象;
- 不是真的把数据结构对象销毁,而是设置为不用状态,保存起来,“数据结构池”;
- 释放程序代码和数据所占的内存空间;
- 不是将代码和数据清空,把内存设置为无效即可;
- 取消曾经该进程的链接关系;
三,进程等待
进程等待的必要性
- 子进程退出,父进程如不管不顾,就可能造成僵死进程,进而造成内存泄露;
- 进程进入僵死状态,将无能为力,即使是kill -9,因为无法杀死一个已经死亡的进程;
- 父进程交派给子进程的任务完成状况需知道,如子进程运行完,其结果对错与否,是否正常退出;
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息;
- 尽量父进程要晚于子进程退出,可规格化进行资源回收;
pid_t wait(int*status)
- 返回值,成功,返回被等待进程pid;失败,返回-1;
- 参数,获取子进程退出状态,不关心可设置为NULL;
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i=0;
//创建5个子进程,睡眠2秒退出
for(i=0; i<5; i++)
{
pid_t id = fork();
if(id<0)
{
perror("fork");
return 1;
}
else if(id==0)
{
sleep(2);
printf("child:pid=%d, ppid=%d, id=%d\n",getpid(),getppid(),id);
exit(1);
}
}
//父进程等待
for(i=0; i<5; i++)
{
sleep(3);
pid_t wait_pid = wait(NULL);
printf("father:pid=%d, wait_pid=%d\n",getpid(), wait_pid);
}
sleep(3);
printf("father finish!\n");
return 0;
}
[wz@192 Desktop]$ ./target
child:pid=116610, ppid=116606, id=0
child:pid=116607, ppid=116606, id=0
child:pid=116608, ppid=116606, id=0
child:pid=116609, ppid=116606, id=0
child:pid=116611, ppid=116606, id=0
father:pid=116606, wait_pid=116607
father:pid=116606, wait_pid=116608
father:pid=116606, wait_pid=116609
father:pid=116606, wait_pid=116610
father:pid=116606, wait_pid=116611
father finish!
pid_ t waitpid(pid_t pid, int *status, int options)
- 返回值
- 正常返回的时候,waitpid返回收集到的子进程的进程PID;
- 如设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,返回0;
- 如调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
- 参数
- pid
- -1等待任一个子进程,与wait等效;
- >0等待指定pid子进程;
- status
- WIFEXITED(status),若为正常终止子进程进程返回的状态,则为真;
- WEXITSTATUS(status),若WIFEXITED非零,提取子进程退出码;
- options
- WNOHANG,若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待;
- pid
#include<sys/types.h>
#include<sys/wait.h>
pid_ t waitpid(pid_t pid, int *status, int options);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
//创建子进程,睡眠2秒退出
pid_t id = fork();
if(id<0)
{
perror("fork");
return 1;
}
else if(id==0)
{
sleep(2);
printf("child:pid=%d, ppid=%d, id=%d\n",getpid(),getppid(),id);
exit(1);
}
//父进程等待
sleep(5);
pid_t wait_pid = waitpid(id, NULL, 0);
printf("father:pid=%d, wait_pid=%d\n",getpid(), wait_pid);
sleep(3);
printf("father finish!\n");
return 0;
}
- 若子进程已退出,调用wait/waitpid时,wait/waitpid会立即返回,并释放资源,获得子进程退出信息;
- 如在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞;
- 如不存在该子进程,则立即出错返回;
获取子进程status
- wait/waitpid都有一个status参数,此参数为输出型参数,由操作系统填充;
- 如传递NULL,表示不关心子进程的退出状态信息;否则,操作系统会根据该参数,将子进程发退出信息反馈给父进程;
- status不能简单的当作整型看待,可当作位图,只研究低16位;
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
//创建子进程,睡眠2秒退出
pid_t id = fork();
if(id<0)
{
perror("fork");
return 1;
}
else if(id==0)
{
sleep(5);
printf("child:pid=%d, ppid=%d, id=%d\n",getpid(),getppid(),id);
exit(1);
}
//父进程等待
int status = 0;
pid_t wait_pid = waitpid(id, &status, 0);
printf("father:pid=%d, wait_pid=%d\n",getpid(), wait_pid);
int stat = (status>>8) & 0xFF; //status二进制中8~15位;
int sig = status & 0x7F; //status二进制中0~7位;
sleep(3);
printf("father finish!\n");
return 0;
}
注:
- 不可通过设置全局变量,来告知父进程子进程的退出状态,因为父子进程相互独立,且写实拷贝;
- waitpid获取的status值,是从子进程的task_struct中得到的;
- 一般子进程提前终止,是该进程收到了OS发送的信号(信号中没有0,0就表示为正常终止,正常终止是没有收到任何退出信号 );
[wz@192 Desktop]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX