一、父子进程的关系:
子进程是父进程的副本。子进程获得父进程数据段,堆,栈,正文段共享。
在fork之后,一般情况那个会先运行,是不确定的。如果非要确定那个要先运行,需要IPC机制。
区别:
1)fork的返回值
2)pid不同
二、进程的终止:8种情况
正常终止
1)main 中 return
2)exit(), c库函数,会执行io库的清理工作,关闭所有的流(关闭所有的文件),以及所有打开的文件。已经清理函数(atexit)。
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 int main(int argc, const char *argv[])
5 {
6 printf("capper");
7 exit(0);
8 printf("made in future\n");
9
10 return 0;
11 }
3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(int argc, const char *argv[])
6 {
7 printf("capper");
8 _exit(0);
9 printf("made in future\n");
10
11 return 0;
12 }
4) 主线程退出
5)主线程调用pthread_exit
异常终止
6)abort()(事态严重,内核调用)
7)signal kill pid (一般错误)
8) 最后一个线程被pthread_cancle
三、进程的退出
僵尸进程(子先没)和孤儿进程(爹先没)
僵尸进程:进程执行结束 但 空间未被回收 变成僵尸进程
1.exit 库函数
退出状态,终止的进程会通知父进程,自己使如何终止的。如果是正常结束(终止),则由exit传入的参数。如果是异常终止,则有内核通知异常终止原因的状态。任何情况下,父进程都能使用wait,waitpid获得这个状态,以及资源的回收。
void exit(int status)
exit(1);
功能:
让进程退出,并刷新缓存区
参数:
status:进程退出的状态
返回值:
缺省
EXIT_SUCCESS 0
EXIT_FAILURE 1
return 当该关键字出现在main函数中时候可以结束进程
如果在其他函数中则表示结束该函数。
exit -> 刷新缓存区 -> atexit注册的退出函数 -> _exit
2._exit 系统调用
void _exit(int status);
功能:
让进程退出,不刷新缓存区
参数:
status:进程退出状态
返回值:
缺省
mian(int agc,argv)
{
main();
}
3.atexit 回调函数
int atexit(void (*function)(void));
功能:
注册进程退出前执行的函数
参数:
function:函数指针
指向void返回值void参数的函数指针
返回值:
成功返回0
失败返回非0
当程序调用exit或者由main函数执行return时,所有用atexit
注册的退出函数,将会由注册时顺序倒序被调用
四、进程空间的回收
exit(20);
1.wait
pid_t wait(int *status);
功能:该函数可以阻塞等待任意子进程退出
并回收该进程的状态。
一般用于父进程回收子进程状态。
参数:status 进程退出时候的状态
如果不关心其退出状态一般用NULL表示
如果要回收进程退出状态,则用WEXITSTATUS回收。
返回值:成功 回收的子进程pid
失败 -1;
WIFEXITED(status) 子进程是不是正常结束,(必须是正常结束)
WEXITSTATUS(status) 使用这个宏去那返回值,
WIFSIGNALED(status) 是不是收到了信号而终止的
WTERMSIG(status)如果是信号终止的,那么是几号信号。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <sys/wait.h>
7
8 int main(int argc, char *argv[])
9 {
10 pid_t pid = fork();
11 if(pid>0)
12 {
13 int status;
14 printf("father pid:%d ppid:%d\n",getpid(),getppid());
15 pid_t recycle_pid = wait(&status);
16 if(WIFEXITED(status))
17 {
18
19 printf("child normal terminal,recycle pid %d , exit value %d\n"
20 ,recycle_pid,WEXITSTATUS(status));
21 }
22 if(WIFSIGNALED(status))
23 {
24 printf("child terminal bu signal ,rrecycle pid %d, signal num %d \n"
25 ,recycle_pid,WTERMSIG(status));
26 }
27 }
28 else if(0 == pid)
29 {
30 printf("child pid:%d ppid:%d\n",getpid(),getppid());
31 sleep(5);
32 exit(20);
33 }
34 else
35 {
36 perror("fork");
37 return 1;
38 }
39
40 return 0;
41 }
2.waitpid
waitpid(-1,status,0)=wait(status);
pid_t waitpid(pid_t pid, int *status, int options);
< -1 回收指定进程组内的任意子进程
-1 回收任意子进程,组内外
0 回收和当前调用waitpid一个组的所有子进程,组内
> 0 回收指定ID的子进程
waitpid (-1,a,0) == wait(a);
status 子进程退出时候的状态,
如果不关注退出状态用NULL;
options 选项:
0 表示回收过程会阻塞等待
WNOHANG 表示非阻塞模式回收资源。
返回值:成功 返回接收资源的子进程pid
失败 -1
0,
3.exec
execute
exec族(有6个,功能一样,参数不一样)
用fork创建子进程后 执行的是 和父进程相同的程序(但有可能执行不同的代码分支),
子进程往往要调用一种 exec函数 以执行另一个程序。当进程调用一种exec函数时,
该进程的用户空间代码 和 数据 完全被新程序(data段,code段都替换) 替换,从新程序的启动例程开始执行。
调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
有六种以exec开头的函数,统称exec函数:
vector
ls -l -i list
execl("/bin/ls","-l","-i",NULL);
execlp("ls","-l","-i",NULL);
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main(int argc, const char *argv[])
5 {
6 execl("/usr/bin/firefox","firefox","www.taobao.com",NULL);
7 printf("看见就错了\n");
8
9 return 0;
10 }
运行结果:成功打开火狐淘宝页面
int execv(const char *path, char *const argv[]);
key=value
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char*path,char*const argv[],char*const evnp[]);
int execlp(const char *file, const char *arg, ...);
p:env $PATH 有一堆路径,里面有bin,ls 在bin路径下,即不需要/bin/ls,直接ls即可
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main(int argc, const char *argv[])
5 {
6 //ls -a -l -h -F
7 // execl("/bin/ls","ls","-a","-l","-h","-F",NULL);
8 //env path
9 execlp("ls","ls","-a","-l","-h","-F","--color",NULL);
10
11 printf("看见就错了\n");
12 return 0;
13 }
echo $PATH
PATH= (bin在里面)
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
这些函数的区别
1),前4个使用路径名作为参数,后面两个使用文件名做参数
当filename中,含有/时视为路径名,否则就按PATH变量,在指定目录下查找可执行文件。
2)相关的参数表传递
l表示list,v表示vector
execl,execlp,execle,需要将参数一个一个列出,并以NULL结尾。
execv,execvp,execve,需要构造一个参数指针数组,然后将数组的地址传入。
3)以e结尾的函数,可以传入一个指向环境字符串的指针数组的指针。其他未指定环境变量,使用父进程继承过来的。
execve 是真正的系统调用
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错
则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
综合练习:execl、execlp、execv、execvp 调用可执行文件argc
1)有 p ,加上 ./ 在当前路径查找,没有 p 不需要 ./
2)execl、execlp 第一个参数放可执行文件,NULL前的参数 放 传到可执行文件argc的参数
3)execv、execvp 函数里放可执行文件、数组名;数组放传到可执行文件argc参数
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main(int argc, char *argv[])
5 {
6 execl("argc","0","1","2",NULL);
7
8 // execlp("/home/linux/yxy/stdio/system/jincheng/sec2/argc","0","1","2",NULL);
9 // execlp("./argc","0","1","2",NULL);
10
11 // char *const arg[] = {"0","1","2",NULL};
12 // execv("argc",arg);
13
14 // char *const arg[] = {"0","1","2",NULL};
15 // execvp("./argc",arg);
16
17 printf("看见就错了\n");
18 return 0;
19 }
标签:status,const,int,pid,char,进程,高编
From: https://blog.csdn.net/weixin_71751116/article/details/139992210