第1节 程序、进程、守护进程、僵尸进程的区别
程序、进程、守护进程、僵尸进程:
- 程序:c/php/java,代码文件,静态的,放在磁盘里的数据。
- 进程:正在内存中运行的程序,进程是动态的,会申请和使用系统资源,并与操作系统内核进行交互。
- 进程运行:系统把程序放在内存里执行。
- 守护进程:在内存持续保持运行着的程序。
- 僵尸进程:是因为子进程在没有执行完毕之前,自己的父进程被kill掉,父进程无法通知内核清理回收;子进程就成为了一个没娘的孩子,任由它自身自灭。
第2节 多任务与CPU
多任务与CPU:
- 现在所有的操作系统(分时设计,例如CentOS)都能"同时"运行多个进程,也就是多任务或者说是并行执行。但实际上这是人类的错觉,一颗物理cpu在同一时刻只能运行一个进程,只有多颗物理cpu才能真正意义上实现多任务。
- cpu如何选择下一个要执行的进程,这是一件非常复杂的事情。在Linux上,决定下一个要运行的进程是通过"调度类"(调度程序)来实现的。程序何时运行,由进程的优先级决定(renice命令调整进程的优先度),但要注意,优先级值越低,优先级就越高,就越快被 调度类选中。除此之外,优先级还影响分配给进程的时间片长短。在Linux中,改变进程的nice值,可以影响某类进程的优先级值。
- 进程切换被CPU执行时,内核会将每个进程临时停止时的运行时环境(寄存器中的内容和页表)保存下来(保存位置为内核占用的内存),这称为保护现场,在下次进程恢复运行时,将原来的运行时环境加载到cpu上,这称为恢复现场,这样cpu可以在当初的运行时环境下继续执行。
- 在进程的优先级相同下;Linux的调度器不是通过cpu的时间片流逝来选择下一个要运行的进程的,而是考虑进程的等待时间,即在就绪队列等待了多久
- cpu的衡量单位是时间,就像内存的衡量单位是空间大小一样。进程占用的cpu时间长,说明cpu运行在它身上的时间就长。注意,cpu的百分比值不是其工作强度或频率高低,而是"进程占用cpu时间/cpu总时间",这个衡量概念一定不要搞错。
第3节 父子进程及创建方式(资源,shell实现)
图1:
[jeson@mage-jump-01 ~/]$echo "echo \$a" >>text.sh
[jeson@mage-jump-01 ~/]$a=123
[jeson@mage-jump-01 ~/]$sh text.sh
[jeson@mage-jump-01 ~/]$export a=456
[jeson@mage-jump-01 ~/]$sh text.sh
456
执行shell脚本 : 基本上与执行bash命令基本一致,只加载父进程的各命令路径。
执行shell的内置bash命令:fork出来的子进程完成继承父进程,但为保护父进程的环境,所有子进程会重新加载环境,使用新的变量。在内置命令角度,没有创建子进程,$BASH_SUBSHELL的值为0。在新的环境及父进程的角度,可以是创建子进程。
图2:
[jeson@mage-jump-01 ~/]$exec echo "123"
123
回到了登录界面
执行shell内置命令:父进程不会创建子进程运行内置命令,若内置命令在管道后面,则与管道左边的进程属于一个进程组。
shell内置命令source :将脚本的变量继承到当前进程中。一般用于加载环境配置类脚本
图4:
[jeson@mage-jump-01 ~/]$cp text.sh /tmp/
[jeson@mage-jump-01 ~/]$
执行非bash内置命令: fork一个进程出来,然后利用exec替代子进程。
非内置命令的命令替换: 遇到替换命令 `command` 或 $(command),将开启一个fork子进程处理。
小结:
在bash环境中,执行bash命令所产生的子进程,是不会全部继承父进程的全部环境变量,一般只继承少量的环境变量(Path路径等) ,因此在脚本设计中或有计划的创建子进程实现某些功能时,考虑父进程的环境变量是否能被子进程继承是重点之一(比如Java变量,java在父进程可以运行,在脚本中却报错了,就是因为脚本(这个儿子没有继承父亲的某些彪悍的优点)没有继承父进程的java变量,所以在脚本中需要另外添加Java的变量即可。
第4节 各进程与CPU的状态
以 cp /etc /tmp/ 为例进行说明:
在当前bash环境下,当执行cp命令时,首先fork出一个bash子进程(即前台),然后在子bash执行exec加载cp程序,cp子进程进入等待队列,由于在命令行下敲的命令,所以优先级较高,调度类很快选中它。在cp这个子进程执行过程中,父进程bash会进入睡眠状态(即后台),并等待被唤醒,此刻bash无法和人类交互。当cp命令执行完毕,它将自己的退出状态码告知父进程,此次复制是成功还是失败,然后cp进程自己消逝掉,父进程bash被唤醒再次进入等待队列,并且此时bash已经获得了cp退出状态码,根据状态码这个"信号",父进程bash知道了子进程已经终止,所以通告给内核,内核收到通知后将进程列表中的cp进程项删除。至此,整个cp进程正常完成。
有时也会发生意外,导致僵尸进程的产生;是因为子进程在没有执行完毕之前,自己的父进程被kill掉,父进程无法通知内核清理回收;子进程就成为了一个没娘的孩子,任由它自身自灭。
第5节 jobs与父子进程的前后台,fuser,lsof
用以下的例子进行说明:
[jeson@mage-jump-01 ~/]$sleep 100& sleep 150& sleep 200&
[1] 5027
[2] 5028
[3] 5029
[jeson@mage-jump-01 ~/]$jobs -l #表示 list jobs 里的tables
[1] 5027 Running sleep 100 & #([1]: 后台进程的编号 空: CPU正在执行 5027: PID号 Done: 运行完成)
[2] - 5028 Running sleep 150 & #([2]: -------------- -: CPU下个执行 5028: PID号 Stopped:运行暂停)
[3] + 5029 Running sleep 200 & #([3]: -------------- +: 在队列中等待 5029: PID号 Running:正在运行)
进程前、后台的切换以及转移到服务端init进程下:
小结:
bash 环境下,将子进程放入后台运行,是实现无限接近并发的效果。
第5节 信号与进程与kill,pkill,killall
信号是软件层次上对(硬)中断的一种模拟,通知进程发送了什么事件(而不是对进程传递数据),令进程改变某些行为。信号是操作系统规范进程改变自身的某种行为。
一般情况下完善的程序设计,在子进程终止、退出的时候,会发送SIGCHLD信号给父进程,父进程收到信号就会通知内核清理该子进程相关信息。
同时,信号也提供了人工干预进程的方式(比如手机上要有关机键,操作系统中有关机的选项,电脑主机上要有重启键),以下命令实现给进程发送信号
kill -s signal pid # kill -s HUP pid1 ... 或 kill -s HUP pid kill -signal pid # kill -9 pid1 pid2 ... 或 kill -HUP pid kill pid #默认 -15 |
pkill |
killall |
键盘上的组合键 如:ctrl+c , ctrl+z ,ctrl+d |
常用信号(kill -l #列出所有信号):
Signal | Value | Comment |
SIGHUP | 1 | 终端退出时,此终端内的进程都将被终止 |
SIGINT | 2 | 中断进程,可被捕捉和忽略,几乎等同于sigterm,所以也会尽可能的释放执行clean-up,释放资源,保存状态等(CTRL+C) |
SIGQUIT | 3 | 从键盘发出杀死(终止)进程的信号 |
SIGKILL | 9 | 强制杀死进程,该信号不可被捕捉和忽略,进程收到该信号后不会执行任何clean-up行为,所以资源不会释放,状态不会保存 |
SIGTERM | 15 | 杀死(终止)进程,可被捕捉和忽略,几乎等同于sigint信号,会尽可能的释放执行clean-up,释放资源,保存状态等 |
SIGCHLD | 17 | 当子进程中断或退出时,发送该信号告知父进程自己已完成,父进程收到信号将告知内核清理进程列表。所以该信号可以解除僵尸进程,也可以让非正常退出的进程工作得以正常的clean-up,释放资源,保存状态等。 |
SIGSTOP | 19 | 该信号是不可被捕捉和忽略的进程停止信息,收到信号后会进入stopped状态 |
SIGCONT | 18 | 发送此信号使得stopped进程进入running,该信号主要用于jobs,例如bg & fg 都会发送该信号。可以直接发送此信号给stopped进程使其运行起来 |
SIGTSTP | 20 | 该信号是可被忽略的进程停止信号(CTRL+Z) |
SIGUSR1 | 10 | 用户自定义信号1 |
SIGUSR2 | 12 | 用户自定义信号2 |
第5节 利用信号实现跳板机功能
链接为: trap实现跳板机
第6节 进程与管道
无名管道(pipe):
在Linux下,你可以通过"|"连接两个程序,这样就可以用它来连接后一个程序的输入和前一个程序的输出,因此被形象地叫做个管道。父进程往第一个文件描述符里头写入东西后,子进程可以从第一个文件描述符中读出来。
实际上当我们输入这样一组命令的时候(echo "hi fd" | cat ),前面一个进程的输出关联(重定向)到管道的输出文件描述符,把后面一个进程的输入关联到管道的输入文件描述符。注意:两个命令都要同时存在。
进程echo将内容到标准输出(文件描述符为fd 1)连接到了管道的写入端,读取进程cat就将其标准输入(文件描述符为fd 0)连接到了管道的输出端。实际上,这两个进程并不知道管道的存在,它们只是从标准文件描述符中读取数据和写入数据。(文件描述符可以参考此处)
有名管道(named pipe)
有名管道实际上是一个文件(无名管道也像一个文件,虽然关系到两个文件描述符,不过只能一边读另外一边写),不过这个文件比较特别,操作时要满足先进先出,而且如果试图读一个没有内容的有名管道,那么就会被阻塞(等待数据过来),同样地,如果试图往一个有名管道里头写东西,而当前没有程序试图读它,也会被阻塞(等待数据被读取)。
有名管道与无名管道的区别:有名管道将无名管道的两边进程(同时存在的写与读)分别独立出来,通过一个中间的文件进行数据传递,因此有名管道特别适合有两个独立程序构成某些任务的模式(可以理解A程序实现菜单及对应的功能,B程序实现read读取用户选择,而有名管道实现B程序传参给A程序),如A程序(类似一个脚本无限的循环菜单,及菜单对应的功能)无限循环的读取有名管道文件,通过获取数据,对应执行那些事件(若没有数据,则阻塞从而避免无限循环导致资源浪费),B程序(类似read命令,读取用户的输入)则作为控制程序往有名管道文件输入指令。
窗口1 | 窗口2 |
[root@server01 tmp]# mkfifo fifo_a [root@server01 tmp]# echo "hello" > fifo_a [root@server01 tmp]# echo "hello again" > fifo_a |
[root@server01 tmp]# cat fifo_a hello [root@server01 tmp]# cat fifo_a hello again [root@server01 tmp]# |
第7节 进程的并发与文件锁(flock)~劝告锁(advisory lock)
Linux中软件、硬件资源都是文件(一切皆文件),文件在多用户环境中是可共享的。当多个进程共享一个资源进行操作时,会出现一些不可预见的情况。
代码(2000个1累加) | 效果(原来并发也可以做随机数哦,只是我用的不是神威,奢侈不起。) |
cat a.sh
|
[root@server01 ~]# bash a.sh 1999 [root@server01 ~]# bash a.sh 1366 [root@server01 ~]# bash a.sh 580 [root@server01 ~]# bash a.sh 78
[root@server01 ~]# bash a.sh
789
|