目录

一、程序与进程

1.程序如何变成进程

2.  Linux系统启动流程

3.进程状态

二、进程的产生

三、僵尸进程

什么是僵尸进程?

如何杀死僵尸进程

Linux进程趣图

四、进程的优先级和nice级别

进程与线程

进程和线程的关系如下所述:

进程和线程的区别如下所述:

 守护进程

五、Linux 进程间通信

​编辑

 信号 signal

常见的进程信号

Linux系统有7个运行级别(runlevel):

管道 pipe

 共享内存 shared memory

先入先出队列 FIFO

消息队列 Message Queue

套接字 Socket

六、系统启动的完整过程

七、查找命令的完整安装名称


一、程序与进程

程序(program)是一个普通文件,是为了完成特定任务而准备好的指令序列与数 据的集合,这些指令和数据以“可执行映像”的格式保存在磁盘中。例如:hello.c源程 序文件经过编译后产生a.out程序,其中a.out文件为可执行镜像格式,Linux 的/bin、/sbin、/usr/bin、/usr/sbin目录下保存着诸多的程序文件。

进程(process)是一个已经开始执行但还没终止的程序实例。Linux系统下使用ps 命令可以查看到当前正在执行的进程。每个进程包含有进程运行环境、内存地址空 间、进程ID、和至少一个被称为线程的执行控制流等资源。同一个程序可以实例化为 多个进程实体。操作系统中所有进程实体共享着计算机系统的CPU、外设等资源。

线程:操作系统进行运行调度的基本单位

也就是说进程是程序的动态执行,一旦运行就会有一个进程ID;

程序是一个静态的普通文件,里面包含为完成特定任务而准备的指令与数据,程序一旦运行结束就会将所占资源释放掉。

1.程序如何变成进程

程序是个静态的文件,进程是一个动态的实体,进程的状态会在运行过程中改变, 那么程序是如何变为一个进程的呢?

通常在Shell中输入命令运行就包含了程序到进程转换的过程。整个转换过程主要 包含以下3个步骤:

(1)查找命令对应程序文件的位置

(2)使用fork()函数创建一个新进程

(3)在新进程中调用exec族函数装载程序文件,并执行程序文件的main()函数

2.  Linux系统启动流程

1、硬件加电自建 BIOS

2、读取MBR信息 512bytes

3、 boot程序加载内核

4、内核初始化进程 init systemd runlevel 

5、建立终端: 虚拟终端

6、用户登录

     守护进程:在某些用户空间中,即使用户退出登录,仍然会有一些进程在后台运行

3.进程状态

Linux是一个多用户多任务的操作系统,可以同时运行多个用户的多个程序,就必 然会产生多进程,而每个进程会有不同的状态。Linux的进程有以下6种状态:

D = uninterruptible sleep D:不可中断的深度睡眠状态,处于这种状态的进程不能响应异步信号;不可中断 的等待状态最典型的例子就是进程等待磁盘I/O操作
R = running R:进程处于运行态或就绪状态,只有在该状态的进程才可能在CPU上运行。而同 一时刻可能有多个进程处于可执行状态;
S = sleeping S:可中断的睡眠状态,处于这个状态的进程因为等待某种事件的发生而被挂 起。;这类进程处于阻塞状态,一旦达到某种条件,就会变为运行状态。同时该状态 的进程也会由于接收到信号而被提前唤醒进入到运行状态。
T = stopped by job control signal

T:暂停状态或跟踪状态;进 向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态(除非该进程本身处于ASK_UNINTERRUPTIBLE状态而不响应信号)

向进程发送一个SIGCONT信号,可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态。

X=TASK_DEAD - EXIT_DEAD X:退出状态,进程即将被销毁
Z = zombie Z:退出状态,进程成为僵尸进程

Linux系统使用ps -aux命令时可观察到进程的当前状态

 当一个进程调用 fork 函数生成另一个进程,原进程就称为父进程新生成的进程 则称为子进程。 Linux 系统中这样父子进程非常多,我们可以使用 pstree 命令查看系统上的进程 「谱系」。

二、进程的产生

        父进程(PPID)---复制自己的地址空间fork()创建一个新的子进程.

        子进程(PID)----每一个新进程会有一个PID

        所有的进程都可以创建子进程

        所有的进程的父进程是第一个系统进程的子进程。

