进程与程序的区别
程序:一个可执行文件,是静态的。
进程:一个正在执行的程序,是动态的。
单道和多道的区别
单道程序设计:所有进程一个一个排队等待执行。若A阻塞,则B只能等待。
多道程序设计:在计算机内存中同时存在着几道相互独立的进程,他们在管理程序控制之下,相互穿插着运行。
并行与并发的区别
并行(parallel):多核状态下,有多个指令在多个处理器下同时执行
并发:单核状态下,多个指令轮流运行,在宏观上达到并行,微观上是轮流执行
进程控制块(PCB)
进程运行时,内核为每个进程提供一个进程控制块(PCB),维护进程的相关信息,Linux内核的进程控制块是task_struct结构体
task_struct结构体
PCB在内核中的存储位置
进程号
概念
每个进程都有一个进程号,类型为pid_t,进程号的范围为:0~32767。
进程号是唯一的,但是可以重复使用,当一个进程销毁时,他的进程号就可以被继续使用。
进程的编号就是该进程的进程号。
内核自带两个进程,分别是进程0和进程1
进程0:调度进程,也被称为交换进程。
进程1:初始化其他进程,所以其他所有进程都是他的直接或间接子进程(后边会提)。
PID:进程号
PPID:父进程号
PGID:进程组号
获取进程id
pid_t getpid(void);
功能:获取本次进程号
返回值:本次进程号(失败返回-1)
形参:无
获取进程的父进程id
pid_t getppid(void)
功能:获取调用此函数的进程的父进程号
返回值:调用此函数的进程的父进程号
形参:无
获取进程所在进程组id
pid_t getpgrp
创建进程fork
概述
系统允许一个进程新建一个子进程,该进程为新创建的子进程的父进程
函数
pid_t fork(void)
头文件:
#include <sys/type.h>
#include <unistd.h>
失败:返回-1
成功:子进程中返回0,父进程返回子进程号
示例
父子进程关系
使用fork函数得到的子进程其实就是父进程的一个复制品,他从父进程处复制了地址空间 (包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等 ),子进程独有的只有他的进程号,计时器等。
因此,使用fork的代价是非常大的(但是还是得用)
示例2
代码1
运行结果
代码2
运行结果
分析
代码1和代码2的区别只有第6行的printf中多了一个\n
打印出来的内容却不一样
printf在打印内存时,会将打印的数据存放在缓冲区
代码1使用了换行缓冲,所以此时的缓冲区中没有内容
而代码2中第6行中的printf中的内容还停留在缓冲区
创建子进程时,子进程会拷贝父进程缓冲区的内容,因此也会打印Hello
思考
该程序运行结果是?
答案:HelloWorldHello
进程状态
分类
进程状态可以分为三大状态和五大状态
三大状态
运行态,就绪态,阻塞态
五大状态
创建态,运行态,就绪态,阻塞态,终止态
ps查看进程状态
进程资源的回收
概述
回收原则:谁创建谁回收
在每个进程结束的时候,内核释放该进程的所有资源,但仍为其保留一些信息,主要是PCB信息(包括进程号、退出状态、运行时间等)
wait函数
头文件
#include <sys/type.h>
#include <sys/wait.h>
函数
pid_t wait(int *status)
参数:status(进程退出时的状态信息)
返回值:pid_t(成功:已经结束子进程的进程号;失败:-1)
注意:
因为回收原则,所以必须在父进程中调用
会阻塞当前进程,知道回收一个子进程
示例
运行结果
exit函数与_exit函数
exit
头文件
exit是一个库函数
#include <stdio.h>
函数
void exit(int status);
_exit
头文件
#include <unistd.h>
函数
void _exit(int status);
区别
_exit是系统调用函数
而exit的底层是_exit函数,所以相对而言exit效率低
示例
运行结果
WIFEXITED与WEXITSTATUS
WIFEXITED
判断进程是否正常退出,如果子进程是正常终止的,取出的值非0
WEXITSTATUS
返回子进程的退出状态值(保存在status变量的8-16位)
waitpid函数
atexit函数
作用
进程在退出之前可以用atexit函数注册退出处理函数
注意
1.一个进程至多可以注册32个函数,由exit调用
2.以与等级相反的顺序调用
特殊进程
僵尸进程
子进程已经结束,父进程没有回收子进程资源(有危害:子进程的pid被占用,系统的pid数量是有限的)
孤儿进程
父进程比子进程先结束(无危害:子进程被1号进程接管)
守护进程
又名精灵进程、不死进程