title: 进程
date: 2019-08-02 09:04:14
tags: [进程,Linux]
categories: Linux
进程相关命令 | 解释 |
---|---|
ps –aux | 列出所有进程 |
ps -ef | 列出所有进程 |
kill | 发送一个特定的信号 (signal) 给 pid 的进程根据该信号而做特定的动作,若没有指定,预设是送出终止 (TERM) 的信号 |
kill -9 pid | 杀死pid号的进程 |
env | 查看环境变量 |
1、进程的概述
进程是程序的动态的执行过程。程序是静态的过程
程序: 存储在磁盘上的一堆指令。
进程: 将程序动态的运行起来,就转成进程,(即程序的执行过程)过程: 包含进程的创建,进程的运行以及进程消亡 。
2、进程的结构
Linux 系统是一个多进程的系统,它的进程之间具有互不干扰等行性、特点。也就是说,进程之间是分离的任务,拥有各自的权利和责任。其中,每个进程都运行在各自独立的虚拟地址 空间,因此,即使一个进程发生了异常,它也不会影响到系统的其他进程。
当创建一个进程,系统会分配0----4G 虚拟地址空间,其中0-----3G 是用户空间 3-----4G内核空间 。多个进程共用同一份内核, 各自进程的用户空间是独立的 。
空间的划分是由内核来决定,0—3G用户空间3—4 内核空间,这个划分是内核默认 。
代码段: 存放字符常量
数据段: 初始化后的全局变量
只读数据段: const 所修饰的变量
bss数据段: 未被初始化的全局变量
堆:程序员自己手动申请的空间 malloc -------- 小栈 大堆 思想 。
栈:存放函数内部所包含的局部变量以及函数。
3、进程的类型
分类 :
- 交互进程: shell 终端 就是交互进程 。
- 批处理进程: 通过 ps –aux 来进行查看到的进程 。
- 守护进程: 随系统的启动开始运行,系统关闭的时候,结束运行,在运行期间不会停止运行。在linux 操作系统下面 守护进程不受终端的控制 。
4、进程的状态
进程的执行是针对单核CPU来说明的,进程的执行需要CPU分配时间片。
进程的基本状态:就绪态、执行态、等待(睡眠阻塞)态
- 就绪态 :程序满足执行的条件 ,等待CPU分配时间片 。
- 执行态: CPU分配时间片给当前进程 ,进程正在占用CPU执行发任务的过程 。
- 等待态 :程序在执行过程中,不满足条件,而进行睡眠 状态
5、进程号
OS会为每个进程分配一个唯一的整型 ID,做为进程的标识号**(pid)**。进程 0 是调度进程,常被成为交换进程,它不执行任何程序,是内核的一部分,因此也被成为系统进程。进程除了自身的 ID 外,还有父进程 ID(ppid)。也就是说每个进程都必须有它的父进程,操作系统不会无缘无故产生一个新进程。
所有进程的祖先进程是同一个进程,它叫做init 进程, ID 为 1, init 进程是内核的第一个启动用户的进程。init 进程负责引导系统、启动守护(后台)进程并且运行必要的程序。它不是系统进程,但它以系统的超级用户特权运行。
①、进程的关闭
-
Ctrl+c
-
Ctrl+\
-
kill -9 进程号 //杀死进程
kill本身是没有杀死进程的功能,本质上是发送信号-9,-9才是真正意义上杀死进程的信号
②、获取当前/父进程的进程号
函数原型 | pid_t getpid(void); |
---|---|
功能 | 获取当前进程的进程号 |
返回值 | 成功: 获取的进程号 |
函数原型 | pid_t getppid(void); |
---|---|
功能 | 获当前进程的父进程的进程号 |
返回值 | 成功: 获取的进程号 |
6、创建一个进程
原型 | pid_t fork(void); |
---|---|
函数功能 | 创建一个子进程 |
返回值 | fork调用一次返回两次 父进程中返回子进程的ID号 子进程中返回0 |
案例:
fork在执行结束后子进程会拷贝父进程虚拟地址空间的0-3G空间的数据(其实不是所有情况下都拷贝)给子进程。3-4G的内核空间是共享的,内核空间的PCB中会多出子进程的PID号。fork结束后根据fork的返回值判断父进程和子进程分别执行哪些代码。此时父子进程是完全独立的。 |
读时共享、写时复制
上面说了,创建子进程后子进程会创建自己的虚拟地址空间,然后完全复制一份父进程的数据到自己的空间。其实实际中操作系统不是这样干的。而是运用读时共享、写时复制机制。
fork出子进程后,父进程的用户空间会映射到子进程的空间。当时需要修改某个变量的值即写操作时才会完全复制父进程的数据给子进程。如果子进程仅仅是读取某些数据,子进程是没有空间的只会通过这种映射方法共享空间。
好处:节省物理内存开销、节省复制所需的时间。
7、僵尸进程与孤儿进程
- 僵尸进程:子进程先于父进程退出,父进程退出时没有回收子进程的退出状态(task_struct),此时子进程就是僵尸进程。linux操作系统提供了相应的回收接口:wait/waitpid
- 孤儿进程 :父进程先于子进程退出,子进程会被init进程收养。此时子进程就是孤儿进程
8、退出函数
①、return
在主函数中:结束程序。在子函数中:结束子函数,还可以当做返回值来使用。
②、exti函数
函数原型 | void _exit(int status); void exit(int status); |
---|---|
功能 | 程序的退出 |
参数 | status:退出的状态 0:正常退出 非0:异常退出 |
区别:exit 与__exit都能能 结束程序,_exit 不会刷新缓冲区 ,exit 可以刷新缓冲区
9、wait/waitpid
函数原型 | pid_t wait(int *status); |
---|---|
功能 | 用来回收子进程的退出状态 |
参数 | status :子进程退出的状态,传输参数,该值记录的子进程退出原因 如果只管回收,不管程序的退出状态,status可以直接给NULL |
返回值 | 成功:要回收的子进程号 失败:-1 |
wait : 阻塞回收子进程的退出状态,只有回收到子进程,才会返回。否则一直
等待(睡眠)回收。
示例:
函数原型 | pid_t waitpid(pid_t pid, int *status, int options); |
---|---|
功能 | 功能: 回收子进程的退出状态 |
参数 | pid: <-1:回收指定进程组的内的任意子进程 -1: 最常使用:任意一个子进程 0 : 回收和当前调用waitpid一个组的所有子进程 >0 : 进程ID等于pid值的子进程。 status: 子进程退出的状态,传出参数,记录了子进程退出的原因。不关心可以给NULL options: WNOHANG:不管子进程有没有结束,都会立即返回一个结果。非阻塞模式的设置。 |
返回值 | 成功:如果设置WNOHANG,是非阻塞模式下没有回收到子进程的退出状态,会立即返回,返回值为0。直到回收到子进程的退出状态,返回值为非0值,这个非0值为 子进程进程号。 失败:-1 |
进程组 | 进程组,每个进程组有一个领头进程。进程组是一个或多个进程的集合,可以接受来自同一终端的各种信号。通常父进程和子进程在同一个进程组里面。如果子进程有创建了子进程。那么该子进程会另起一个组 |
示例:
阻塞模式:程序在执行过程中不满足条件,程序进入等待睡眠状态,直到条件满足,继续向下执行。 |
非阻塞模式:程序在执行过程中条件满足不满都会返回,继续往下执行 。 |
阻塞的模式效率高于非阻塞的效率,阻塞的时候程序进入 睡眠状态之后,这个过程不会占用cpu 的资源,非阻塞模式会一直空转,占用cpu 的资源 。
10、 Exec 函数族 environ
作用: 子进程调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行 ,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
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[]); |
参数: path:路径;argv[]:以数组的形式传递参数; envp [] :存储环境变量;…:可变参数
exec函数组的参数以NULL结尾
示例 1
示例 2
exec调用举例如下
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
//路径必须给全,参数列表形式给出
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
//不用给全路径值只需要给命令,他会去环境变量中找ps命令的路径,参数以列表形式给出
execv("/bin/ps", ps_argv);
//前面是路径,后面的参数放在指针数组中
execvp("ps", ps_argv);
//路径从环境变量中,参数放在指针数组中
//ps_envp环境变量指针数组,会改变当前程序的环境变量
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
11、 system 系统调用
system(const char *command); 功能: 执行指令 |
举例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
//int system(const char *command);
//system("clear");
system("ls");
return 0;
}
system是多个函数命令的集合体 :
运行过程 :1、先会调用fork 创建子进程 2、在子进程中 调用 execve 来执行要执行的指令 。3、调用wait 进程子进程的回收 。