进程创建
pid_t fork(void)
通过fork()
创建一个子进程,返回值有两个:
情况\返回值\进程 | 父进程 | 子进程 |
---|---|---|
创建成功 | 子进程的pid | 0 |
创建失败 | -1 |
【Note】进程创建应用了写时拷贝技术
pid_t vfork(void)
通过vfork()
创建子进程,会阻塞父进程
阻塞:为了完成一个功能,发起调用,若当前不具备完成条件,则一直等待
非阻塞:为了完成一个功能,发起调用,若当前不具备完成条件,则报错返回
【Note】父子进程共用同一虚拟地址空间
进程退出
1.main()
中return
——会刷新缓冲区
2.调用库函数exit()
——会刷新缓冲区
3.系统系统调用接口_exit()
——不会刷新缓冲区,直接释放资源
进程退出情况分为:正常退出、异常退出
系统调用错误原因获取接口:perror()
,stderror(errno)
进程等待
父进程等待子进程退出,获取子进程退出状态及运行结果的操作,正确使用进程等待,可以及时释放资源,避免僵尸进程的出现。
pid_t wait(int* status)
wait()
是一个阻塞函数,会阻塞父进程,当子进程没有返回时,父进程会一直等待子进程的返回值。
status
:用于获取子进程的退出返回值
(后面有详细解释)
返回值
:成功返回则为子进程的pid
,失败返回-1
pid_t waitpid(pid_t pid,int* status,int option)
waitpid()
可以等待任意一个子进程退出,也可以等待一个指定的子进程退出;它可以设置为默认阻塞接口,也可以设为非阻塞接口
pid
:设为-1
表示等待一个子进程,>0
表示等待指定子进程
status
:用于获取返回值
options
:0
表示默认阻塞等待,WNOHANG
设置为非阻塞
返回值
:大于0
即子进程的pid,等于0
表示没有子进程退出,错误返回-1
status组成初探
status
(一个int
型数据)组成如下所示:
status我们暂时关心的是其低16位数据:
低16位中的高8位表示子进程的退出返回值;获取方式:status>>8&0xff
低16位中的低8位的最高位表示该子进程结束时是否产生coredump文件;获取方式:status>>7&1
低16位中的低8位的剩余7位表示异常信号值;获取方式:status&0x7f
wait中status的获取例子
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
int main(){
int ret = fork();
if(ret<0){
printf("creat child_process failed!\n");
return -1;
}
else if(ret == 0){
printf("i am child\n");
// char* str = NULL;
// memcpy(str,"test",4);
sleep(5);
exit(99);
}
else{
int ret,status;
while((ret = waitpid(-1,&status,0))==0){
sleep(1);
}
printf("coredump_flag is %d\n",status>>7&1);
printf("exit_value is %d\n",status>>8&0xff);
printf("signal_abnormal is %d\n",status&0x7f);
if(status&0x7f!=0){
printf("异常信号值非0!返回值没有意义!\n");
}
}
return 0;
进程程序替换
用fork()
创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec()
函数以执行另一个程序。
当进程调用一种exec()
函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
excel()
函数族:
#include <unistd.h>
extern char** environ;
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* file,char* const argv[],char* const envp[]);
命名理解
l(list):表示参数采用列表
v(vector):表示参数采用数组
p(path):有p自动搜索环境变量PATH
e(env):表示自己维护环境变量
函数名 | 参数格式 | 是否带路径 | 是否使用当前环境变量 |
---|---|---|---|
execl | 列表 | 否 | 是 |
execlp | 列表 | 是 | 是 |
execle | 列表 | 否 | 否,须自己组装环境变量 |
execv | 数组 | 否 | 是 |
execvp | 数组 | 是 | 是 |
execve | 数组 | 否 | 否,须自己组装环境变量 |
一个replace例子
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char* argv[]){
printf("程序即将开始替换!\n");
execl("/usr/bin/ls","ls","-a","-l",NULL);
printf("替换之后的代码!\n");
return 0;
}
运行效果:
可以发现,当程序运行到第6行时,程序发生替换,main
被替为ls
,因此打印出main的一句提示后,后面输出的ls
运行结果,ls
运行结束,程序即运行终止。
换句话说,当发生程序替换后,替换之后的语句将不再执行!
结语
我的本质就是复读机!复读虽然不快乐但很高效!