Linux系统编程5:进程

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一个私有的地址空间独占存储器系统工厂
  • 本质
  1. 程序在地址空间中按照代码逻辑控制流执行
  2. 资源分配最小单位

3. 从代码到程序

在这里插入图片描述

4. 从程序到进程

  1. 内核将程序读入内存,为程序镜像分配内存空间。
  2. 内核为该进程分配进程标志符PID。
  3. 内核为该进程保存PID及相应的进程状态信息。
  4. 进程控制块(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详细资料

ELF文件格式浅析
ELF文件格式分析

4.3 进程状态

在这里插入图片描述

类似视频/音频播放器
No.状态含义
1就绪(Ready)进程已获得到除CPU以外的所有必要的资源,获得CPU立即执行
2运行(Running)程序正在CPU上执行
3阻塞(Blocked)等待某个事件发生而无法执行时,放弃CPU

4.4如何查看进程

No.OS命令e.g.
1Windowstasklisttasklist /FI “PID eq 进程PID”
2Linuxps / pstree / top一般结构/树结构/详细信息

4.5 ps命令

  • 查看某进程
  1. 通过进程PID查看:ps -p 进程PID
  2. 通过命令行查看:ps -C 命令行
  3. 查看程序的PID: pidof 程序名
  4. 查看 指定的列:ps -o pid,ppid,cmd,time,tty,s

在这里插入图片描述

  • 查看进程
No.风格命令属性说明
1BSD风格ps auxa: 终端上所有用户的进程;u:以用户为中心显示详细信息,x:无终端进程
2System V风格ps -efe:所有进程;f:树状显示
Unix从操作风格分为System V风格和BSD风格。它们在目录结构、脚本、命令行等方面存在一些差异。
System V, 曾经也被称为AT&T SystemV,是Unix操作系统众多版本中的一支。它最初由AT&T开发。
BSD(BerkeleySoftware Distribution,伯克利软件套件)是Unix的衍生系统,1970年代由伯克利加州大学(UniversityofCalifornia, Berkeley)开发。
随着一些并不基于这两者代码的UNIX实现的出现,例如Linux,这一归纳不再准确。像POSIX这样的标准化努力一直在试图减少各种实现之间的不同。
  • 列表示说明
No.标识含义
1USER用户
2PID进程ID
3%CPU进程占用的CPU百分比
4%MEM占用内存的百分比
5VSZ进程虚拟大小
6RSS常驻内存(内存中页的数量)
7TTY终端ID
8STAT进程状态
9START启动进程的时间
10TIME进程消耗CPU的时间
11COMMAND命令的名称和参数
  • 进程状态标识
No.标识含义
1D不可中断Uninterruptible(usually IO)
2R正在运行,或在队列中的进程
3S处于休眠状态
4T停止或被追踪
5Z僵尸进程
6W进入内存交换(从内核2.6开始无效)
7X死掉的进程
8<高优先级
9n低优先级
10s包含子进程
11+位于后台的进程组

4.6 pstree命令

以树状图的方式展现进程之间的派生关系

  • 安装
  yum install psmisc

4.7 top命令

实时显示系统中各个进程的资源占用,类似Windows任务管理器。

Linux一切皆文件,在/proc/下也可以查看到进程。
命令|含义|
–|--|–|
top|查看任务管理器
q|退出任务管理器

4.8 如何创建进程

No.OS命令
1Windows程序名
2Linux程序名

4.9 如何杀死进程

No.OS命令
1Windowstaskkill /F /PID 进程标识/taskkill /F /IM 程序名
2Linuxkill 进程标识PID
3ctrl+Z打入后台,暂停
4fg 文件名或者pid返回前台,重新执行
5fjobs查看后台程序
6ctrl+c停止进程

5. 进程操作接口

5.1 获取PID

进程标识pid:进程身份证号

No.函数接口
1pid_t getpid()获取当前进程ID
2pid_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失败
20子进程逻辑控制流
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含义
1run开始执行
2q退出
3n或者空格键)接下来
4c继续
5b 10设置断点在第10行
6info breakpoints查看变量
7set follow-fork-mode child查看子进程
8set follow-fork-mode parent查看父进程
  • 本质

复制+分叉

No.概念状态硬件特点
1并发(concurrency)两个或者多个进程在同时存在单核进程指令同时或者交错执行。
2并行(parallellism)两个或者多个进程在同时执行多核一种特殊的并发

5.2.2 执行函数exec()

  • 分类
No.分类函数
1字符串数组参数execv()、execvp()、execve()
2可变参数execle()、execlp()、execl()
  • exec函数组名字规律
No.字符含义
1v第二个参数是数组
2l第二个参数之后是变参
3p第一个参数是文件名
4e最后一个参数是环境变量
  • 返回值
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失败
2127无法启动shell来运行
3其他命令退出码
  • 特点
    一次调用,一次返回

  • 本质
    shell执行命令/程序

Linux系统可以创建多少个进程?使用ulimit -a可以查看到。可以通过ulimit -u 进程数修改。
更详细的设置可以在/etc/security/limits.conf修改。

6. 如何结束进程

No.方式说明定义的os
1main函数退出只能用在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
  • 特性
  1. 如果程序没有处理信号,直接中断,执行默认信号处理,程序后续代码不再执行。
  2. 如果程序存在信号处理,执行信号处理后,执行后续代码。
  • 等待信号
No.快捷键信号说明
1Ctrl+CSIGINT中断
2Ctrl+ZSIGTSTP终端的停止信号
  • 示例

中断(避免僵尸进程)程序控制而非系统控制

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.参数含义说明
1pid等待的进程<-1:等待进程组为pid的所有进程;-1: 等待任何子进程;0:等待同组的进程;>0:进程为pid 的子进程
2status子进程结束状态判断正常结束:使用WIFEXITED(status);判断异常结束使用WIFSIGNALED(status);判断暂停使用WIFSTOPPED(status)
3options选项WNOHANG若子进程没有结束,返回0,不予以等待;若子进程结束,返回该子进程的ID。WUNTRACED若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。
  • 返回值
No.返回值含义
1-1失败
2其他等待的PID
  • 正常结束:WIFEXITED(status)
No.参数含义
1非0正常结束子进程
20非正常结束子进程
WEXITSTATUS(status)取得子进程exit()返回的结束代码
一般会先用WIFEXITED来判断是否正常结束才能使用此宏
  • 异常结束:WIFSIGNALED(status)
No.参数含义
1非0异常结束子进程
20非异常结束子进程
WTERMSIG(status)取得子进程因信号而中止的信号代码
一般会先用 WIFSIGNALED 来判断后才使用此宏
  • 暂停:WIFSTOPPED(status)
No.参数含义
1非0暂停结束子进程
20非暂停结束子进程
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());
    }
}

进程的终老、他杀与自杀

  1. 如何kill多个进程?kill -9 PID PID…
  2. 如何按名字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");
        }
    }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值