进程
一、基本概念
1.进程与程序
程序:就是存储在磁盘上的文件,里面包含了一些可以执行的二进制指令和数据。
进程:就是运行着的程序(处于活动状态)
程序 | 进程 |
---|---|
程序是静态的 | 进程是动态的 |
1个程序可以对应多个进程 | 1个进程只能对应1个程序 |
程序是一组有序的指令集合 | 进程由程序、数据和进程控制块(PCB)三部分组成 |
程序是对于用户而言,也可以叫做软件 | 进程是对于程序员而言,是操作系统的概念 |
注意:在系统中只有进程没有程序
2.查看进程
1.简单形式:ps 以简略形式显示当前用户控制的终端下的进程。
2.以列表形式显示进程的详细信息:ps aux
-a 所有用户控制的终端进程
-u 以详细形式显示
-x 显示包括无终端控制的进程
3.进程详细信息表
USER:进程属主
PID:进程ID
%CPU:CPU占用率
%MEN:内核使用率
VSZ:占用虚拟内存大小
RSS:占用物理内存大小
TTY:终端设备号,?表示无终端控制的进程
STAT:进程的状态
O 就绪状态,等待被调用
R 运行状态,Linux系统没有就绪状态,就绪态就是运行态。
S 可被唤醒的睡眠状态,当系统中断、获得资源、收到信号都可以将它唤醒然后装入运行状态
D 只能被wake_up系统调用唤醒的睡眠状态
T 暂停状态,收到SIGSTOP(19)信号
X 死亡状态
Z 僵尸,已经停止,但父进程没有回收它的相关资源
START:进程启动时间
TIME:进程运行了多长时间
COMMAD:启动进程的指令(可执行文件的名字)
3.父子进程
每一个进程都有一个以非负整数表示的唯一编号,即进程PID。
进程ID在任何时候都是唯一的,当可以重用,当进程结束时它的ID可以给新的进程用,采用的规则是延迟重用。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:获取当前的进程ID
pid_t getppid(void);
功能:获取父进程的ID
一个进程可以创建另一个进程,创建者叫父进程,被创建者叫子进程。
//fork
#include <stdio.h>
pid_t fork(void);
功能:创建一个子进程
返回值:失败返回-1,成功返回2次
父进程:返回子进程的pid
子进程:返回0
根据返回值的不同可以分别为父子进程实现不同的处理分支。
fork调用后父子进程各自运行,先后顺序不确定,但某些操作系统可以保证子进程先运行。注意:父子进程谁先返回不确定。
子进程被创建时会拷贝父进程的data、bss、heap、stack、缓冲区等,唯独代码段不拷贝,父子进程共享一个代码段
fork调用结束后,父进程的文件描述符会复制到子进程中,此时子进程中也是可以继续使用的,因为所有进程共享一个文件表(内核级)。
子进程会继承父进程的信号处理方式,子进程结束后内核会向父进程发送SIGCHLD信号,父进程默认的处理该信号的方式是忽略,也可以捕获后回收子进程的资源。
注意:系统中的总进程数是有限的,当进程过多超过限制时,fork就会失败(不要轻易尝试,当fork前系统就会卡的不行)。
// vfork
pid_t vfork(void);
功能:创建子进程
/*以下一系列函数的功能就是配合vfork创建子进程*/
int execl(const char *path, const char *arg, ...);
path:程序的路径
arg:命令行参数
int execlp(const char *file, const char *arg, ...);
file:只需要程序的名字,系统会去PATH环境变量的路径中来查找这个程序。
arg:命令行参数
int execle(const char *path, const char *arg,..., char * const envp[]);
envp:环境变量表,创建子进程时传递父进程的环境变量表。
int execv(const char *path, char *const argv[]);
argv:命令参数以指针数组的形式提供
vfork与fork的区别:
1.子进程先返回,父进程等待,子进程临时占用父进程的资源,需要子进程真正创建出来后,父进程才返回。
2.vfork无法单独创建出一个子进程,需要与exec函数配合
3.vfork创建出的子进程不会复制父进程的相应数据内存,而是进行替换。
4.孤儿进程与僵尸进程
孤儿进程:当一个进程的父进程先于子进程结束,子进程就变成了孤儿进程,孤儿进程会被收养到孤儿院(init).
僵尸进程:当一个进程结束时,它的父进程没有回收相关资源,此进程就变成了僵尸进程。
二、进程的正常退出
1.从main函数中return
2.调用标准库函数exit
1.EXIT_SUCCESS/EXIT_FATLURE,约定好的进程的退出状态码,把参数的低8位返回给父进程。
2.进程退出前会调用通过atexit/on_exit函数注册的函数.
int atexit(void (*function)(void));
功能:注册一个函数,当进程退出时调用此函数。
int on_exit(void (*function)(int , void *), void *arg);
功能:注册一个函数,当进程退出时调用此函数,同时可以附加一个指针参数(如果进程是通过return从main函数退出,则不能指向栈空间,因此此时栈空间可能已经被释放)。
3。冲刷处于打开状态的标准IO流的缓冲区,并关闭文件。
3.进程的最后一个线程执行了返回语句
4.进程的最后一个线程调用了pthread_exit函数
三、进程的异常终止
1.进程收到某些信号,他杀。
2.进程自己调用abort函数,产生了SIGABRT信号,自杀。
3.进程的最后一个线程收到取消操作,并作出响应。
四、子进程回收
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int* status);
功能:以阻塞状态等待子进程结束,并回收它的相关资源,要有一个子进程结束它就返回,如果当前进程没有子进程就立即返回。
status:获取子进程的退出状态码,需要相关的宏进行解析才能获取到。
返回值:成功返回子进程的PID,失败返回-1.
pid_t waitpid(pid_t pid, int *status, int options);
功能:以阻塞状态等待子进程结束,与wait不同的是它可以指定等待哪个进程。
pid:要等待的子进程PID
<-1 等待进程组中的任意子进程结束
=-1 等待任意子进程结束,此时功能与wait等价。
=0 等待同组的任意进程结束。
>0 此时它代表着一个特定的子进程。
status:获取子进程的结束状态码, 需要相关的宏进行解析才能获取到。
options:
0 表示忽略此参数,功能与wait一样。
WNOHANG 如果没有子进程结束立即返回。
WUNTRACED 如果有子进程暂停也立即返回
WCONTINUED 如果有子进程从暂停状态切换为继续状态,立即返回。
返回值:成功返回子进程的PID,失败返回-1。