1. 程序与进程
如果程序是菜谱,进程就是厨师烹饪;
如果程序是乐谱,进程就是乐师演奏;
如果程序是剑谱,进程就是剑客舞剑;
如果程序是棋谱,进程就是棋士复盘;
程序是静的,进程是动的。
- 进程与程序区别
No. | 进程 | 程序 |
---|---|---|
1 | 动态 | 静态 |
2 | 有生命周期 | 指令集合 |
3 | 只能对应一个程序 | 可以对应多个进程 |
No. | 进程 | 状态 |
---|---|---|
1 | 进程正在处理器运行,这个状态从来木见过. | O |
2 | 休眠状态(sleeping) | S |
3 | 等待运行(runable)R Running or runnable (on run queue) 进程处于运行或就绪状态可以对应多个进程 | R |
4 | 空闲状态(idle) | I |
5 | 僵尸状态(zombie) | Z |
2. 概念
进程:程序在计算机上的一次执行过程,执行中的程序。
- 进程是一个抽象概念
No. | 组成 | 含义 | 类比 |
---|---|---|---|
1 | 一个独立的逻辑控制流 | 独占处理器 | 工人/机器 |
2 | 一个私有的地址空间 | 独占存储器系统 | 工厂 |
- 本质
- 程序在地址空间中按照代码逻辑控制流执行
- 资源分配最小单位
3. 从代码到程序
4. 从程序到进程
- 内核将程序读入内存,为程序镜像分配内存空间。
- 内核为该进程分配进程标志符PID。
- 内核为该进程保存PID及相应的进程状态信息。
- 进程控制块(PCB):保存进程控制信息
4.1程序格式ELF
ELF(Executable and Linkable Format)文件格式,一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。
查看程序(ELF文件):readelf -S 文件名 (必须是二进制文件 ./a.out)
查看进程空间大小:size 文件名
查看进程时间大小:time 文件名
gdb查看内存映射信息:info proc mapping
4.2 虚拟存储器/虚拟地址空间
段名 | 组成 | 来源 |
---|---|---|
代码段 | .text | 可执行文件 |
数据段 | .data bss | 可执行文件 |
堆栈段 | heap stack | 请求 |
变量 | 位置 |
---|---|
经过初始化的全局变量和静态变量 | .data |
未经初始化的全局变量和静态变量 | .bss |
函数内部声明的局部变量 | stack |
const修饰的全局变量 | .text |
const修饰的局部变量 | stack |
字符串常量 | .text |
.bss(Block Started by Symbol)存放程序中未初始化的全局变量和静态变量,程序执行之前BSS段会自动清0。
4.2.2详细资料
4.3 进程状态
类似视频/音频播放器
No. | 状态 | 含义 |
---|---|---|
1 | 就绪(Ready) | 进程已获得到除CPU以外的所有必要的资源,获得CPU立即执行 |
2 | 运行(Running) | 程序正在CPU上执行 |
3 | 阻塞(Blocked) | 等待某个事件发生而无法执行时,放弃CPU |
4.4如何查看进程
No. | OS | 命令 | e.g. |
---|---|---|---|
1 | Windows | tasklist | tasklist /FI “PID eq 进程PID” |
2 | Linux | ps / pstree / top | 一般结构/树结构/详细信息 |
4.5 ps命令
- 查看某进程
- 通过进程PID查看:ps -p 进程PID
- 通过命令行查看:ps -C 命令行
- 查看程序的PID: pidof 程序名
- 查看 指定的列:ps -o pid,ppid,cmd,time,tty,s
- 查看进程
No. | 风格 | 命令 | 属性说明 |
---|---|---|---|
1 | BSD风格 | ps aux | a: 终端上所有用户的进程;u:以用户为中心显示详细信息,x:无终端进程 |
2 | System V风格 | ps -ef | e:所有进程;f:树状显示 |
Unix从操作风格分为System V风格和BSD风格。它们在目录结构、脚本、命令行等方面存在一些差异。
System V, 曾经也被称为AT&T SystemV,是Unix操作系统众多版本中的一支。它最初由AT&T开发。
BSD(BerkeleySoftware Distribution,伯克利软件套件)是Unix的衍生系统,1970年代由伯克利加州大学(UniversityofCalifornia, Berkeley)开发。
随着一些并不基于这两者代码的UNIX实现的出现,例如Linux,这一归纳不再准确。像POSIX这样的标准化努力一直在试图减少各种实现之间的不同。
- 列表示说明
No. | 标识 | 含义 |
---|---|---|
1 | USER | 用户 |
2 | PID | 进程ID |
3 | %CPU | 进程占用的CPU百分比 |
4 | %MEM | 占用内存的百分比 |
5 | VSZ | 进程虚拟大小 |
6 | RSS | 常驻内存(内存中页的数量) |
7 | TTY | 终端ID |
8 | STAT | 进程状态 |
9 | START | 启动进程的时间 |
10 | TIME | 进程消耗CPU的时间 |
11 | COMMAND | 命令的名称和参数 |
- 进程状态标识
No. | 标识 | 含义 |
---|---|---|
1 | D | 不可中断Uninterruptible(usually IO) |
2 | R | 正在运行,或在队列中的进程 |
3 | S | 处于休眠状态 |
4 | T | 停止或被追踪 |
5 | Z | 僵尸进程 |
6 | W | 进入内存交换(从内核2.6开始无效) |
7 | X | 死掉的进程 |
8 | < | 高优先级 |
9 | n | 低优先级 |
10 | s | 包含子进程 |
11 | + | 位于后台的进程组 |
4.6 pstree命令
以树状图的方式展现进程之间的派生关系
- 安装
yum install psmisc
4.7 top命令
实时显示系统中各个进程的资源占用,类似Windows任务管理器。
Linux一切皆文件,在/proc/下也可以查看到进程。
命令|含义|
–|--|–|
top|查看任务管理器
q|退出任务管理器
4.8 如何创建进程
No. | OS | 命令 |
---|---|---|
1 | Windows | 程序名 |
2 | Linux | 程序名 |
4.9 如何杀死进程
No. | OS | 命令 | |
---|---|---|---|
1 | Windows | taskkill /F /PID 进程标识/taskkill /F /IM 程序名 | |
2 | Linux | kill 进程标识PID | |
3 | ctrl+Z | 打入后台,暂停 | |
4 | fg 文件名或者pid | 返回前台,重新执行 | |
5 | fjobs | 查看后台程序 | |
6 | ctrl+c | 停止进程 |
5. 进程操作接口
5.1 获取PID
进程标识pid:进程身份证号
No. | 函数 | 接口 |
---|---|---|
1 | pid_t getpid() | 获取当前进程ID |
2 | pid_t getppid() | 获取当前进程父进程ID |
- 注意加头文件
#include <unistd.h>
- 示例
#include <stdio.h>
#include <unistd.h>
int main(){
printf("PID:%dPPID:%d\n",getpid(),getppid());
}
如何查看进程的PID和PPID? ps -o pid,ppid,cmd,s
5.2如何创建进程
5.2. 1 分叉函数pid_t fork()
- 返回值
No. | 返回值 | 含义 |
---|---|---|
1 | -1 | 失败 |
2 | 0 | 子进程逻辑控制流 |
3 | 其他(子进程PID) | 父进程逻辑控制流 |
-
特点
- 调用一次,返回两次
- 相同但是独立的地址空间
- 并发执行
- 共享文件
-
示例
注意:fock之后调用一次,返回两次;
返回两个不同的进程pid_t id = fork();
(1)创建新的进程,后面代码在两个进程中都会执行,各执行各自的命令(两个纬度)
(2)仍存在联系:父子id的关系;
- 完整代码
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
pid_t pid = getpid();
pid_t ppid = getppid();
cout<< getpid()<< "\t"<< getppid()<<endl;
pid_t id = fork(); //(1)创建新的进程,后面代码在两个进程中都会执行,各执行各自的命令(两个纬度) (2)仍存在联系:父子id的关系;
pid = getpid();
ppid = getppid();
if(-1 == id){return 1;}
if(0 == id){
cout << pid << "\t" << ppid << endl;
}else{
cout << pid << "\t" << ppid << endl;
}
}
注意进程:(3)并行的顺序,没有先后顺序;
(4)地址一样,变量不共享,个用各的虚拟空间;
- 完整案例
#include <iostream>
#include <cstdio>
#include <unistd.h>
using namespace std;
int main(){
cout << "pid:" <<getpid()<<"\t"<< "ppid:"<<getppid()<<endl;
int n = 5;
pid_t id = fork();
//(1)并行的顺序,没有先后顺序;
//(2)地址一样,变量不共享,个用各的虚拟空间;
switch(id){
case -1: perror("fork error");
break;
case 0:
for(int i = 0; i < 5;++i){
cout << "CHILD pid:" <<getpid()<<"\t"<< "ppid:"<<getppid()<<endl;
}
break;
default:
for(int i = 0; i < 5;++i){
cout << "PARENT pid:" <<getpid()<<"\t"<< "ppid:"<<getppid()<<endl;
}
}
}
- 共享文件+gdb的使用
- 完整案例
#include <iostream>
#include <fstream>
#include <cstdio>
#include <unistd.h>
using namespace std;
int main(){
cout << "pid:" <<getpid()<<"\t"<< "ppid:"<<getppid()<<endl;
int n = 5;
pid_t id = fork();
ofstream fout("test");
//(1)并行的顺序,没有先后顺序;
//(2)地址一样,变量不共享,个用各的虚拟空间;
switch(id){
case -1: perror("fork error");
break;
case 0:
for(int i = 0; i < 5;++i){
fout << "CHILD pid:" <<getpid()<<"\t"<< "ppid:"<<getppid()<<endl;
}
break;
default:
for(int i = 0; i < 5;++i){
fout << "PARENT pid:" <<getpid()<<"\t"<< "ppid:"<<getppid()<<endl;
}
}
}
(2)gdb 的使用
a.先进行gdb的编译
g++ 003_fock.cpp -g
b. 执行文件
./a.out
c.进入tui界面,然后按空格键
gdb ./a.out -tui
d.常见操作
No. | cmd | 含义 |
---|---|---|
1 | run | 开始执行 |
2 | q | 退出 |
3 | n或者空格键) | 接下来 |
4 | c | 继续 |
5 | b 10 | 设置断点在第10行 |
6 | info breakpoints | 查看变量 |
7 | set follow-fork-mode child | 查看子进程 |
8 | set follow-fork-mode parent | 查看父进程 |
- 本质
复制+分叉
No. | 概念 | 状态 | 硬件 | 特点 |
---|---|---|---|---|
1 | 并发(concurrency) | 两个或者多个进程在同时存在 | 单核 | 进程指令同时或者交错执行。 |
2 | 并行(parallellism) | 两个或者多个进程在同时执行 | 多核 | 一种特殊的并发 |
5.2.2 执行函数exec()
- 分类
No. | 分类 | 函数 |
---|---|---|
1 | 字符串数组参数 | execv()、execvp()、execve() |
2 | 可变参数 | execle()、execlp()、execl() |
- exec函数组名字规律
No. | 字符 | 含义 |
---|---|---|
1 | v | 第二个参数是数组 |
2 | l | 第二个参数之后是变参 |
3 | p | 第一个参数是文件名 |
4 | e | 最后一个参数是环境变量 |
- 返回值
No. | 返回值 | 含义 |
---|---|---|
1 | -1 | 失败 |
2 | 不返回 | 成功 |
- 特点
- 一次调用,失败返回,仅仅执行一次,PID不变,可以借助父子进程进行循环;
- 改朝换代,取而代之
-PID不变 - 地址空间内容变化
(1)//exec v 第二个参数是数组 | 第二个参数是变参
//p 第一个参数是文件名 e 最后一个参数是环境变量
(2)数组最后一个元素一定是NULL;
const char* const cmd[] ={"ps","-o", "pid,ppid,s,cmd,time,tty",NULL};
(3)环境变量的一个全局变量
execve("bin/ps",const_cast<char* const*>(cmd),environ);
//(2)环境变量的一个全局变量
(4)最好带路径,如果是p结尾的,可以不用;
(5)仅仅失败的时候返回-1,成功的时候不返回;
if(-1 == res){
perror("exec error");
}else{
cout << "success" << endl;
}
- 完整案例
#include <iostream>
#include <unistd.h>//environ
#include <fstream>
using namespace std;
//execv() execvp() execve()
//execl() execlp() execle()
//
//exec v 第二个参数是数组 | 第二个参数是变参
//p 第一个参数是文件名 e 最后一个参数是环境变量
//
int main(){
const char* const cmd[] ={"ps","-o", "pid,ppid,s,cmd,time,tty",NULL};
//(1)数组最后一个元素一定是NULL;
//execv("bin/ps",const_cast<char* const*>(cmd));
//execvp("ps",const_cast<char* const*>(cmd));
//execve("bin/ps",const_cast<char* const*>(cmd),environ);
//(2)环境变量的一个全局变量
// execl("bin/ps","ps","-o","ppid,pid,s,cmd",NULL);
// execlp("bin/ps","ps","-o","ppid,pid,s,cmd",NULL);
// execle("bin/ps","ps","-o","ppid,pid,s,cmd",NULL,environ);
// execlp("size","size","a.out",NULL);
//(3)最好带路径,如果是p结尾的,可以不用;
int res = execlp("size","size","a.out",NULL);
//(4)仅仅失败的时候返回-1,成功的时候不返回;
if(-1 == res){
perror("exec error");
}else{
cout << "success" << endl;
}
}
- 本质
覆盖程序
5.2.3 系统函数int system(Shell字符串)
注意(1)头文件
#include <cstdlib>
(2)可以理解为借助shell将进程显示出来;和shell有父子关系;
- 完整案例
#include <iostream>
#include <cstdlib>
using namespace std;
int main(){
system("ps -ef");
}
- 返回值
No. | 返回值 | 含义 |
---|---|---|
1 | -1 | 失败 |
2 | 127 | 无法启动shell来运行 |
3 | 其他 | 命令退出码 |
-
特点
一次调用,一次返回 -
本质
shell执行命令/程序
Linux系统可以创建多少个进程?使用ulimit -a可以查看到。可以通过ulimit -u 进程数修改。
更详细的设置可以在/etc/security/limits.conf修改。
6. 如何结束进程
No. | 方式 | 说明 | 定义的os |
---|---|---|---|
1 | main函数退出 | 只能用在main函数内 | windows/linux |
2 | 调用exit()函数 | 一般用在main函数以外的函数 | windows/linux |
3 | 调用_exit()函数 | 一般用来结束子进程 | linux |
4 | 调用abort()函数 | 一般用来异常退出 | windows/linux |
5 | 信号终止 | 终止其他进程 |
(1)return 0 正确退出;
return 1 错误退出;
宏定义下的退出
return EXIT_SUCCESS;//成功;
return EXIT_FAILURE;//失败
- 完整案例
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;
void Func(){
//在函数中结束进程;
exit(EXIT_SUCCESS);
_exit(EXIT_SUCCESS);
abort(); //异常退出;
}
int main(){
//在main()函数使用return 结束进程;
return EXIT_SUCCESS;//成功;
return EXIT_FAILURE;//失败
exit(EXIT_SUCCESS);
}
exit()/abort()退出
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
//exit(EXIT_FAILURE);
abort();
}
7. 如何停止进程
7.1 休眠
int sleep(unsigned int secs)
- 参数
secs指定休眠的秒数,-1表示永久休眠 - 返回值
未休眠的秒数 - 特性
如果没有信号中断,休眠指定秒数返回0,否则马上返回未休眠的秒数。
(1)在linux定义,#include <uxistd.h>
(2)相当于定时器,时间为s;
- 完整案例
#include <unistd.h>
#include <iostream>
using namespace std;
int main(){
for(int i=0;i <10;++i){
sleep(1);
cout <<i << endl;1
}
}
示例1:电子时钟
(1)隔1s进行打印,并换行
cout << str << endl;
sleep(1);
(2)间隔5s,微妙级别;
'\r’进行打印,回车,不换行;
flush(刷新,仅仅显示每次循环的最后一行)
cout << '\r' << str << flush;
usleep(5000000);
(3)strftime()的使用规则,
参数1:字符串,参数2:长度,参数三:格式,参数4:当前时间的指针;
strftime(str,sizeof(str),"%Y/%m/%d %T",ptm);
- 完整案例
#include <iostream>
#include <unistd.h>
#include <ctime>
using namespace std;
int main(){
while(true){
time_t t = time(NULL);
tm* ptm = localtime(&t);
//yy,mm,dd,hh:mm:ss
char str[20] = {0};
//(3)strftime()的使用规则,
//参数1:字符串,参数2:长度,参数三:格式,参数4:当前时间的指针;
strftime(str,sizeof(str),"%Y/%m/%d %T",ptm);
//(1)隔1s进行打印,并换行
//cout << str << endl;
//sleep(1);
//(2)间隔5s,微妙级别;
//'\r'进行打印,回车,不换行;
//flush(刷新,仅仅显示每次循环的最后一行)
cout << '\r' << str << flush;
usleep(5000000);
}
}
示例2:进度条
注意:打印完具体的符号之后刷新;
即最外层循环的里面;
(保证数字和符号一起刷新)
- 完整代码
#include <iostream>
#include <unistd.h>
#include <iomanip>
using namespace std;
int main(){
for(int i=0;i<=100;++i){
cout<<'\r'<<setw(3)<<i<<'%';
for(int j=0;j<=i;++j){
cout<<'=';
}
cout<<flush;
usleep(1000000);
}
cout<< endl;
}
小型的shell 脚本的编辑(实战)
(1)从终端读取一行
string line;
getline(cin,line);
(2)把命令行按照空白符分割
istringstream iss(line);
vector<string> cmd;
string option;
while(iss >> option){
cmd.push_back(option);
}
- 完整案例
#include <iostream>
#include <sstream>
#include <vector>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
using namespace std;
int main(){
for(;;){
// 从终端读取一行
string line;
getline(cin,line);
// 把命令行按照空白符分割
istringstream iss(line);
vector<string> cmd;
string option;
while(iss >> option){
cmd.push_back(option);
}
for(auto c:cmd) cout << c << endl;
const char* opts[cmd.size()+1]={NULL};
int i=0;
for(auto& c:cmd) opts[i++] = c.c_str();
if(0 == fork()){
if(-1==execvp(opts[0],const_cast<char* const*>(opts))){
perror("execute cmd error");
}
}
}
}
7.2 暂停
int pause()
- 返回值
总是-1 - 特性
- 如果程序没有处理信号,直接中断,执行默认信号处理,程序后续代码不再执行。
- 如果程序存在信号处理,执行信号处理后,执行后续代码。
- 等待信号
No. | 快捷键 | 信号 | 说明 |
---|---|---|---|
1 | Ctrl+C | SIGINT | 中断 |
2 | Ctrl+Z | SIGTSTP | 终端的停止信号 |
- 示例
中断(避免僵尸进程)程序控制而非系统控制
A. ctrl+c
(1)回调函数:捕获ctrl+c的信息,暂停,系统不做处理(阻塞状态);
void SignalFunc(int sig){
cout << "\nSIG:" << sig << endl;
exit(EXIT_FAILURE); //(1)捕获ctrl+c的信息,暂停,系统不做处理(阻塞状态);
}
(2)捕获ctrl+c的信息
signal(SIGINT,SignalFunc); //(2)捕获ctrl+c的信息
}
- 完整案例
#include <iostream>
#include <unistd.h> // sleep()
#include <signal.h> // signal() SIGINT
#include <cstdlib>
using namespace std;
void SignalFunc(int sig){
cout << "\nSIG:" << sig << endl;
exit(EXIT_FAILURE); //(1)捕获ctrl+c的信息,暂停,系统不做处理(阻塞状态);
}
int main(){
signal(SIGINT,SignalFunc); //(2)捕获ctrl+c的信息
}
while(true){
sleep(1);
}
}
B. ctrl+z
(1)回调函数:捕获ctrl+z的信息,暂停,系统不做处理(阻塞状态);
void SignalFunc(int sig){
cout << "\nSIG:" << sig << endl;
exit(EXIT_FAILURE); //(1)捕获ctrl+z的信息,暂停,系统不做处理(阻塞状态);
}
(2)捕获ctrl+z的信息
signal(SIGSTOP,SignalFunc); //(2)捕获ctrl+c的信息
}
- 完整案例
#include <iostream>
#include <unistd.h> // sleep()
#include <signal.h> // signal() SIGINT
#include <cstdlib>
using namespace std;
void SignalFunc(int sig){
cout << "\nSIG:" << sig << endl;//ctrl打入后台的捕获;(系统暂时不做处理,程序中的宏定义处理)
exit(EXIT_FAILURE);
}
int main(){
signal(SIGSTOP,SignalFunc);
while(true){
sleep(1);
}
}
C. pause()
暂停进程进入阻塞状态,等待信号
pause();// 暂停进程进入阻塞状态,等待信号
- 完整案例
#include <iostream>
#include <unistd.h> // sleep()
#include <signal.h> // signal() SIGINT
#include <cstdlib>
using namespace std;
void SignalFunc(int sig){
cout << "\nSIG:" << sig << endl;
// exit(EXIT_FAILURE);
}
int main(){
signal(SIGINT,SignalFunc);
cout << "before pause" << endl;
pause();// 暂停进程进入阻塞状态,等待信号
cout << "after pause" << endl;
}
7.3 等待
-
pid_t wait(int* status):等价pid_t waitpid(-1,stauts,0)
-
pid_t waitpid(pid_t pid,int * status,int options)
-
参数
No. | 参数 | 含义 | 说明 |
---|---|---|---|
1 | pid | 等待的进程 | <-1:等待进程组为pid的所有进程;-1: 等待任何子进程;0:等待同组的进程;>0:进程为pid 的子进程 |
2 | status | 子进程结束状态 | 判断正常结束:使用WIFEXITED(status);判断异常结束使用WIFSIGNALED(status);判断暂停使用WIFSTOPPED(status) |
3 | options | 选项 | WNOHANG若子进程没有结束,返回0,不予以等待;若子进程结束,返回该子进程的ID。WUNTRACED若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。 |
- 返回值
No. | 返回值 | 含义 |
---|---|---|
1 | -1 | 失败 |
2 | 其他 | 等待的PID |
- 正常结束:WIFEXITED(status)
No. | 参数 | 含义 |
---|---|---|
1 | 非0 | 正常结束子进程 |
2 | 0 | 非正常结束子进程 |
WEXITSTATUS(status)取得子进程exit()返回的结束代码
一般会先用WIFEXITED来判断是否正常结束才能使用此宏
- 异常结束:WIFSIGNALED(status)
No. | 参数 | 含义 |
---|---|---|
1 | 非0 | 异常结束子进程 |
2 | 0 | 非异常结束子进程 |
WTERMSIG(status)取得子进程因信号而中止的信号代码
一般会先用 WIFSIGNALED 来判断后才使用此宏
- 暂停:WIFSTOPPED(status)
No. | 参数 | 含义 |
---|---|---|
1 | 非0 | 暂停结束子进程 |
2 | 0 | 非暂停结束子进程 |
WSTOPSIG(status)取得引发子进程暂停的信号代码
一般会先用 WIFSTOPPED 来判断后才使用此宏。
- 示例
父进程等待子进程退出
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
sleep(2);
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
//exit(0);
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
printf("pid:%d exit\n",waitpid(pid,NULL,0));
}
}
更加安全的方式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
void handler(int sig){
int status;
pid_t cpid = wait(&status);
if(WIFEXITED(status)){
printf("child exit by %d\n",WEXITSTATUS(status));
}
if(WIFSIGNALED(status)){
printf("child exit by signal %d\n",WTERMSIG(status));
}
printf("child %d exit\n",cpid);
}
int main(){
signal(SIGCHLD,handler);
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
sleep(2);
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
//exit(0);
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
printf("leave:%d\n",sleep(5));
//exit(200);
}
for(;;);
}
如果不关心退出的情况。
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
void handler(int sig){
pid_t cpid = wait(NULL);
printf("child %d exit",cpid);
}
int main(){
signal(SIGCHLD,handler);
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
sleep(2);
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}
}
进程的终老、他杀与自杀
- 如何kill多个进程?kill -9 PID PID…
- 如何按名字kill进程?pkill 进程名 \ killall 进程名 \ kill -9 $(pidof 进程名)
7.4 特殊进程
No. | 概念 | 出现条件 | 导致结果 | 是否有害 |
---|---|---|---|---|
1 | 孤儿进程 | 父进程先于子进程退出 | init进程作为新的父进程 | 无害 |
2 | 僵尸进程 | 子进程退出,父进程没有获取子进程的状态信息 | 调用wait或waitpid | 有害,避免出现僵尸进程 |
(1)僵尸进程
a.以defunct结尾
b.kill不能删除
c。删除的方柿:重启;关掉父进程; pkill+进程名字
d.最重要的杀掉僵尸进程的方式(wait)回收资源
即:将signal和wait连用
(1)头文件匹配
#include <unistd.h> //(1): fork() getpid() getppid()
#include <signal.h> // signal()
#include <wait.h> // wait()
(2)wait()回守资源,避免僵尸进程;注释则会出现;
void Func(int sig){
pid_t id = wait(NULL);//(1)回守资源,避免僵尸进程;注释则会出现;
cout << "child " << id << "exit" << endl;
}
(3)回调函数
孤儿进程:父进程退出,子进程没有退出,仍然运行,无害;
僵尸进程:子进程退出,父进程没有退出并且父进程没有wait子进程
signal(SIGCHLD,Func); //回调函数
// (2)孤儿进程:父进程退出,子进程没有退出,仍然运行,无害;
// (3) 僵尸进程:子进程退出,父进程没有退出并且父进程没有wait子进程
- 完整案例
#include <iostream>
#include <unistd.h> //(1)头文件匹配: fork() getpid() getppid()
#include <signal.h> // signal()
#include <wait.h> // wait()
using namespace std;
void Func(int sig){
pid_t id = wait(NULL);//(1)回守资源,避免僵尸进程;注释则会出现;
cout << "child " << id << "exit" << endl;
}
int main(){
signal(SIGCHLD,Func);
// (2)孤儿进程:父进程退出,子进程没有退出,仍然运行,无害;
// (3) 僵尸进程:子进程退出,父进程没有退出并且父进程没有wait子进程
if(0==fork()){
cout << "PID:" << getpid() << "\tPPID:" << getppid() << endl;
sleep(5);
}else{
cout << "PID:" << getpid() << "\tPPID:" << getppid() << endl;
//pause();
while(true) sleep(1);
}
}
案例2:完整代码 tinyShell.cpp
#include <iostream>
#include <sstream>
#include <vector>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
using namespace std;
void handle(int sig){
wait(NULL); //回首子进程的资源;
}
int main(){
signal(SIGCHLD,handle);
for(;;){
// 从终端读取一行
string line;
getline(cin,line);
// 把命令行按照空白符分割
istringstream iss(line);
vector<string> cmd;
string option;
while(iss >> option){
cmd.push_back(option);
}
// for(auto c:cmd) cout << c << endl;
const char* opts[cmd.size()+1] = {NULL};
int i=0;
for(auto& c:cmd) opts[i++] = c.c_str();
if(0 == fork()){
if(-1==execvp(opts[0],const_cast<char* const*>(opts))){
perror("execute cmd error");
}
}
}
}