一、进程的基本概念
1、进程和程序
程序是存储在磁盘上的可执行文件,当程序被加载到内存中开始运行时就被称为进程,一个程序可以被多次加载为很多个进程,进程就是处于活动状态的程序。
2、进程的分类
进程一般分为三个种类:交互进程、批处理进程、守护进程。
3、查看进程
想要在linux系统查看进程可以使用ps命令查看当前用户的进程的简单信息,也可以采用ps -auxw查看进程的详细信息,具体实现效果如下:
USER 进程的属主用户名
PID 进程号
%CPU CPU的使用率
%MEM 内存的使用率
VSZ 虚拟内存使用的字节数
RSS 物理内存使用的字节数
TTY 终端设备号 ? 表示无终端控制
STAT 进程的状态
O 就绪态 等待被调用
R 运行态,Linux系统没有O,就绪也用R表示
S 可被唤醒的睡眠态,如系统中断、获取资源、收到信号等都可以唤醒进入运行态
D 不可被唤醒的睡眠态,只能被系统唤醒
T 暂停态 收到SIGTSTP信号进入暂停态,收到SIGCONT信号转回运行态
X 死亡态
Z 僵尸态(遇到过z+的状态,+表示处于前台)
N 低优先级
< 高优先级
l 多线程进程
s 进程的领导者
START 进程的启动时间
TIME 进程运行时间
COMMAND 启动进程的命令
4、父子进程、僵尸进程、孤儿进程
一个进程可以被另一个进程创建,创建者叫父进程,被创建者被称为子进程。子进程在被父进程创建后会在操作系统的调度下同时进行。而当子进程先于父进程结束,子进程会向父进程发送SIGCHILD信号,父进程应当去回收子进程的相关资源。
孤儿进程:父进程先于子进程结束,子进程就变成了孤儿进程,孤儿进程会被init守护进程“收养”,init守护进程就变为此进程的父进程。
僵尸进程:进程已经死亡,但是它的父进程没有立即回收它的资源,改进程就会变成僵尸态。
5、进程标识符
每个进程都有一个用非负整数表示的唯一标识,也就是进程ID,也叫PID。进程ID在任意时刻都是唯一的,但是可以被重用,进程一旦结束,它的进程ID就会被回收,一段时间后重新分配其他新创建的进程。
二、进程的创建
1、fork和vfork
fork和vfork的具体使用方法本文不会详述,只介绍一下二者的特点和区别,其原型分别是:
pid_t fork(void);
pid_t vfork(void);
2、fork的特点
fork的返回值比较特殊,对它一次调用会有两个返回值,子进程返回0,父进程返回子进程的PID,而当创建失败时返回-1。我们可以根据不同的返回值使父子进程进入不同的分支执行不同的代码。
fork的写时复制机制:通过fork创建的子进程会拷贝父进程的数据段、BSS段、堆、栈、I/O缓冲区,但是这些部分不是一开始就拷贝的,在初始时子进程会和父进程共享这些资源,当其试图修改这些资源时,系统会复制相应的数据,使子进程拿到相应的副本。这样可以极大的减少不必要的资源复制。
子进程会共享父进程的代码段,还有信号处理方式以及文件描述符。
3、vfork的特点
vfork和fork的区别在于,vfork是通过加载可执行文件的方式来创建子进程,它的子进程返回0,父进程返回子进程的PID。
vfork不像fork那样遵循写时复制,它创建的父子进程共享相同的地址空间,子进程会直接使用父进程的内存来节省资源。也是由于不进行复制内存,vfork创建进程的速度更快。在使用vfork后,直到子进程调用exec()或者exit()之前,父进程都处于阻塞状态,也就是说vfork创建的子进程一定先返回。
通过vfork、exec系列函数加载的子进程不会继承父进程的信号处理函数,但是会继承父进程的信号屏蔽集。
三、进程的退出
1、通过main函数正常退出
当主线程执行return n时,该返回值可以被父进程获取,标识子进程的退出状态。等价于调用exit(n)。
2、调用exit函数
当进程调用exit(int status)时,进程会正常退出,且可以在任意地方调用,都会立刻结束该进程,status一般常用EXIT_SUCCESS和EXIT_FAILURE。
退出前的步骤:
-
调用注册的函数:在进程退出前,会依次调用通过
atexit
或on_exit
注册的函数,这些函数按照相反的顺序执行。 -
冲刷和关闭标准I/O流:这一步确保所有缓冲的数据被写入相关文件,并关闭标准输入、输出和错误流。
-
调用
_exit
或_Exit
函数:最终会调用底层的_exit
或_Exit
函数,这两者直接结束进程。
3、调用_exit和_Exit函数
_exit是系统提供的退出函数,_Exit是标准库提供的一个函数,二者都用于立即结束进程。它们的主要作用有:
1、传入的状态码会被父进程接收
2、关闭所有的文件描述符
3、向父进程发送SIGCHILD信号
4、不会返回
4、最后一个线程执行返回语句
如果一个线程执行了return语句,那么这个线程就会结束,进程的状态取决于线程返回的状态码。
5、最后一个线程执行了pthread_exit函数
-
pthread_exit(void *retval)
是一个专门用于线程退出的函数。 -
当线程调用
pthread_exit
时,它会:-
结束该线程的执行,但不影响其他线程或整个进程。
-
使得其他线程可以通过
pthread_join
获取其返回值retval
。 -
如果最终唯一存活的线程(主线程)调用
pthread_exit
,并且没有其他线程存活,则整个进程会终止。
-
以上是五种退出进程的方法,可以选择使用。
四、总结
Linux进程管理机制通过分离进程的创建、调度及退出过程,确保了系统的高效运行和资源的合理分配。理解进程之间的关系及其生命周期对系统编程和故障排查尤为重要。