1.进程:
程序:保存在磁盘空间中的的一段代码的集合,死的
进程:是一个程序动态执行的过程,包括进程的创建、调度和消亡的过程
2.进程相关的命令
PID:进程的标识符(进程的ID)
PPID:父进程的ID号
1.top
根据CPU占用率查看进程信息
q退出
2.ps -ef
查看当前所有进程的信息
ps -ef | grep a.out
3.ps -aux
查看当前所有进程的信息
ps -aux | grep a.out
4.kill
向进程发送信号,并让进程结束
kill -l
查看信号的类型
kill -信号名/信号编号 进程ID
kill -9 进程ID
killall -信号名/信号编号 进程名
5.后台运行代码:
./a.out &
6.jobs
查看后台执行的任务
7.fg 任务编号
将任务编号对应的后台任务放到前台执行
8.nice
nice -n 10 ./a.out
以10作为优先级运行a.out进程 在Linux系统中数字越小优先级越高
9.renice
renice -n 10 进程ID
10.pstree
查看进程的关系
3.进程的创建:
32bit操作系统中,进程运行,操作系统开辟 0 - 4G虚拟内存空间
虚拟内存空间分为:
用户空间:
1.文本段:文本区 存放代码、指令
2.数据段:数据区 存放全局变量、静态变量、常量
3.系统数据段:堆、栈
内核空间:
1.用户无法访问内核空间,通过信号、系统调用实现用户层和内核层的交互
实际物理内存空间:
MMU内存映射单元实现虚拟地址和物理地址间的映射关系
两个进程所占虚拟内存空间并不是 8 G,而是 4 G,两个进程分时共用同一空间
两个进程物理内存空间是独立的,进程1和进程2空间独立
结论:
进程的空间是独立的,但是两个进程并不是8G
4.进程的调度:
进程调度算法:
1.先来先执行,后来后执行
2.高优先级调度算法
3.时间片轮转调度算法
4.抢占式调度算法
5.多级反馈队列调度算法
时间片:
CPU在一个进程任务中执行的时间称为时间片
宏观并行、微观串行
进程的状态:
1.运行态、就绪态: R
运行态:正在被CPU执行的进程任务
就绪态:正在执行的任务,但由于CPU调度还没有被调度到
2.可唤醒等待态: S
睡眠态
由于缺少资源导致程序阻塞的状态,当拿到资源后,继续向下执行
3.不可唤醒等待态: D
不想被CPU任务调度打断
4.暂停态 T
用户主动让进程任务暂停(挂起)状态
5.僵尸态 Z
进程任务结束了,但空间没有被回收
6.结束态 X
进程任务执行完了,空间也被回收了
5.进程相关的函数接口:
1.fork
pid_t fork(void);
功能:
创建一个子进程
参数:
void 缺省
返回值:
创建失败返回-1
成功父进程中返回创建的子进程的PID
子进程中返回0
写时拷贝:fork创建子进程,子进程拷贝父进程中的数据,如果只是读取时不会为子进程单独生成空间,只有做写入操作时,才会
为子进程开辟独立空间,提高进程创建的效率
#include "../head.h" int main(void) { pid_t pid1; pid_t pid2; pid1 = fork(); if (-1 == pid1) { perror("fail to fork"); return -1; } if (0 == pid1) { printf("子进程1 PID:%d 父进程 PPID:%d\n", getpid(), getppid()); } else if (pid1 > 0) { pid2 = fork(); if (-1 == pid2) { perror("fail to fork"); return -1; } if (0 == pid2) { printf("子进程2 PID:%d 父进程 PPID:%d\n", getpid(), getppid()); } else if (pid2 > 0) { printf("父进程 PID:%d 子进程1PID:%d 子进程2PID:%d\n", getpid(), pid1, pid2); } } while (1) { } return 0; }
2.getpid
pid_t getpid(void);
功能:
获取进程的PID
3.getppid
pid_t getppid(void);
功能:
获得父进程的PID
练习:
编写一个代码实现,一个父进程创建2个子进程,
父进程中打印自己的PID和两个子进程的PID
子进程中打印自己的PID和父进程的PID
#include "../head.h" int main() { pid_t pid = 0; pid = fork(); if (-1 == pid) { return -1; } if (0 == pid) { printf("==========================\n"); printf("子进程pid:%d,父进程pid:%d\n",getpid(),getppid()); } if (pid > 0) { pid_t ppid = 0; ppid = fork(); if (-1 == ppid) { return -1; } if (0 == ppid) { printf("===========================\n"); printf("子进程pid:%d,父进程pid:%d\n",getpid(),getppid()); } if (ppid > 0) { printf("进程:%d,子进程1pid:%d,子进程2pid:%d\n",getpid(),pid,ppid); } } while (1) { } return 0; }
2.exit
void exit(int status);
功能:
进程结束
参数:
status:进程结束的状态码
返回值:
缺省
主函数中使用exit等价于return
#include "../head.h" int main(void) { pid_t pid; pid = fork(); if (-1 == pid) { perror("fail to fork"); return -1; } if (0 == pid) { printf("我要结束了!"); //exit(0); _exit(0); } else if (pid > 0) { } while (1) { } return 0; }
3._exit
void _exit(int status);
功能:
立即结束进程任务
exit()与_exit()函数最大的区别在于:
exit()函数在调用exit系统之前要检查文件的打开情况,把文件缓冲区的内容写回文件。
如果用_exit()函数直接将进程关闭,缓冲区的数据将会丢失。
6.进程消亡:
僵尸态:进程代码执行结束,空间没有被回收
僵尸进程产生原因:
1.子进程任务结束,空间没有被回收,该进程成为僵尸进程
如何避免产生僵尸进程:
1.让父进程先结束,子进程再结束,子进程不会变成僵尸进程
2.子进程结束,父进程回收子进程空间,子进程不会变成僵尸进程
孤儿进程:
父进程先结束,子进程成为孤儿进程,被init进程(1号)进程收养,子进程结束,init系统进程自动回收进程空间
7.wait
pid_t wait(int *wstatus);
功能:
等待子进程结束,回收子进程空间
参数:
wstatu s:存放子进程结束的状态码空间首地址
返回值:
成功返回子进程的PID
失败返回-1
注意:
wait具有阻塞功能,如果子进程先结束,wait会不阻塞直接回收
如果子进程没结束,wait会阻塞直到子进程结束回收子进程空间
#include "../head.h" int main(void) { pid_t pid; pid_t ret; int status; pid = fork(); if (-1 == pid) { perror("fail to fork"); return -1; } if (0 == pid) { printf("子进程(PID:%d)开始执行\n", getpid()); sleep(10); printf("我要结束了!\n"); exit(10); } else if (pid > 0) { printf("父进程(PID:%d)开始执行\n", getpid()); ret = wait(&status); printf("已经回收到子进程(PID:%d)空间了\n", ret); if (WIFEXITED(status)) { printf("子进程正常结束, 值为:%d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("子进程被 %d 号信号杀死\n", WTERMSIG(status)); } } return 0; }
WIFEXITED(wstatus):判断子进程是否正常结束
WEXITSTATUS(wstatus):获取子进程正常结束的状态码
WIFSIGNALED(wstatus):判断子进程是否被信号打断结束
WTERMSIG(wstatus):获取子进程被信号打断结束的状态码
wait可以实现同步功能。