1. 进程创建
1.1. 写实拷贝
1.2. fork常规用法
一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子
进程来处理请求。
一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数 。
1.3. fork调用失败的原因
系统中有太多的进程
实际用户的进程数超过了限制
2. 进程终止
2.1. 进程退出场景解读
代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止
main函数的返回值,通常表明进程退出的情况
- 返回值为0:代码运行完毕,结果正确
- 返回值非0:代码运行完毕,结果不正确
-
- 返回值是退出码,退出码不同表示不同的出错原因
- echo $?:查看退出码(最近运行的一个进程)
- 进程退出码会写到task_struct中
- 代码异常:退出码意义,进程收到了信号
- strerror查看退出码
- 可以自己定义退出码:exit(20)
2.2. 常见进程退出方法
- main函数返回值
- exit/_exit退出
-
- exit是函数调用,_exit是系统调用,exit底层还是调用的_exit系统调用,他们是上下层关系
- exit退出会进行缓冲区刷新
- _exit退出不会进行缓冲区刷新
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int i = 0;
for(; i < 180; i++)
{
printf("%d,%s\n",i,strerror(i));
}
int num = 10;
printf("进程退出:%d",num);
_exit(1);
printf("进程退出:%d",num);
return 0;
}
3. 进程等待
解决僵尸问题:进程等待
3.1. 为什么要进行进程等待
之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法
杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,
或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.2. 进程等待是什么
waitpid函数解释
status解释:
- 表示子进程退出的状态,类似与main函数的返回值
- 是输出型参数:会在函数内部改变,返回给外部,有点像取地址调用
- status:存储子进程退出状态(需用宏解析,如
WIFEXITED
,WEXITSTATUS
等)
printf("%d\n",(status>>8)&0xff);
printf("%d\n",WEXITSTATUS(status));
//这两种方法都可以获取进程退出码
返回值解释
>0
:等待指定的子进程。-1
:等待任意子进程(等价于wait
)。0
:等待同进程组的子进程。<-1
:等待进程组 ID 为|pid|
的子进程。
option解释:
- 控制行为
- WNOHANG:如果子进程没有退出就立即返回--->非阻塞调用
-
- 例子:计算机卡住了---->阻塞调用
- 如果子进程没有退出,父进程会阻塞在wait调用处,类似于scanf函数
- 阻塞调用:父进程一直等待子进程结束调用,不做其它事情。
- 非阻塞调用(none block):在等待子进程的期间做其他事情。
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
printf("我的程序要运行了!\n");
//execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
execl("/usr/bin/top", "top", NULL);
printf("我的程序运行完毕了\n");
return 0;
}
//// 函数指针类型
//typedef void (*func_t)();
//
//#define NUM 5
//func_t handlers[NUM+1];
//
//// 如下是任务
//void DownLoad()
//{
// printf("我是一个下载的任务...\n");
//}
//void Flush()
//{
// printf("我是一个刷新的任务...\n");
//}
//void Log()
//{
// printf("我是一个记录日志的任务...\n");
//}
//
//
//
//// 注册
//
//void registerHandler(func_t h[], func_t f)
//{
// int i = 0;
// for(; i < NUM; i++)
// {
// if(h[i] == NULL)break;
// }
// if(i == NUM) return;
// h[i] = f;
// h[i+1] = NULL;
//}
//
//int main()
//{
// registerHandler(handlers, DownLoad);
// registerHandler(handlers, Flush);
// registerHandler(handlers, Log);
//
//
// pid_t id = fork();
// if(id == 0)
// {
// //子进程
// int cnt = 3;
// while(1)
// {
// sleep(3);
// printf("我是一个子进程, pid : %d, ppid : %d\n", getpid(), getppid());
// sleep(1);
// cnt--;
// //int *p = 0;
// //*p= 100;
// // int a = 10;
// // a /= 0;
// }
// exit(10);
// }
//
// // 父进程
// while(1)
// {
// int status = 0;
// pid_t rid = waitpid(id, &status, WNOHANG);
// if(rid > 0)
// {
// printf("wait success, rid: %d, exit code: %d, exit signal: %d\n", rid, (status>>8)&0xFF, status&0x7F) ; // rid
// break;
// }
// else if(rid == 0)
// {
// // 函数指针进行回调处理
// int i = 0;
// for(; handlers[i]; i++)
// {
// handlers[i]();
// }
// printf("本轮调用结束,子进程没有退出\n");
// sleep(1);
// }
// else
// {
// printf("等待失败\n");
// break;
// }
//
// }
//
//
// //父进程
// //1. 子进程退出
// //2. 子进程没退出呢??
// //pid_t rid = wait(NULL);
// //int status = 0;
// //pid_t rid = waitpid(id, &status, 0);
// //if(rid > 0)
// //{
// // if(WIFEXITED(status))
// // printf("wait success, rid: %d, exit code: %d, exit signal: %d\n", rid, WEXITSTATUS(status), status&0x7F) ; // rid
// // else
// // printf("子进程退出异常!\n");
// // //printf("wait success, rid: %d, exit code: %d, exit signal: %d\n", rid, (status>>8)&0xFF, status&0x7F) ; // rid
// //}
// //else
// //{
// // printf("wait failed: %d: %s\n", errno, strerror(errno));
// //}
// return 0;
//}
//
//
//void fun()
//{
// printf("fun begin!\n");
// _exit(4);
// printf("fun end!\n");
//}
//
////int main()
////{
////
//// //fun();
//// printf("main!");
////
//// sleep(2);
////
//// _exit(23);
////
//// //int i = 0;
//// //for(; i < 200; i++)
//// //{
//// // printf("%d->%s\n", i, strerror(i));
//// //}
////
////
//// ////printf("hello world\n");
////
//// //FILE *fp = fopen("log.txt", "r");
//// //if(fp == NULL) return 13;
////
//// //// 读取
//// //fclose(fp);
//// return 0;
////}
4. 进程程序替换
4.1. 替换原理:
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
- 只要是能在linux上跑的语言都能通过程序替换来跑
- 程序替换不创建新的进程
4.2. 替换函数使用
上面的接口不是系统调用最终都要调用下面这个系统调用接口
int execve(const char *path, char *const argv[], char *const envp[]);
- 函数解释:
-
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1,execute系列函数不用对返回值做判
所以exec函数只有出错的返回值而没有成功的返回值
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
小知识点
linux运行python:
linux运行shell脚本:
4.3. 命名理解
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量 ,覆盖式的写入当前进程(可以是当前进程的子进程)环境变量,
#include<unistd.h>
int main()
{
printf("我的程序要运行了!\n");
//execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
execl("/usr/bin/top", "top", NULL);
printf("我的程序运行完毕了\n");
return 0;
}
运行该程序的时候会执行top命令。
- putenv():在当前程序中获得
4.4. 补充知识点
导入环境变量到当前进程
- putenv()到environ中
- execvpe(path,argv,environ)
- //这里传递将新增的环境变量导入到environ中,直接传参的时候传environ
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
extern char ** environ;
char* const addenv[]=
{
(char* const)"myenv1=1234",
(char* const)"myenv2=1234",
(char* const)"myenv3=1234",
(char* const)"myenv4=1234",
(char* const)"myenv5=1234",
NULL
};
int main()
{
printf("程序替换前\n");
//替换命令
//execl("/usr/bin/ls","/usr/bin/ls", "-la", "-ln", NULL);
//不携带路径
//execlp("ls","ls","-a","-l", NULL);
//命令行参数以vector的形式
//execv("/usr/bin/ls",argv);
char* const argv[]=
{
(char* const)"-a",
(char* const)"-l",
(char* const)"-ln",
NULL
};
int i = 0;
for(; addenv[i]; i++)
{
putenv(addenv[i]);
}
//不需携带路径和命令行参数
execvpe("./oother",argv,environ);
char** e = environ;
for(;*e; *e++)
{
printf("%s\n", *e);
}
//execl("/usr/bin/ls","ls", "-la", "-ln", NULL);
//替换自己写的程序
//execl("/usr/bin/python3","python","other.py", NULL);
//execl("/usr/bin/bash","bash","other.sh", NULL);
//execl("./other","other", NULL);
//printf("程序替后\n");
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<sys/types.h>
#include<unistd.h>
int main()
{
std::cout << "hello C++" << std::endl;
std::cout << "mypid is:" <<getpid()<<"myppid is:"<<getppid()<< std::endl;
char** e = environ;
for(;*e; *e++)
{
printf("%s\n", *e);
}
return 0;
}