进程
一个正在进行的程序
并行与并发
并行:执行的程序在不同CPU上同时执行
并发:一个CPU,多个进程交替执行,因为交替速度很快,所以从宏观上来看是同时执行的,但是从围观的角度是交替执行的
单道与多道
单道程序设计:所有进程一个一个排队执行,若A阻塞,B只能等待,,即使CPU处于空闲状态
多道程序设计:在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行
进程控制块(PCB)
进程运行时,内核为每个进程分配一个PCB(进程控制块),维护进程相关的信息,linux内核的进程控制块是task_struct结构体
task_struct结构体
在 /usr/src/linux-headers-xxx/include/linux/sched.h 文件中可以查看
task_struct 结构体定义其内部成员有很多
如:
进程id:语言使用pid_t的类型表示,其实就是一个非负整数
进程的状态:有就绪,运行,挂起,停止等状态
PCB存储位置
在内核中
进程号
当前进程的id就是进程号,由系统分配
每个进程都由一个进程号来标识,其类型为pid_t,进程号的范围:0~32767
进程号总是唯一的,但进程号可以重用,当一个进程终止后,其进程号就可以再次使用了
进程号为0和1的进程由内核创建
进程号为0的进程通常是调度进程,常被称为交换进程,进程号为1的进程通常是init进程
除调度进程(0号进程)外,在linux下面所有的进程都是由init(1号进程)进程直接或间接创建的
进程号(PID)标识进程一个非负整型数
对应函数:
pid_t getpid() 获取当前进程的进程号
进程组号(PGID)
多个进程在同一个组中,该组就是进程组
该组中第一个进程为组长进程,该组长进程的进程id就是该进程组id
由进程A开启的子进程默认进程A为同组进程,此时A就是组长进程
对应函数;
pid_t getpgid(int pid) 获取指定进程所在的进程组号
父进程号(PPID)
由进程A开启进程B,此时进程A就是进程B的父进程,进程B就是进程A的子进程
对应函数:
pid_t getppid() 获取当前进程的父进程号
当进程A比进程B先销毁时,此时进程B就是孤儿进程,由1进程充当其父进程
扩展
命令:ps
作用:查看进程状态
参数:
-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-w 显示加宽,以便显示更多的信息
-r 只显示正在运行的进程
ps -aux:显示当前用户在运行到的进程信息
ps -ajx:显示正在运行的相关联进程信息(包含父进程id(ppid),组id(pdid))
显示信息中STAT参数含义
D 不可中断 Uninterruptible(usually IO)
R 正在运行,或在队列中的进程
S(大写 ) 处于休眠状态
T 停止或被追踪
Z 僵尸进程
W 进入内存交换(从内核 2.6 开始无效)
X 死掉的进程
< 高优先级
N 低优先级
s 包含子进程
l 表示线程加锁
- 位于前台的进程组
fork函数
作用:创建进程
语法:
pid_t fork();
返回值:
如果在子进程中就是0,如果在父进程中就是创建的子进程id,-1创建失败
fork失败的两个主要原因是;
1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN
2)系统内存不足,这时errno的值被设置为ENOM
注意:
1,子进程会从fork函数后开始执行
2,子进程共享父进程所有内容,不包括进程号,父进程号等
使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间
地址空间:包括进程上下文,进程堆栈,打开的文件描述符,信号控制设定,进程优先级,进程组号等
子进程所独有的只有它的进程号,计时器等
此外,使用fork函数的代价是很大的
3,谁创建谁回收
进程状态
三状态:就绪态 执行态 等待态
五状态:新建态 终止态 运行态 就绪态 阻塞态
注意:
阻塞态在其他语言中进行细分又可以分为阻塞态与休眠态
休眠态又可以分为有限期休眠与无限期休眠
休眠状态下不会抢夺CPU执行权
进程资源的回收
每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存资源等,但是任然为其保留一定的信息,这些信息主要指的是进程控制块的信息(包括进程号,退出状态,运行时间等);
回收原则:谁创建谁回收(父进程回收子进程资源)
wait函数
作用:回收进程资源
头文件:
#include <sys/types.h>
#include <sys/wait.h>
语法:
pid_t wait(int *status);//阻塞
功能:
等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源
参数:
status:进程退出时的状态信息
返回值:
成功:已经结束子进程的进程号
失败:-1
注意:
1,会阻塞当前进程,知道回收一个子进程
2,因为谁创建谁回收的原则,所以该函数在父进程中调用
exit函数与_exit函数
作用:退出当前进程
exit函数与_exit函数的区别?
exit函数属于库函数,_exit函数属于系统调用
exit函数所需头文件#include<stdlib.h>,_exit函数所需头文件#include<unistd.h>
exit函数为 void exit(int status) ,_exit函数 void -exit(int status);
两者参数一致:
退出状态,0,正常退出,非0异常退出
注意:
退出状态码取值区间:0~255
WIFEXITED(status)与WEXITSTATUS(status)
作用:
WIFEXITED:获取进程退出时状态
WEXITSTSTUS:获取进程退出时状态码
语法:
WIFEXITED(status)
WEXITSTATUS(status)
waitpid函数
作用:回收进程
头文件:
#include<sys/types.h>
#include<sys/wait.h>
语法:
pid_t waitpid(pid_t pid,int *status,int options);
功能:
等待子进程终止,如果子进程终止了,此函数会回收子进程的资源
参数:
pid: 参数 pid 的值有以下几种类型:
pid>0 等待进程ID等于pid的子进程
pid=0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会等待它
pid=-1等待任一此时waitpid和wait作用一样
pid<-1 等待指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值
status:进程退出时的状态信息,和wait()用法一样
option:option提供了一些额外的选项来控制waitpid()
0:同wait(),阻塞父进程,等待子进程退出
WNOHANG:没有任何已经结束的子进程,则立即返回(非阻塞)
WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态
返回值:
1,当正常返回的时候,waitpid()返回收集到的已经回收子进程的进程号
2,如果设置了选项WNOHANG,而调用中waitpid()还有子进程在运行,且没有子进程退出,返回0,父进程的所有子进程都已经退出了返回-1,返回>0表示等到一个子进程退出
3,如果调用中出错,则返回-1,这时error会被设置成相应的值以指示错误所在
atexit函数
作用:进程在退出前可以用atexit函数注册退出处理函数
注意:
1,一个进程可以登记最多32个函数,这些函数将由exit自动调用,我们称这些函数为终止处理程序,用atexit函数来登记这些函数
2,以登记这些函数的相反顺序调用他们,同一函数如登记多次,则也会被调用多次
3,执行注册的函数依据是当前子进程
语法:
int atexit (void (*__func)(void))
僵尸进程
子进程退出,父进程没有回收子进程资源,子进程为僵尸进程(有害)
子进程的PID被占用,系统的PID是有数量限制
孤儿进程
父进程先结束,子进程为孤儿进程(无害)
孤儿进程被1号进程接管(当孤儿进程结束时,1号进程负责回收其资源)
守护进程
守护进程是在后台独立运行、脱离控制终端且周期性执行任务或等待处理事件的特殊进程,于系统启动时自动开启并持续至系统关闭,它在系统服务支持(如 httpd 提供 Web 服务)、资源管理(如 syslogd 收集日志)、任务调度(如 cron 按计划执行命令)等多方面发挥关键作用,在 Linux 中创建时需先通过 fork () 创建子进程,父进程退出后子进程进一步执行后续设置步骤以成为守护进程并稳定运行
终端
终端:是与计算机系统相连的一种输入输出设备
在UNIX系统中,用户通过终端登录后得到一个shell进程,这个终端成为shell进程的控制终端,进程中,控制终端是保存在PCB中的信息,而fork会复制PCB中的信息,因此由shell进程启动的其他进程的控制终端也是这个终端
作用 : 获取当前进程所属终端名称
#include <unistd.h>
char *ttyname(int fd);
功能:由文件描述符查出对应的文件名
参数: fd: 文件描述符
返回值:
成功:终端名
失败: NULL
进程组
代表一个或多个进程的集合
每个进程都有对应的进程组
进程组ID为当前进程中的第一进程ID
如果一个进程ID和组ID相同,那么这个进程就是组长进程
当父进程创建子进程的时候,默认子进程与父进程属于同一进程组
shell进程启动的进程独立为一个进程组
如果进程中只是组长进程结束,当前进程组不会解散,只有进程组的所有进程离开(终止或转移),该进程组才会解散
一个进程可以为自己或子进程设置进程组ID
注意:组长进程不能设置进程组id
如果进程ID==进程组ID==会话ID,那么该进程为会话首进程
获取所属进程组id
所属头文件
#include<unistd.h>
函数
pid_t getpgrp(void)
功能:
获取当前进程的进程组ID
参数:
无
返回值:
总是返回调用者的进程组ID
函数:
pid_t getpgid(pid_t pid);
功能:获取指定进程的进程组ID
参数:
pid:进程号,如果pid=0,那么该函数作用和getpgrp一样
返回值:
成功:进程组ID
失败:-1
设置进程组
函数:
int setpgid(pid_t pid,pid_t pgid)
功能:
改变进程默认所属的进程组,通常可以用来加入一个现有的进程组或创建一个新进程组
参数:
将参1对应的进程,加入参2对应的进程组中
返回值:
成功:0
失败:-1
会话
会话是一个或多个进程组的集合
一个会话可以有一个控制终端,这通常是终端设备或伪终端设备,建立与控制终端连接的会话首进程被称为控制进程
一个会话中的几个进程组可以一个前台进程组以及一个或多个后台进程组
如果一个会话有一个控制终端,则它有一个前台进程组,其他进程组为后台进程组
如果终端接口检测到断开连接,则将挂断信号发送至控制进程(会话首进程)
函数getsid
作用:获取会话id
所需头文件
#include<unistd.h>
函数:
pid_t getsid(pid_t pid);
参数:
pid: 进程号,pid为0表示查看当前进程 session ID(会话id)
返回值:
成功:返回调用进程的会话ID
失败:-1
组长进程不能成为新会话的首进程,新会话首进程必定会成为组长进程
函数setsid
作用:创建会话
所需头文件:
#include <unistd.h>
函数:
pid_t setsid(void);
功能:
创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID,调用了setsid函数的进程,既是新的会长,也是新的组长
参数:
无
返回值:
成功:返回调用进程的会话ID
失败:-1
注意:
1,组长进程不能设置为会话
2,需要root权限(ubuntu不需要)
3,新会话丢弃原有的控制终端,该会话没有控制设备