三、僵尸进程

什么是僵尸进程?

前面提到过,在 Linux 环境中,我们是通过 fork 函数来创建子进程的。创建完毕 之后,父子进程独立运行,父进程无法预知子进程什么时候结束。

通常情况下,子进程退出后,它立即从内存中移除,但进程描述符仍然保留在内存中 (进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父 进程发送SIGCHLD(-17)信号,父进程会使用 wait 或 waitpid 函数进行回收子 进程的资源,并获得子进程的终止状态。在wait()调用之后,僵尸进程就完全从内存 中移除了。因此一个僵尸进程存在于其终止到父进程调用wait()函数。

但是,如果父进程先于子进程结束,则子进程成为孤儿进程。孤儿进程将被 init 进程 (进程号为1)领养,并由 init 进程对孤儿进程完成状态收集工作。

而如果子进程先于父进程退出,同时父进程太忙了,无瑕回收子进程的资源,子进程 残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程,如下图所示:

如果子进程在退出的时候父进程还处于等待状态,父进程还未苏醒,因此没人给子进程「收尸」,所以它就变成 了僵尸进程。

在CentOS7中PID的默认最大值为131072,PID的范围是0~4194304。如果僵尸进 程持续增加可能用占用大量PID,影响到新进程的创建。

如何杀死僵尸进程

对于普通进程,我们可以通过使用 kill 命令来杀死它们。 kill 命令它还有几个兄 弟,比如 pkill 和 killall ,虽然它们名称里都带 kill 这样杀气腾腾的字眼, 但它们实际上是被设计为向一个或多个进程发送信号。

在未指定的情况下,这几个命令默认发送的是 SIGTERM 信号。

普通进程可以被 kill ,但僵尸进程是不行的。为什么?因为僵尸进程本身就已经 「死」过一次了!如果还可以再「死」,那「僵尸」这个名号就没多大意义了。

僵尸进程其实已经就是退出的进程,因此无法再利用kill命令杀死僵尸进程。僵尸进 程的罪魁祸首是父进程没有回收它的资源,那我们可以想办法它其它进程去回收僵尸 进程的资源,这个进程就是 init 进程。

因此,我们可以直接杀死父进程,init 进程就会很善良地把那些僵尸进程领养过来, 并合理的回收它们的资源,那些僵尸进程就得到了妥善的处理了。


例如,如果 PID 5878 是一个僵尸进程,它的父进程是 PID 4809,那么要杀死僵尸 进程 (5878),您可以结束父进程 (4809):

kill -9 4809   #4809 is the parent, not the zombie

注意:杀死父进程时要非常小心,如果一个进程的父进程就是 PID 1 ,并且你还杀死 了它,那么系统将直接重启!这将是一个更可怕的故事!

Linux进程趣图

 

四、进程的优先级和nice级别

Linux是一个多用户多任务的操作系统。所有的任务都放在一个队列中,操作系统根 据每个任务的优先级为每个任务分配合适的时间片(时间片是进程在处理器中执行时 间)。每个时间片很短,用户根本感觉不到是多个任务在运行,从而使所有任务共享 系统资源。因此,Linux可以在一个任务还未执行完时,短暂挂起此任务,去执行另 一个任务,过一段时间后再回来处理这个任务,直到这个任务完成才从任务队列中去 除。这就是单CPU操作系统多任务的概念

在单CPU的环境下,虽然系统可以运行多个任务,但是在某个时间点,CPU只能执行 一个进程(串行执行)。而在多CPU的环境下,某个时间点可以有多个进程同时执行 (并行执行)。这就是多核CPU比单核CPU处理性能高的原因

在CPU执行每个任务的过程中,进程优先级决定了进程在CPU中的执行顺序。优先级 越高的进程被处理器执行的的机会越大。

进程优先级由动态优先级和静态优先级决定,根据进程的行为,内核使用启发式算法 决定开启或关闭动态优先级。可以通过nice级别直接修改进程的静态优先级,拥有越 高的静态优先级的进程会获得更长的时间片。Linux支持的nice级别从19(最低优先 级)到-20(最高优先级),默认为0。只有root用户才能把进程的nice级别调整为 负数(让其具备较高优先级)。

进程与线程

进程是程序的一次动态执行,它对应着从代码加载、执行至执行完毕的一个完整的过 程,是一个动态实体,它有自己的声明周期。进程因创建而产生,因调度而运行,因 等待资源或事件而被处于等待状态,因完成任务而被撤销。

线程是进程的一个实体,是CPU调度和分派的基本单位它是比进程更小的能独立运 行的基本单位一个线程可以创建和撤销另一个线程,同一个进程的多个线程之间可 以并发执行


进程和线程的关系如下所述:

  • 一个线程只能属于一个进程,而一个进程可以由多个线程
  • 线程是资源分配的最小单位,同一个进程的所有线程共享该进程的所有资源
  • 真正在处理器上运行的是线程

    进程和线程的区别如下所述:

  • 调度:线程作为调度和分配的基本单位,进程最为拥有资源的基本单位
  • 并发性:不仅进程可以并发执行,同一个进程的多个线程也可以并发执行
  • 拥有资源:进程是拥有资源的基本单位,线程不拥有系统资源,但可以访问隶属 于进程的资源
  • 系统开销:在创建和撤销进程时,由于系统都要为之分配和回收资源,导致系统 的开销明显大于创建或撤销线程时的开销

 守护进程

在某些用户空间中,即使用户退出登录,仍然会有一些后台进程在运行,这些进程被 称为 守护进程(daemon) 。(例如:httpd   atd)

systemctl status cornd          ##查看指定服务的状态

Linux 中有一种特殊的守护进程被称为 计划守护进程(Cron daemon) ,计划守护进程 可以每分钟醒来一次检查是否有工作要做,做完会继续回到睡眠状态等待下一次唤 醒。

Linux的大多数服务器就是用守护进程实现的。比如,Web服务器httpd等。同时, 守护进程完成许多系统任务。比如,作业规划进程crond, 打印进程lpd等。下面将 我理解的Linux下守护进程做一些解释和说明。同时将网上一个常用的解说Linux下守 护进程的程序作为实例介绍一下。

五、Linux 进程间通信

Linux 进程间的通信机制通常被称为 Internel-Process communication,IPC 下面 我们来说一说 Linux 进程间通信的机制,大致来说,Linux 进程间的通信机制可以分 为 6 种

 信号 signal

信号是 UNIX 系统最先开始使用的进程间通信机制,因为 Linux 是继承于 UNIX 的, 所以 Linux 也支持信号机制,通过向一个或多个进程发送 异步事件信号 来实现,信号 可以从键盘或者访问不存在的位置等地方产生;信号通过 shell 将任务发送给子进 程。

你可以在 Linux 系统上输入 kill -l 来列出系统使用的信号,下面是我提供的一些 信号

 进程可以选择忽略发送过来的信号,但是有两个是不能忽略的: SIGSTOP SIGKILL 信号

SIGSTOP 信号会通知当前正在运行的进程执行关闭操作,SIGKILL 信号会通知当前 进程应该被杀死除此之外,进程可以选择它想要处理的信号,进程也可以选择阻止 信号,如果不阻止,可以选择自行处理,也可以选择进行内核处理。如果选择交给内 核进行处理,那么就执行默认处理。

操作系统会中断目标程序的进程来向其发送信号、在任何非原子指令中,执行都可以 中断,如果进程已经注册了新号处理程序,那么就执行进程,如果没有注册,将采用 默认处理的方式。

例如:当进程收到 SIGFPE 浮点异常的信号后,默认操作是对其进行 dump(转储) 和 退出。信号没有优先级的说法。如果同时为某个进程产生了两个信号,则可以将它们 呈现给进程或者以任意的顺序进行处理。

常见的进程信号

Linux系统有7个运行级别(runlevel):

  • 运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
  • 运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆
  • 运行级别2:多用户状态(没有NFS)
  • 运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式
  • 运行级别4:系统未使用,保留
  • 运行级别5:X11控制台,登陆后进入图形GUI模式
  • 运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动

忘记超级用户密码可以进入单用户工作状态去修改

  • init 0可以实现关机
  • init 6可以实现重启
  • init 3可以实现切换到文本界面
  • init 5可以实现切换到图形化界面

在图形化界面也可以通过快捷键CRTL+ALT+F3实现切换到文本界面,但是反过来不 行,因为文本界面不支持这种快捷键操作

管道 pipe

Linux 系统中的进程可以通过建立管道 pipe 进行通信 在两个进程之间,可以建立一个通道,一个进程向这个通道里写入字节流,另一个进 程从这个管道中读取字节流。管道是同步的,当进程尝试从空管道读取数据时,该进 程会被阻塞,直到有可用数据为止。shell 中的 管线 pipelines 就是用管道实现 的,当 shell 发现输出 

sort <f | head

它会创建两个进程,一个是 sort,一个是 head,sort,会在这两个应用程序之间建 立一个管道使得 sort 进程的标准输出作为 head 程序的标准输入。sort 进程产生的 输出就不用写到文件中了,如果管道满了系统会停止 sort 以等待 head 读出数据

管道实际上就是 | ,两个应用程序不知道有管道的存在,一切都是由 shell 管理和控 制的。 

 共享内存 shared memory

两个进程之间还可以通过共享内存进行进程间通信,其中两个或者多个进程可以访问 公共内存空间。两个进程的共享工作是通过共享内存完成的,一个进程所作的修改可 以对另一个进程可见(很像线程间的通信)。

  •  在使用共享内存前,需要经过一系列的调用流程,流程如下
  • 创建共享内存段或者使用已创建的共享内存段 (shmget())
  • 将进程附加到已经创建的内存段中 (shmat())
  • 从已连接的共享内存段分离进程 (shmdt())
  • 对共享内存段执行控制操作 (shmctl())

先入先出队列 FIFO

先入先出队列 FIFO 通常被称为 命名管道(Named Pipes) ,命名管道的工作方式与常 规管道非常相似,但是确实有一些明显的区别。未命名的管道没有备份文件:操作系 统负责维护内存中的缓冲区,用来将字节从写入器传输到读取器。一旦写入或者输出 终止的话,缓冲区将被回收,传输的数据会丢失。相比之下,命名管道具有支持文件 和独特 API ,命名管道在文件系统中作为设备的专用文件存在。当所有的进程通信完 成后,命名管道将保留在文件系统中以备后用。命名管道具有严格的 FIFO 行为。

 写入的第一个字节是读取的第一个字节,写入的第二个字节是读取的第二个字节,依 此类推。

消息队列 Message Queue

消息队列是用来描述内核寻址空间内的内部链接列表。可以按几种不同的方式将消息 按顺序发送到队列并从队列中检索消息。每个消息队列由 IPC 标识符唯一标识。消息 队列有两种模式,一种是 严格模式 , 严格模式就像是 FIFO 先入先出队列似的,消息 顺序发送,顺序读取。还有一种模式是 非严格模式 ,消息的顺序性不是非常重要。

套接字 Socket

还有一种管理两个进程间通信的是使用 socket ,socket 提供端到端的双相通信。 一个套接字可以与一个或多个进程关联。就像管道有命令管道和未命名管道一样,套 接字也有两种模式,套接字一般用于两个进程之间的网络通信,网络套接字需要来自 诸如 TCP(传输控制协议) 或较低级别 UDP(用户数据报协议) 等基础协议的支持。

套接字有以下几种分类

  • 顺序包套接字(Sequential Packet Socket) :此类套接字为最大长度固定的数 据报提供可靠的连接。此连接是双向的并且是顺序的。
  • 数据报套接字(Datagram Socket) :数据包套接字支持双向数据流。数据包套接 字接受消息的顺序与发送者可能不同。
  • 流式套接字(Stream Socket) :流套接字的工作方式类似于电话对话,提供双向 可靠的数据流。
  • 原始套接字(Raw Socket) :可以使用原始套接字访问基础通信协议。

六、系统启动的完整过程

七、查找命令的完整安装名称

如果直接yum一个命令有时候可能找不到,这时候使用下面的命令就可以查到他的安装包名称

yum search 命令

例如安装pstree命令,直接使用yum install -y pstree是无法安装的,要先使用yum search pstree来查找他的完整安装名称,再进行安装。 

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