目录
一,进程创建
写时拷贝
二,进程终止
三,进程等待
获取子进程status
四,进程程序替换
替换函数
五,手写简单的shell
一,进程创建
#include
//子进程返回0,父进程返回子进程的ID,出错返回-1
pid_t fork(void);
进程调用fork,当控制转移到内核中的fork代码后,内核将会:
- 分配新的内存块和内核数据结构给子进程;
- 拷贝父进程部分数据结构内容给子进程;
- 添加子进程到系统进程列表中;
- fork返回,开始调度器调度;
#include
#include
#include
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
写时拷贝
通常父子代码共享,数据也共享(父子在不写入时);当有任意一方写入时,便以写实拷贝的方式各自一份副本,从而保证父子进程的独立性;
二,进程终止
#include
#include
#include
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
void exit(int status);
#include
void _exit(int status);
//status 定义了进程终止状态,父进程通过wait来获取该值;
//虽然status为int,但仅低8位可被父进程使用,_exit(-1)执行echo $?结果为255;
#include
#include
int main()
{
printf("hello");
exit(0);
return 0;
}
[wz@192 Desktop]$ ./target
hello[wz@192 Desktop]$
#include
#include
int main()
{
printf("hello");
_exit(0);
return 0;
}
[wz@192 Desktop]$ ./target
[wz@192 Desktop]$
return退出
一种常见的退出进程方法,执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当作exit的参数;
进程终止的核心思想,就是归还资源;
- 释放曾经为管理进程所维护的所有数据结构对象;
- 不是真的把数据结构对象销毁,而是设置为不用状态,保存起来,“数据结构池”;
- 释放程序代码和数据所占的内存空间;
- 不是将代码和数据清空,把内存设置为无效即可;
- 取消曾经该进程的链接关系;
三,进程等待
进程等待即将当前进程放入等待队列,并将进程状态设置为非R状态;唤醒进程即将进程从等待队列中移入运行队列,并将进程设置为R状态;
进程等待的必要性
pid_t wait(int*status)
#include
#include
pid_t wait(int *status);
#include
#include
#include
#include
#include
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)
#include
#include
pid_ t waitpid(pid_t pid, int *status, int options);
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
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;
}
注:
[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
int status = 0;
pid_t wait_pid = waitpid(id, &status, 0); //阻塞调用
printf("father:pid=%d, wait_pid=%d\n",getpid(), wait_pid);
if(wait_pid>0)
{
if(WIFEXITED(status))
printf("child exit normal, status=%d\n", WEXITSTATUS(status));
else
printf("child exit error, sig=%d\n", WTERMSIG(status));
}
int status = 0;
pid_t wait_pid = 0;
do
{
wait_pid = waitpid(id, &status, WNOHANG); //非阻塞调用
if(wait_pid == 0)
printf("child is running\n");
sleep(1);
}while(wait_pid == 0)
if(WIFEXITED(status) && wait_pid==pid)
printf("child exit normal, status=%d\n", WEXITSTATUS(status));
else
printf("child exit error, sig=%d\n", WTERMSIG(status));
四,进程程序替换
替换原理,用fork创建子进程后执行的是和父进程相同的程序(可能执行分流),子进程往往要调用一种exec函数以执行另一个程序;当调用exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行;调用exec并不创建新进程,exec前后进程id不变;
创建子进程的目的
替换函数
//六种exec开头的替换函数
//调用成功,则加载新的程序,从启动代码开始执行,不返回;
//调用出错,则返回-1;
#include
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[]);
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
execlp("ps", "ps", "-ef", NULL);
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
execvp("ps", argv);
execve("/bin/ps", argv, envp);
五,手写简单的shell
流程:
- 获取命令;
- 解析命令;
- 建立子进程fork;
- 替换子进程execvp;
- 父进程等待子进程的退出wait;
#include
#include
#include
#include
#include
#include
#define MAX_CMD 1024
char command[MAX_CMD];
//读取数据,获取命令行
int do_face()
{
memset(command, 0x00, MAX_CMD); //设置字符数组值为0;
printf("[myshell]$ ");
fflush(stdout);
if(fgets(command, MAX_CMD-1, stdin)) //从输入中读取
{
command[strlen(command)-1] = 0; //printf("%s", command);
return 1;
}
return 0;
}
//解析数据,解析命令行
char** do_parse(char* buff)
{
static char* argv[32];
int index=0;
argv[index]=strtok(buff, " "); //printf("index[0]: %s\n", argv[index]);
while(1)
{
index++;
argv[index]=strtok(NULL, " ");
if(argv[index]==NULL)
break;
}
return argv;
}
//执行命令,建立子进程、替换子进程,父进程等待子进程退出
int do_exec(char* buff)
{
char** argv=do_parse(buff);
if(argv[0]==NULL)
exit(-1);
//内置命令
//如cd应是针对父进程,不可像非内置命令一样使用子进程
//是shell内的一个函数调用
if(strcmp(argv[0], "cd")==0 && chdir(argv[1])==0)
continue;
//非内置命令
pid_t pid=fork();
if(pid==0)
execvp(argv[0], argv);
else
{
int status;
int wait_pid=waitpid(-1, &status, 0);
if(WIFEXITED(status) && wait_pid==pid)
printf("exit normal, exit code:%d\n", WEXITSTATUS(status));
else
printf("exit error\n");
}
return 0;
}
int main()
{
while(1)
{
if(do_face()==0)
continue;
do_exec(command);
}
return 0;
}