目录
一、进程创建
1、fork函数初识
二、进程终止
1、正常终止
2、异常终止
三、进程等待
1、进程等待必要性
2、进程等待的方法:
四、获取子进程status
1、基本概念
2、进程的阻塞等待方式
3、进程的非阻塞等待方式
五、进程程序替换
1、六种替换函数
2、可执行程序和脚本能跨语言调用的原因
3、环境变量是什么时候给进程的
一、进程创建
1、fork函数初识
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
#include<unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1
二、进程终止
代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止
1、正常终止
可以通过 echo $? 查看进程退出码
1. 从main返回
2. 调用exit
3. _exit 异常退出:
_exit与exit区别:
_exit为纯正的系统调用,
exit最后也会调用_exit, 但在调用 _exit之前,还做了其他工作: 1. 执行用户通过 atexit或on_exit定义的清理函数。 2. 关闭所有打开的流,所有的缓存数据均被写入 3. 调用_exit
2、异常终止
ctrl + c,信号终止
异常时的退出码没有意义
三、进程等待
如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。 如果不存在该子进程,则立即出错返回。
1、进程等待必要性
子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。 进程一旦变成僵尸状态,就不能被杀死。 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
2、进程等待的方法:
①wait方法
阻塞等待
#include<sys/types.h>
#include pid_t wait(int*status);
返回值: 成功返回被等待进程pid,失败返回-1。
参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
②waitpid方法
pid_ t waitpid(pid_t pid, int *status, int options);
返回值: 当正常返回的时候waitpid返回收集到的子进程的进程ID; 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0; 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid: Pid=-1,等待任一个子进程。与wait等效。 Pid>0.等待其进程ID与pid相等的子进程。
status: WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options: WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进 程的ID。
四、获取子进程status
1、基本概念
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。 如果传递NULL,表示不关心子进程的退出状态信息。 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。 status不能简单的当作整形来看待,可以当作位图来看待。
2、进程的阻塞等待方式
int main(){pid_t pid;pid = fork();if(pid < 0){printf("%s fork error\n",__FUNCTION__);return 1;} else if( pid == 0 ){ //childprintf("child is run, pid is : %d\n",getpid());sleep(5);exit(257);} else{int status = 0;pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5Sprintf("this is test for wait\n");if( WIFEXITED(status) && ret == pid ){printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));}else{printf("wait child failed, return.\n");return 1;}}return 0;}
3、进程的非阻塞等待方式
#include <stdio.h>
#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>int main(){pid_t pid;pid = fork();if(pid < 0){printf("%s fork error\n",__FUNCTION__);return 1;}else if( pid == 0 ){ //childprintf("child is run, pid is : %d\n",getpid());sleep(5);exit(1);} else{int status = 0;pid_t ret = 0;do{ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待if( ret == 0 ){printf("child is running\n");}sleep(1);}while(ret == 0);if( WIFEXITED(status) && ret == pid ){printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));}else{printf("wait child failed, return.\n");return 1;}}return 0;}
五、进程程序替换
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
1、六种替换函数
#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[]);
程序替换成功后,exec*之后的代码不会被执行,替换失败了才可能会执行后续代码
exec系列函数只有失败返回值,没有成功返回值,承担加载器的效果
Linux中形成的可执行程序是有格式的,ELF,可执行程序的表头。
exec系列函数可以调用系统命令,也可以调用我们自己编译的可执行程序
2、可执行程序和脚本能跨语言调用的原因
所有语言写的代码运行起来本质都是进程
3、环境变量是什么时候给进程的
环境变量也是数据,创建子进程的时候,环境变量就已经被子进程继承下去。