进程概念
程序:存放在磁盘上的指令和数据的有序集合(文件)静态的。
进程:执行一个程序所分配的资源的总称,进程是程序的一次执行过程,动态的,包括创建、调度、执行和消亡。
进程和程序的区别
进程包含的内容
BSS段:存放程序中未初始化的全局变量;
数据段:存放程序中已初始化的全局变量;
代码段:程序执行代码;
堆(heap):堆适用于存放进程运行中被动态分配的内存段(malloc等函数分配内存),当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张), 当利用free等函数释放内存时,被适当的内存从堆中被剔除(堆被缩减);
栈(stack):栈又称为堆栈,使用户存放程序时临时创建的局部变量(但不包括static声明的变量,相当于全局变量,static意味着在数据段中存放变量)局部变量、函数参数、函数返回值;
进程控制块(PCB):PID、进程优先级、文件描述符表。
进程类型
交互进程:在shell下启动,以在前台运行,也可以在后台运行。
批处理进程:和在终端无关,被提交到一个作业队列一遍顺序执行。
守护进程:和终端无关,一直在后台运行。
进程状态
运行态:进程正在运行,或者准备运行;
等待态:进程在等待一个事件的发生或某种系统资源;
停止态::进程被中止,收到信号后可继续运行
死亡态(僵尸态 ):已终止的进程,但PCB还没被释放。
进程常用命令
查看进程信息
ps :查看系统进程快照;
-e:显示所有信息;
-l :长格式显示更加详细的信息;
-f :全部列出,通常和其他选项连用;
ps -elf|grep test :查看指定进程test;
top:查看进程动态(实时)信息;
top -p PID 查看指定进程;
shift + > 后翻页;
shift + < 前翻页;
/proc:查看进程详细信息;
进程含义:
表头 | 含义 |
F | 进程标志,说明进程的权限,常见的标志有两个:
|
S | 进程状态。进程状态。常见的状态有以下几种:
|
UID | 运行此进程的用户的 ID; |
PID | 进程的 ID; |
PPID | 父进程的 ID; |
C | 该进程的 CPU 使用率,单位是百分比; |
PRI | 进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行; |
NI | 进程的优先级,数值越小,该进程越早被执行; |
ADDR | 该进程在内存的哪个位置; |
SZ | 该进程占用多大内存; |
WCHAN | 该进程是否运行。"-"代表正在运行; |
TTY | 该进程由哪个终端产生; |
TIME | 该进程占用 CPU 的运算时间,注意不是系统时间; |
CMD | 产生此进程的命令名; |
进程相关命令
改变程序的优先级
nice :按用户指定的优先级运行进程;
nice [-n NI值] 命令
NI范围是-20~19,数值越大优先级越低;
普通用户只能调整NI值得范围是0~19,而且只能调整自己的进程;
普通用户只能调高NI值,而不能降低;
只有root用户才能设定进程NI 值为负值,而且可以调整任何用户的进程。
renice:改变正在运行的进程的优先级;
renice [优先级] PID
jobs :查看后台进程;
bg :将挂起的进程在后台运行;
fg :把后台运行的进程放到前台运行;
Ctrl + z :把运行的前台进程转为后台并停止;
./test & : 把test程序后台运行
创建子进程
子进程的概念:子进程为由另外一个进程(对应称之为父进程)所创建的进程;
#include <unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1;
成功时父进程返回子进程的进程号,子进程返回0;
通过fork的返回值区分父进程和子进程。
代码实现:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv[])
{
pid_t pid;
pid = fork();
if (pid > 0) {
printf("this is father process\n");
}
else if(pid == 0) {
printf("this is child process\n");
}else if(pid < 0) {
perror("fork");
return 0;
}
printf("pid=%d\n", (int)pid);
}
执行结果:
要点:
1、子进程只执行fork之后的代码;
2、父子进程执行顺序是操作系统决定的;
父子进程之间的关系:
1、子进程继承了父进程的内容;
2、父子进程有独立的地址空间,互不影响;
3、若父进程先结束:
子进程成为孤儿进程,被init进程收养;
子进程变成后台进程;
4、若子进程先结束:
父进程如果没有及时回收子进程变成僵尸进程;
子进程进阶
题目1:一个父进程五个子进程;
代码实现:
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
int i;
for (i = 0; i < 4; i++) {
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
}else if(pid == 0) {
printf("chaild process\n");
sleep(1);
break;
}else{
printf("father process\n");
sleep(5);
}
}
sleep(100);
}
题目2:实现一个进程链,父进程->子进程->孙进程->重孙进程->重重孙进程
代码实现:
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
int i;
for (i = 0; i < 4; i++) {
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
}else if(pid == 0) {
printf("chaild process\n");
sleep(1);
}else{
printf("father process\n");
sleep(5);
break;
}
}
sleep(100);
}
进程的退出
#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
结束当前的进程并将status返回;
exit结束进程是会刷新缓冲区,_exit不会;
return和exit的区别:
main函数结束时会隐式的调用exit函数,普通函数return是返回上一级;
进程的回收
#include <unistd.h>
pid_t wait(int *status);
成功时返回回收的子进程的进程号;失败时返回EOF
若子进程没有结束,父进程一直阻塞
若有多个子进程,哪个先结束就先回收
status 指定保存子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB,不接收返回值
#include <unistd.h>
pid_t waitpid(pid_t pid, int *status, int option);
参数:
pid
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
options
options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用
WNOHANG :若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
WUNTRACED: 返回终止子进程信息和因信号停止的子进程信息
wait(wait_stat) 等价于waitpid(-1,wait_stat,0)