前言
进程是一个程序一次执行的过程,是操作系统动态执行的基本单位。
进程的概念主要有两点:
第一,进程是一个实体。每个进程都有自己的虚拟地址空间,包括文本区、数据区和堆栈区。文本区域存储处理器执行的代码;数据区域存储变量和动态分配的内存;堆栈区域存储着活动进程调用的指令和本地变量。
第二,进程是一个“执行中的程序”,它和程序有本质的区别。程序是静态的,是一些保存在磁盘上的指令的有序集合;而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建、调度和消亡的过程,是Linux的基本调度单位。只有当处理器赋予程序生命时,它才能成为一个活动的实体,称之为进程。
内核的调度器负责在所有的进程间分配CPU执行时间,称为时间片(time slice),它轮流在每个进程分得的时间片用完后从进程那里抢回控制权。
一、进程标识
OS(操作系统)会为每个进程分配一个唯一的整形ID,作为进程的标识号(PID)。
进程O是调度进程,常被称为交换进程,它不执行任何程序,是内核的一部分,因此也被称为系统进程。
进程除了自身的ID外,还有父进程ID(PPID),也就是说每个进程都必须有它的父进程,操作系统不会无缘无故产生一个新进程。
所有进程的祖先进程是同一个进程,它叫作 init 进程,ID为1,init进程是内核自举后的第一个启动的进程。init 进程负责引导系统、启动守护(后台)进程并且运行必要的程序,它不是系统进程,但它以系统的超级用户特权运行。
二、进程的用户ID与组ID(进程的运行身份)
进程在运行过程中,必须具有一个类似于用户的身份,以便进行进程的权限控制。默认情况下,哪个登陆用户运行程序,该程序进程就具有该用户的身份。例如,假设当前登录用户为gotter,他运行了ls程序,则ls程序在运行过程中就具有gotter的身份,该ls进程的用户ID和组ID分别为gotter和gotter所属的组。这种类型的ID叫作进程的真实用户ID和真实组ID。真实用户ID和真实组ID可以通过函数getuid()和getgid()获得。
与真实ID对应,进程还具有有效用户ID和有效组ID的属性,内核对进程的访问权限检查时,它检查的是进程的有效用户ID和有效组ID,而不是真实用户ID和真实组ID。默认情况下,用户的有效用户ID和有效组ID与真实用户ID和真实组ID是相同的。有效用户ID和有效组ID通过函数geteuid()和getegid()获得。
代码如下(示例):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("uid:%d gid:%d euid:%d egid:%d\n", getuid(), getgid(), geteuid(), getegid());
return 0;
}
shell > id
uid=500(ghaha) gid=500(ghaha) groups=500(ghaha)
编译生成可执行文件a.out,程序文件的属性可能为:
-rwxrwxr-x 1 ghaha ghaha 12132 Oct 709:26 a.out
执行结果可能为:
shell>./a.out
uid:500 gid:500 euid:500 egid:500
现在将 a.out 的所有者可执行属性改为s:
shell>chmod u+s a.out
shell>ll
-rwsrwxr-x 1 ghaha ghaha 12132 Oct 7 09:26 a.out
此时改另外一个用户 gotter 登录并运行程序 a.out:
shell>id
uid=502(gotter) gid=502(gotter) groups=502(gotter)
shell>./a.out
uid:502 gid:502 euid:500 egid:502
可以看到,进程的有效用户身份变为了 ghaha,而不是 gotter 了,这是因为文件 a.out 的访问权限的所有者设置了 s 的属性,设置了该属性以后,用户运行 a.out 时, a.out 进程的有效用户身份将不再是运行 a.out 的用户,而是 a.out 文件的所有者。
s 权限最常见的例子是 /usr/bin/passwd 程序,它的权限位为:
shell>ll /usr/bin/passwd
-r-s- -x- -x 1 root root 16336 Feb 13 2003 /usr/bin/passwd
我们知道,用户的用户名和密码是保存在 /etc/passwd (后来专门将密码保存在 /etc/shadow,它是根据 /etc/passwd 文件来生成 /etc/shadow 的,它把所有口令从 /etc/passwd 中移到了 /etc/shadow 中。这里用到的是影子口令,它将口令文件分成两部分:/etc/passwd 和 /etc/shadow,此时 /etc/shadow 就是影子口令文件,它保存的是加密的口令,而 /etc/passwd 中的密码全部变成 x )下的。
通过 ls -l 查看 /etc/passwd 这个文件,你会发现,这个文件普通用户都没有可写的权限,那我们执行 passwd 的时候确实能够修改密码,也就是说,任何一个用户运行该程序时,该程序的有效身份都将是 root (用普通用户身份去执行这个操作的时候,它会暂时得到文件拥有者 root 的权限),而这样 passwd 程序才有权限读取 /etc/passwd 文件的信息。
三、进程的状态
进程是程序的执行过程,根据它的生命周期可以划分成3种状态,
时间片 ———————————— 就 ←———————————— 因为等待资源分配而唤醒
| 调度 —————————— 绪 |
| | |
| | |
| ↓ |
执 行 ————— 因为等待某个资源而睡眠 ———————→ 等 待
- 执行态:该进程正在运行,即进程正在占用CPU
- 就绪态:进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片
- 等待态:进程不能使用 CPU,若等待事件发生(等待的资源分配到)则可将其唤醒
四、Linux下的进程结构及管理
Linux 系统是一个多进程的系统,它的进程之间具有并行、互不干扰等特点,也就是说,进程之间是分离的任务,拥有各自的权利和责任。其中,每个进程都运行在各自的独立的虚拟地址空间,因此,即使一个进程发生了异常,它也不会影响到系统的其他进程。进程中各段如下所示:
进程中的数据段:
高地址 | 存放传递参数及环境变量 |
---|---|
---------- | 堆栈 |
---------- | ↓ |
---------- | ↑ |
---------- | 堆 |
---------- | BSS 数据段 |
---------- | 数据段(可读/只读) |
---------- | 数据段 |
低地址 | 代码段 |
进程的调用:
______________
| | |
|用户进程 ↓ | 用户态
|____________|
中断或系统调用——→______________
| | |
|内核进程 ↓ | 内核态
|____________|
Linux中的进程包含以下几个部分:
- “数据段”是存放全局变量、常数及动态数据分配的数据空间。数据段分成普通数据段(包括可读可写/只读数据段,存放静态初始化的全局变量或常量)、BSS 数据段(存放未初始化的全局变量)及堆(存放动态分配的数据)
- “正文段”存放的是 CPU 执行的机器指令部分
- “堆栈段”存放的是子程序的返回地址、子程序的参数及程序的局部变量等
一些进程相关的信息:
进程 process:是 OS 的最小单元,大小位 4GB,其中 1GB 给 OS,3GB 给进程 {代码区 堆 栈}。
ps:查看活动进程
ps -aux:查看各个进程的状态,包括运行、就绪、等待等状态
ps -aux | grep ‘aa’:查找指定(aa)进程
ps -ef:查看所有进程的 PID、PPID 等信息
ps -aux:看 %cpu(cpu使用量)和 %mem(内存使用量)
stat状态 {S就绪 T中断 R运行 Z僵尸}
via.c &:&表示后台运行
jobs:查看后台任务,fg 1把后台任务带到前台,ctrl+z 把进程带入后台
kill -9:进程号→杀掉某个进程,top 显示前20条进程,动态改变
pgrep ‘vi’:查找进程
nice:改变优先级
crontab:计划任务、定时操作等