进程与程序:
程序就是存储在磁盘上包含可执行指令、数据的静态实体(可执行文件)。
进程就是被操作系统读取加载到内存中的正在执行的程序。
进程的分类:
守护进程:
由系统在开机时通过启动脚本激活的,总处于活动状态,一般运行在后台维护系统的正常运行,进程号为1
批处理进程:
系统会每隔一段时间集中处理该类进程的相关指令,会有一定的延时,但避免的状态切换的耗时因此执行效率高
交互进程:
在执行需要用户输入一些数据否则无法继续执行
Linux/UNIX系统查看进程:
简单形式:ps 以简略方式显示当前用户当前终端有控制权的进程
详细显示:ps auwx
a:所有用户的当前终端有控制权的进程
u:显示详细信息
x:包含无控制权进程
w:以更大列宽显示
进程的信息列表:
USER 进程属主
PID 进程IP
%CPU CPU使用率
%MEM 内存使用率
VSZ 使用虚拟内存的大小(kb)
RSS 使用武力内存的大小(kb)
TTY 终端设备号(表示他被哪个终端控制着),?表示无终端控制
STAT 进程的状态
O 就绪态,所有准备工作都已经完成,等待被系统执行
R 运行状态的进程,Linux系统中没有就绪态,就绪态也用R表示
S 可被唤醒的睡眠,当收到系统中断、获取到资源、手动信号都可以从进程唤醒转入运行状态
D 不可唤醒的睡眠,只能被系统调用wake_up唤醒
T 暂停,收到信号SIGSTOP进程将转为暂停状态,收到SIGCONT信号转为进行状态
Z 僵尸状态,进程已经结束运行,但其父进程还没有回收他的资源
s 会话首进程
l 多线程进程
< 高优先级进程
N 低优先级进程
+ 前台进程
START 进程的启动时间
TIME 进程的运行时间
COMMAND 启动进程的程序123
父进程和子进程:
1.一个进程可以创建另一个进程,创建者叫父进程,被创建者叫子进程
2.父进程启动子进程后,子进程会在操作系统的高度下与父进程同时执行
3.子进程先于父进程结束时,会向父进程发送SIGCHLD信号,父进程收到SIGCHLD信号后就应该去回收子进程的相关资源。如果父进程没有及时回收,子进程将变成僵尸进程
4.父进程先于子进程结束,子进程就会变成孤儿进程,同时会被inie进程收养,即成为init的子进程,而init进程也叫孤儿院
进程标识符:
1.每个进程都会有一个以非负数表示的唯一的标识,也就是进程ID,相当于进程的身份证号
2.进程在任何时候都是唯一的,但可以重用,当一个进程结束时,它的进程ID就可以被其他进程使用(延时重用)
pid_t getpid(void);
功能:获取当前进程的进程ID
pid_t getppid(void);
功能:获取当前进程的父进程ID
fork创建进程:
pid_t fork(void);
功能:创建一个子进程
返回值:
成功返回两次,子进程返回0,父进程返回子进程的ID
失败返回一次-1
1.通过fork创建的子进程会拷贝父进程的代码段、全局数据段、静态数据段、堆、栈、IO缓冲区、文件表。
2.进程创建完成后,父子进程各自独立运行,先后顺序不确定。
3.当进程的总数超出系统限制,fork将创建失败
4.通过fork创建的子进程会继承父进程的信号处理方式
vfork创建进程:
pid_t vfork(void)
功能:创建子进程
返回值:
失败返回一次-1
成功返回两次,子进程返回0,父进程返回子进程的ID
区别:
vfork创建的子进程不会复制父进程的代码段资源,而是通过exec系列函数直接加载一个可执行文件启动子进程
注意:子进程创建成功前,子进程暂时借用父进程的相关资源来加载子进程,而此时的父进程是阻塞状态,只有子进程创建成功后,父进程才继续执行。
通过vfork创建的子进程不会继承父进程的信号处理方式
int execl(const char *path, const char *arg, ...);
path:可执行文件的路径
arg:命令行参数,至少一个,且以NULL结尾
注意:必须以NULL结尾
int execlp(const char *file, const char *arg, ...);
file:可执行文件名,该文件必须存储在PATH环境变量的路径下
arg:命令行参数,至少一个,且以NULL结尾
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
argv:存储命令行参数的字符串数组,最后一个必须是NULL
system创建进程:
int system(const char *command);
功能:加载一个可执行程序创建子进程
返回值:成功返回0,失败返回-1
区别:子进程执行时,父进程阻塞,子进程之行结束后父进程才继续
进程的正常退出:
1.从main函数return
2.调用标准C函数exit
无论在任何时候的任何位置调用此函数的进程会立即结束
void exit(int status);
功能:结束当前进程,该函数一旦执行就不会返回
status:进程的结束状态码,与main函数中的返回值具有同样的意义,反映了进程是如何结束的,父进程调用相关函数可以获取到status的第八位,常用的参数:EXIT_SUCCESS,EXIT_FAILURE
注意:进程在结束之前会先执行atexit/on_exit注册的函数,然后再调用_exit/_Exit函数
int atexit(void (*function)(void));
int on_exit(void (*function)(int , void *), void *arg);
功能:向内存注册一个函数,进程结束时自动执行被注册的函数
void (*function)(int , void *)
int:exit函数的参数或者return的返回值
void*:是on_exit注册时的第二个参数
3.调用_exit/ _Exit函数
void _exit(int status);
功能:Linux系统提供的进程结束函数
void _Exit(int status);
功能:标准库提供的等价于_exit的进程结束函数,目的是为了提高代码的兼容性
1.status可以是exit函数的参数,也可以是用户提供的,父进程调用相关函数可以获取status的第八位
2.冲刷所有文件的缓冲区,关闭所有文件
3.把子进程托付给init进程收养
4.取消所有的虚拟内存映射
5.向父进程发送SIGCHLD信号
4.进程的最后一个线程执行了返回语句
5.进程的最后一个线程调用了pthread_exit函数
进程的异常中止:
1.进程接受到某些信号(他杀)
2.调用abort函数(自杀)
3.最后一个线程对“取消”请求作出响应(让你死你还真去死了)
回收子进程
pid_t wait(int *status);
功能:等待所有子进程结束,如果所有子进程都在运行,则父进程阻塞
返回值:
1.如果没有子进程,则返回错误ECHILD
2.只要由一个子进程结束,则返回子进程的进程ID和结束状态码
pid_t waitpid(pid_t pid, int *status, int options);
功能:等待指定的子进程结束,回收子进程
pid:
1.< -1 等待进程组号为pid的绝对值的进程结束
2.= -1 等待任意子进程结束
3.= 0 等待组进程ID为当前ID的进程结束
4.> 0 等待进程ID为pid的进程结束
options:
WNOHANG 如果没有子进程则立即返回
WUNTRACED 子进程停止立即返回
WCONTINUED 子进程由暂停状态转为继续状态页返回
返回值:
1.如果没有子进程,则返回错误ECHILD
2.只要由一个子进程结束,则返回子进程的进程ID和结束状态码
解析status的宏:
WIFEXITED(status) 检查进程是否正常退出
WEXITSTATUS(status) 获取status的第八位,但只有WIFEXITED(status)为真时才有意义
WIFSIGNALED(status) 检查进程是否被信号中止
WTERMSIG(status) 获取导致进程中的信号WIFSIGNALED(status)结果为真时才有意义
WCOREDUMP(status) 检查进程是否是否由于核心转储结束(段错误)
WIFSTOPPED(status) 结果为真时才有意义
WSTOPSIG(status) 获取导入进程暂停的信号,WIFSTOPPED(status)结果为真才有意义
WIFCONTINUED(status)当进程由暂停转换为继续运行进程返回真
当父进程收到子进程的SIGCHLD信号时,就应该调用wait/waitpid函数回收子进程
如果子进程已经结束,在调用wait/waitpid函数之前子进程处于僵尸状态,调用后子进程会立即消失
如果不关心子进程的结束状态,status的参数可以为NULL