进程简介
程序和进程
- 程序:是指编译好的二进制文件,在磁盘上,占用磁盘空间,是一个静态的概念
- 进程:一个启动的程序, 进程占用的是系统资源,如:物理内存、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
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
getppid
- 得到当前进程的父进程的PID
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
获取进程 id 实例
#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。基于此我们可以实现以下简单程序
#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。
但是如果可执行程序要对一个文件进行写入,但是执行该程序的用户没有对那个文件的写入权限。此时程序就无法运行了。
#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
-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 使用语法
nice -n nice值 可执行程序名称
# 例如
nice -n 19 ./a.out
如果要将 nice 设为负值,需要使用sudo
renice
已经启动的进程可以使用 renice 来设置优先级,语法如下
renice -n nice值 -p pid
kill
前台和后台
前台:可以响应键盘中断的进程,如Ctrl+C
中止、Ctrl+\
终止、Ctrl+Z
暂停等
后台:不可以响应键盘中断的进程
# 启动前台进程
./a.out
# 启动后台进程
./a.out &
# 查看本终端内的前后台进程 只针对bash有效,对zsh无效
jobs
# 将后台运行的进程拉到前台运行状态
fg n
# 将前台运行状态切换到后台暂停状态
直接使用 Ctrl+Z
# 将后台暂停的进程拉到后台运行状态
bg n
杀死进程
使用 ps 查看进程 pid
给这个进程发送
-9
信号
kill -l
查看系统有哪些信号
kill -9 pid
杀死某个进程
crontab
crontab 是 linux 系统中定时执行任务的命令。crontab -e
可以打开单用户定时任务的vi编辑页面。
# m h dom mon dow command
# 分 时 天 月 周几 命令
可以修改/etc/crontab
文件来修改全局用户的定时任务
# 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; }