什么是进程
什么是程序
一组可以被计算机直接识别的 有序 指令 的集合。
通俗讲:C语言编译后生成的可执行文件就是一个程序。
那么程序是静态还是动态的?
程序是可以被存储在磁盘上的,所以程序是静态的。
那什么是进程
进程是程序的执行过程,是动态的,随着程序的使用被创建,随着程序的结束而消亡。
可以说进程是一个独立的可调度的任务。
进程是系统调度的独立任务。
进程是程序执行的独立任务。
进程是资源(内存资源)管理的最小任务。
一个程序可以只有一个进程,此时正在运行的这个程序也叫进程。
一个程序也可以有多个进程,此时正在运行的这个程序有多个进程动态执行。
所以说进程可以是程序,但程序不一定是进程。
注意:每一个程序运行时,操作系统分配给进程的 是虚拟内存,意味着每一个进程所使用的空间都是虚拟内存, 虚拟内存会被单元管理模块(MMU)映射到物理内存上,如何映射是操作系统关心的事情,程序开发者不用关心。
时间片
进程有多个,而CPU只有一个,假设该CPU是单核的,那么在某一时刻CPU只能处理一个进程,但是不能一直去处理这个进程,得多个进程之间轮流处理,给用户感觉这些进程在同时进行,而CPU处理一个进程的时间段即时间片。时间片是约定好CPU处理一个进程的时间段。
进程的类型
交互进程:完成人机交互的进程,比如shell
批处理进程:比如gcc的四步流程
守护进程:开机自启动,关机自动关闭(后台运行)
进程的状态
就绪状态:具备运行条件,等待处理器运行的进程。
当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。在一个系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
运行状态:处理器正在运行的进程。
等待状态:又称阻塞态或睡眠态,指进程不具备运行条件,正在等待某个时间完成的状态。
也称为等待或睡眠状态,一个进程正在等待某一事件发生(例如请求I/O而等待I/O完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。
死亡状态:运行结束的进程。
进程的模式
终端:内核发送的信号。
系统调用:调用操作系统提供给用户来访问硬件的一组接口。
进程三态模型
运行态→等待态:等待使用资源;如等待外设传输;等待人工干预。
等待态→就绪态:资源得到满足;如外设传输结束;人工干预完成。
运行态→就绪态:运行时间片到;出现有更高优先权进程。
就绪态---→运行态:CPU 空闲时选择一个就绪进程。
孤儿进程
指父进程先于子进程退出,此时子进程称为孤儿进程。但是该进程会被pid为1的init进程收养。
僵尸进程
指子进程先于父进程退出并且没有被父进程回收子进程的资源。此时子进程就会变成僵尸进程。僵尸进程会造成浪费空间、资源泄露等问题。
进程的相关系统调用
创建进程
- 每个进程都由父进程创建。
- 通过系统调用函数 fork() 实现进程创建。
fork()
头文件:<sys/types.h> <unistd.h>
函数原型:pid_t fork();
返回值:PID,进程ID号。返回 0 表示子进程,返回-1失败,返回大于0的整数表示创建进程的PID。
可以通过getpid()来获取当前运行的进程ID,通过getppid()获取当前进程的父进程ID。
wait()
头文件:<sys/wait.h><sys/types.h>
函数原型:pid_t wait(int* status) status为空时表示忽略子进程退出时的状态,不为空表示保存子进程退出时的状态。
返回值:成功返回子进程的PID,失败返回-1
使进程进入阻塞状态。
直到任意子进程结束或者该进程接收到信号为止。
如果该进程没有子进程,或子进程已经结束。wait()会立即返回。
此函数时进程阻塞时父进程什么也不干。
该函数可以获取子进程终止使的退出状态。
waitpid()
函数原型:pid_t waitpid(pid_t pid, int *status, int options)
入参:pid
pid 传-1时 等待任意子进程与wait功能一样。
pid 传0时 等待其组ID等于调用进程的组ID的任意子进程。
pid 传 小于-1时 等待其组ID等于PID的绝对值的任意子进程。
入参:status 同wait
status 通过WIFEXITED宏来测验 子进程正常退出返回true,否则返回false
status 通过WEXITSTATUS宏 来查看退出状态值。
return exit() _exit() 在WIFEXITED看来都算正常退出
入参:options
传0 同wait 阻塞父进程
传WNOHANG:若由PID指定的子进程并不立即可用,则waitpid不会被阻塞,此时返回值为0,子进程结束时返回子进程PID
返回值:正常返回结束的子进程PID,-1失败,
功能与wait类似。
可以指定等待某个子进程以及等待方式(阻塞或非阻塞)
wait和waitpid都可以实现对子进程资源的回收
exit(int status)
status:退出状态。
使进程终止,并清空缓冲区。
_exit(int status)
使进程终止,但是不会清空缓冲区。
Exec函数族
以exec开头的一系列函数
该族函数提供了在一个进程中执行新的进程
通过fork开启的子进程中拥有与父进程相同的代码,但是开辟了新的空间,这么做实际意义不大。所以exec族函数可以对fork创建的子进程进行代码替换,只保留PID不变,这就实现了在一个进程中产生了新的进程。
参数 | 意义 |
---|---|
l(list) | 参数地址列表,以空指针结尾 |
v(vector) | 存有各参数地址的指针数组的地址 |
p(path) | 按 PATH 环境变量指定的目录搜索可执行文件 |
e(environment) | 存有环境变量字符串地址的指针数组的地址 |
守护进程
- 运行在后台的进程,与终端没有任何关系。
- 开机自启动,关机自关闭。
前台进程
和终端有关系的进程
后台进程
与终端脱离关系。
变成后台进程的步骤
- 首先变成孤儿进程。
- 让自己成为新的进程组组长。
- 让自己成为新的会话组组长。
- 使控制终端tty变成 '?' 才能完全脱离终端。
创建守护进程的步骤
-
创建子进程父进程退出。(为了让子进程先被init收养)
-
创建新的会话组。(通过setsid()函数)让自己成为新的会话组组长。
此时守护进程已经创建,但是还需要优化。再使用chdir()函数修改守护进程的工作路径。
重设文件掩码。将文件掩码设置为0可以增加守护进程的灵活性
关闭父进程继承过来的文件描述符。因为守护进程用不到这些资源,会造成资源浪费。
-
getdtablesize()返回一个进程可以打开的最大文件数
-
再到/etc/rc.local 文件中exit 0之前 将这个守护进程的绝对路径写在这里。开机自启动。