Skip to content

进程简介

程序和进程

  • 程序:是指编译好的二进制文件,在磁盘上,占用磁盘空间,是一个静态的概念
  • 进程:一个启动的程序, 进程占用的是系统资源,如:物理内存、CPU、终端等,是一个动态的概念

可以把程序看做是剧本(纸),进程看做是戏(舞台、演员、灯光、道具...);同一个剧本可以在多个舞台同时上演。同样 同一个程序也可以加载为不同的进程(彼此之间互不影响)。

并行和并发

  • 并发:在一个时间段内,是在同一个cpu上,同时运行多个程序。

CPU会将一个时间段分为若干个时间片,让程序轮流使用CPU。如:若将CPU的1S的时间分成1000个时间片,每个进程执行完一个时间片必须无条件让出CPU的使用权,这样1S中就可以执行1000个进程。

  • 并行:指两个或两个以上的程序在同一时刻发生(需要有多颗CPU,或者只有1颗CPU但是拥有多核心)。

PCB-进程控制块

每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。

/usr/src/linux-headers-4.4.0-96/include/linux/sched.h文件的1390行处可以查看 struct task_struct 结构体定义。其内部成员有很多,我们重点掌握以下部分即可:

  • 进程id。系统中每个进程有唯一的id(类似于人的身份证号码),在C语言中用pid_t类型表示,其实就是一个非负整数。在 Linux 中可以使用ps命令进行查看
  • 进程的状态,有就绪、运行、挂起、停止等状态。
  • 进程切换时需要保存和恢复的一些CPU寄存器。
  • 描述虚拟地址空间的信息。
  • 描述控制终端的信息。
  • 当前工作目录(Current Working Directory)。
    • getcwd函数 –相当于Linux的pwd命令
  • umask掩码。
  • 文件描述符表,包含很多指向file结构体的指针。
  • 和信号相关的信息。
  • 用户id和组id。
  • 会话(Session)和进程组。
  • 进程可以使用的资源上限(Resource Limit)。
    • ulimit -a

/usr/src/linux-headers-6.2.0-37/include/linux/sched.h

task_struct描述了进程的一切信息。

在 Linux 中,进程之间存在亲缘关系。使用ps -elf 中可以看到有 PID 列和 PPID 列,PPID 列所展示的就是父进程的 PID。

进程状态(面试考)

  • 进程基本的状态有5种。分别为初始态、就绪态、运行态、挂起态与终止态。其中初始态为进程准备阶段,常与就绪态结合来看。

获取进程 id

  • getpid - 得到当前进程的PID
cpp
#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);
  • getppid - 得到当前进程的父进程的PID
cpp
#include <sys/types.h>
#include <unistd.h>

pid_t getppid(void);

获取进程 id 实例

cpp
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    pid_t pid = getuid();
    pid_t ppid = geteuid();

    printf("uid = %d\t", pid);
    printf("euid = %d\n", ppid);

    return 0;
}

编译运行这个程序每次都打印的 pid 都不同,但是 ppid 都会是相同的并且 ppid 是运行该程序的终端的 pid。

进程的权限

使用man getuid可以看到它的描述信息。getuid()返回调用进程的真实用户ID;geteuid()返回调用进程的有效用户ID。基于此我们可以实现以下简单程序

cpp
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    pid_t pid = getuid();
    pid_t ppid = geteuid();

    printf("uid = %d\t", pid);
    printf("euid = %d\n", ppid);

    return 0;
}

从运行程序的情况可以得出结论,user 有可执行程序的x权限,通过该程序启动一个进程,进程的 uid 和 euid 都是 user。

但是如果可执行程序要对一个文件进行写入,但是执行该程序的用户没有对那个文件的写入权限。此时程序就无法运行了。

cpp
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int fdw = open("file1", O_WRONLY);
    if (fdw < 0) {
        perror("open");
        return -1;
    }
    write(fdw, "hello", 5);

    pid_t pid = getuid();
    pid_t ppid = geteuid();

    printf("uid = %d\t", pid);
    printf("euid = %d\n", ppid);

    return 0;
}

解决该问题的办法也很简单。使用创建该程序的用户执行chmod u+s 可执行程序名称,就可以改变该可执行程序的 euid,使得其他用户运行该可执行程序时拥有与该用户相同的权限。

其实 Linux 中文件的权限不止 9 位,应是 12 位权限

添加 suid 权限chmod u+s 可执行程序名称

添加 sgid 权限chmod g+s 可执行程序名称

添加 sticky 权限chmod o+w 目录名称 chmod o+s 目录名称

Linux控制进程的终端命令

  • ps:查看系统当中的进程
  • top:动态显示系统当中的进程
  • nice:用于 shell 脚本中,指定程序的优先级
  • renice:改变正在运行进程的优先级
  • kill:发送信号(可以给后台进程发送)
  • crontab:控制 cron 后台进程,设置定时任务
  • bg:将暂停的进程放到后台

ps

shell
-a:显示所有终端机下执行的程序,除了阶段作业领导者之外。
a:显示现行终端机下的所有程序,包括其他用户的程序。
-A:显示所有程序。
-c:显示CLS和PRI栏位。
c:列出程序时,显示每个程序真正的指令名称,而不包含路径,选项或常驻服务的标示。
-C<指令名称>:指定执行指令的名称,并列出该指令的程序的状况。
-d:显示所有程序,但不包括阶段作业领导者的程序。
-e:此选项的效果和指定"A"选项相同。
e:列出程序时,显示每个程序所使用的环境变量。
-f:显示UID,PPIP,C与STIME栏位。
f:用ASCII字符显示树状结构,表达程序间的相互关系。
-g<群组名称>:此选项的效果和指定"-G"选项相同,当亦能使用阶段作业领导者的名称来指定。
g:显示现行终端机下的所有程序,包括群组领导者的程序。
-G<群组识别码>:列出属于该群组的程序的状况,也可使用群组名称来指定。
h:不显示标题列。
-H:显示树状结构,表示程序间的相互关系。
-j或j:采用工作控制的格式显示程序状况。
-l或l:采用详细的格式来显示程序状况。
L:列出栏位的相关信息。
-m或m:显示所有的执行绪。
n:以数字来表示USER和WCHAN栏位。
-N:显示所有的程序,除了执行ps指令终端机下的程序之外。
-p<程序识别码>:指定程序识别码,并列出该程序的状况。
p<程序识别码>:此选项的效果和指定"-p"选项相同,只在列表格式方面稍有差异。
r:只列出现行终端机正在执行中的程序。
-s<阶段作业>:指定阶段作业的程序识别码,并列出隶属该阶段作业的程序的状况。
s:采用程序信号的格式显示程序状况。
S:列出程序时,包括已中断的子程序资料。
-t<终端机编号>:指定终端机编号,并列出属于该终端机的程序的状况。
t<终端机编号>:此选项的效果和指定"-t"选项相同,只在列表格式方面稍有差异。
-T:显示现行终端机下的所有程序。
-u<用户识别码>:此选项的效果和指定"-U"选项相同。
u:以用户为主的格式来显示程序状况。
-U<用户识别码>:列出属于该用户的程序的状况,也可使用用户名称来指定。
U<用户名称>:列出属于该用户的程序的状况。
v:采用虚拟内存的格式显示程序状况。
-V或V:显示版本信息。
-w或w:采用宽阔的格式来显示程序状况。 
x:显示所有程序,不以终端机来区分。
X:采用旧式的Linux i386登陆格式显示程序状况。
-y:配合选项"-l"使用时,不显示F(flag)栏位,并以RSS栏位取代ADDR栏位 。
-<程序识别码>:此选项的效果和指定"p"选项相同。
--cols<每列字符数>:设置每列的最大字符数。
--columns<每列字符数>:此选项的效果和指定"--cols"选项相同。
--cumulative:此选项的效果和指定"S"选项相同。
--deselect:此选项的效果和指定"-N"选项相同。
--forest:此选项的效果和指定"f"选项相同。
--headers:重复显示标题列。
--help:在线帮助。
--info:显示排错信息。
--lines<显示列数>:设置显示画面的列数。
--no-headers:此选项的效果和指定"h"选项相同,只在列表格式方面稍有差异。
--group<群组名称>:此选项的效果和指定"-G"选项相同。
--Group<群组识别码>:此选项的效果和指定"-G"选项相同。
--pid<程序识别码>:此选项的效果和指定"-p"选项相同。
--rows<显示列数>:此选项的效果和指定"--lines"选项相同。
--sid<阶段作业>:此选项的效果和指定"-s"选项相同。
--tty<终端机编号>:此选项的效果和指定"-t"选项相同。
--user<用户名称>:此选项的效果和指定"-U"选项相同。
--User<用户识别码>:此选项的效果和指定"-U"选项相同。
--version:此选项的效果和指定"-V"选项相同。
--widty<每列字符数>:此选项的效果和指定"-cols"选项相同。

ps -elf

ps aux

专门看内存占用的命令 free

buff 和 cache 的区别
  • buff 是内核缓冲区,cache 是页缓冲
  • buff 的本质是队列,是先进先出的结构。是为了解决两个设备间 IO 速度不一致的问题,避免其中一方速度过快或过慢
  • cache 是为了提升访问速度。也是用在 IO 速度不一致的两个设备上面,但是高速缓存cache 会直接把低速 IO 设备上的数据复制一份来提高访问速度

top

nice

优先级系统

共有140 个优先级级别,在 Ubuntu 系统中使用 -40~99 这些数值来描述优先级,数值越低优先级别越高。不同系统会有所差异。

其中, -40~59 表示实时优先级,60~99 表示普通优先级 实行完全公平调度策略。

用户无法修改调度策略,但是可以使用 nice间接修改优先级,其取值为 -20~19 对应到 Ubuntu 系统中的60~99 。如果一个进程的 nice 值是 0,那么对应到系统优先级的值就是 80。

nice 使用语法

shell
nice -n nice值 可执行程序名称

# 例如
nice -n 19 ./a.out

如果要将 nice 设为负值,需要使用sudo

renice

已经启动的进程可以使用 renice 来设置优先级,语法如下

shell
renice -n nice值 -p pid

kill

前台和后台

前台:可以响应键盘中断的进程,如Ctrl+C中止、Ctrl+\终止、Ctrl+Z暂停等

后台:不可以响应键盘中断的进程

shell
# 启动前台进程
./a.out

# 启动后台进程
./a.out &

# 查看本终端内的前后台进程 只针对bash有效,对zsh无效
jobs

# 将后台运行的进程拉到前台运行状态
fg n

# 将前台运行状态切换到后台暂停状态
直接使用 Ctrl+Z

# 将后台暂停的进程拉到后台运行状态
bg n

杀死进程

  1. 使用 ps 查看进程 pid

  2. 给这个进程发送-9信号

kill -l 查看系统有哪些信号

kill -9 pid 杀死某个进程

crontab

crontab 是 linux 系统中定时执行任务的命令。crontab -e可以打开单用户定时任务的vi编辑页面。

shell
# m h dom mon dow command
# 分 时 天 月 周几 命令

可以修改/etc/crontab文件来修改全局用户的定时任务

shell
# Example of job definition:
# .---------------- minute (0 - 59) 分钟
# |  .------------- hour (0 - 23) 小时
# |  |  .---------- day of month (1 - 31) 日期
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ... 月份
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat 周几
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed 要执行的命令
17 *	* * *	root	cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
47 6	* * 7	root	test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
52 6	1 * *	root	test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.monthly; }