[Linux] The concept of process (1)

  1. von Neumann architecture

Our common computers, such as notebooks. Most of our uncommon computers, such as servers, obey the von Neumann system.

A few points must be emphasized about von Neumann:

The storage here refers to the memory
Regardless of the cache situation, the CPU here can and can only read and write memory, and cannot access peripherals (input or output devices)
Peripherals (input or output devices) to input or output data can only be written to or read from memory.
In a word, all devices can only deal directly with memory (in order to improve the efficiency of the whole machine)
The program must be loaded into the memory to run, because the CPU needs to execute and access our code, and can only read the memory because of the architecture regulations.
  1. operating system

2.1. Management

An operating system is software that manages hardware and software resources.

Why manage?

Provide users with a good/stable/efficient/safe execution environment through reasonable management of software and hardware resources.

How to manage?

The operating system detects the data of each hardware and manages the hardware through the driver program.
The data of each piece of hardware will be abstracted into a structure and stored in the form of a linked list.
All the management of the hardware is abstracted as the management of the structure.
The management idea of ​​the operating system is: first describe, then organize.
Description is to encapsulate all information in a structure (object-oriented thinking)
Organization is how he organizes (data structure)
Therefore, the essence of management is to manage data, and
the idea of ​​management is to first describe (language) and then manage (data structure).
The operating system also manages the software. Same thing: describe first, then organize.

2.2. System calls

From the perspective of development, the operating system will appear as a whole to the outside world, but it will expose part of its interface for upper-layer development. This part of the interface provided by the operating system is called a system call.
In the use of system calls, the functions are relatively basic, and the requirements for users are relatively high. Therefore, interested developers can properly encapsulate some system calls to form a library. With a library, it is very beneficial for higher-level users or Developers carry out secondary development.
The library of c language and c++ is one of them.
The system call interface is a c-style interface, because the operation is written in C language. The function call provided to us by os through C language.

Why provide services to users in the form of interfaces?

In order to protect the operating system , if any user, any program can access the core of the operating system, or modify the core of the operating system, it will be messy. Therefore, the operating system will appear as a whole to the outside world and only provide interface services.
  1. process introduction

From the perspective of the operating system, it is necessary to manage the process, and it also needs to be described first, and then managed.

3.1. Basic concepts

Textbook concepts: an execution instance of a program, a program being executed, etc.
Kernel point of view: Acts as the entity that allocates system resources (CPU time, memory).

3.2. Describe the process - PCB

Process information is placed in a data structure called a process control block, which can be understood as a collection of process attributes
It is called PCB (process control block) in the textbook, and the PCB under the Linux operating system is: task_struct
task_struct is a data structure of the Linux kernel that is loaded into RAM (memory) and contains process information.

task_struct content classification:

标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息等------
  1. 查看进程

4.1.ps ajx

ps ajx :查看系统中所有的进程
先写一个程序让他运行起来成为一个进程。
#include<stdio.h>
#include<unistd.h>
int main()
 {
     printf("heello world!\n");
     int i=0;
     while(1)
     {
         ++i;                                                                            
         printf("%d我是一个进程!\n",i);
         sleep(1);
     }
     return 0;
 }
一般使用grep(行过滤工具)
ps ajx | grep“mytest"
ps ajx | grep“mytest" | grep -v grep (可以添加一个过滤条件)去掉grep那个进程。

这每一行都是什么意思?

grep ajx | head -1 :查看第一行,

怎么杀死这个程序?

kill -9 【进程id】:杀死这个id。

4.2.通过 /proc 系统文件夹

ls /proc :查看proc目录里面的我文件。
存放的是以进程pid命名的目录,只要进程在运行,就一定可以在里面找到,以进程pid命名的一个目录。
进程的信息可以通过 /proc 系统文件夹查看
是一个内存级的进程目录。
大多数进程信息使用 top和ps这些用户级工具来获取。
进程执行起来之后 ,已经被加载到了内存中 ,现在即使我们删掉,原来的exe文件,还是可以跑起来的。

  1. 系统接口:获取进程pid

进程id(PID)
父进程id(PPID)
#include<stdio.h>    
#include<unistd.h>    
#include<sys/types.h>    
int main()    
{    
    printf("heello world!\n");    
    int i=0;    
    while(1)    
    {    
        ++i;    
        printf("%d我是一个进程!",i);    
        printf("pid:%d, ppid:%d \n",getpid(),getppid());                                    
        sleep(1);    
    }    
    return 0;    
}  
每一次重启进程,他的进程pid就会发生改变。
父进程的id一般是不会变换的。
命令行上启动的进程一般没有特殊情况,他的父进程就是bash(命令行解释器)
命令行解释器(bash)通过创建子进程的方式来执行命令 。这样子进程挂掉了不会影响父进程。
  1. 系统调用:创建进程-fork

fork():创建子进程。
fork()的返回值,很特别。
父进程返回子进程的pid;
子进程返回0;
fork();之后,会有父进程+子进程两个进程执行后续的代码。
fork后续的代码。会被父子进程共享!
通过返回值的不同,让父子进程执行后续共享的代码的一部分,这就是多进程。
  1. 进程状态

7.1.前提知识

由于操作系统的管理方式是:先描述,再组织。
每一个进程在加载进入内存的时候,操作系统会马上给进程创建进程PCB,并且完成属性记录。
操作系统会维护一个进程队列,队列中的每一个节点就是进程的PCB,方便所有进程的管理。
一个cpu一个运行队列,每一个硬件也要维护一个队列,(每一个外设的数量都有限)
让进程入运行队列,就是把进程的 PCB 入队列,而不是把进程的代码和数据入队列。
PCB里面存有,进程的代码地址,数据,进程状态----。

什么是状态?

状态是进程内部的属性,它一定在进程task_struct中存在。可能就是结构体中的一个整形变量。(1:run,2:sleep,3:dead----)
所谓进程不同的状态,本质上是进程在不同的队列中,等待某种资源。

7.1.1.运行状态和阻塞状态

我们把在CPU运行队列中等待CPU资源的进程叫做运行状态。
进程在等待某种外设资源叫做阻塞状态,在某种外设的等待队列中。

7.1.2.挂起状态

因为外设的速度很慢,假如很多进程同在等待某一个外设的时候,这个外设的队列中后面的某些进程就不会立即被调用,要等待很长时间,万一此时内存不够了怎么办,就先把某些进程的代码和数据暂时保存在磁盘上,这样就为内存节省出了一部分空间。
上述说的是阻塞挂起状态,还有一种挂起状态是运行挂起(就绪挂起),但是运行挂起的概率很小很小,除非内存是真的太小了。
挂起状态通常和别的状态一起使用,比如阻塞挂起,运行挂起,新建挂起---。总归就是为了解决内存不够的用的问题。
上面我们讲解的是所有计算机都有的状态。

7.2.Linux的具体进程状态。

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)

下面的状态在kernel源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

7.2.1.运行状态

R运行状态(running) : 并不意味着进程一定在运行中,它表明进程要么是在 运行中 要么 在运行队列 里。

7.2.2.睡眠状态

S睡眠状态(sleeping) : 意味着 进程在等待事件完成 (这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。 (睡眠状态 (S)== 阻塞状态的一种)
进程如果有访问外设,大部分都是S状态。

7.2.3.磁盘休眠状态(Disk sleep)

D磁盘休眠状态(Disk sleep): 有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
S是浅度睡眠,D是深度睡眠。
S是可以被终止的,但是D是不能被终止的(操作系统也无法杀死它),只能通过断电,进程自己执行完。
想看到D状态的进程,必须在高 io的情况下!!我怕机器挂掉,我就不演示了。
D状态是操作系统给进程发的 一个免死金牌,用于执行一些重要的数据 io,普通进程杀死就杀死了,没问题。假如一个进程正在向磁盘写数据,而且这个数据很重要。这个进程一般会进入D状态。

7.2.4.停止状态(stopped)

T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
很多教材都没有暂停状态。很多教材把停止状态归结带挂起或者阻塞当中了。

kill 不止能杀死进程,还有很多指令

kill -l : 查看所有 kill指令。
kill -19 【进程pid】 :暂停进程。
kill -18 【进程pisd} : 继续运行进程。
kill -9 【进程pid】 : 杀死进程

7.2.5.跟踪停止(tracing stop)

t跟踪停止(tracing stop): 一种特殊的停止状态.
先将一个程序调试起来
查看进程状态
这个也是一种暂停状态,表示当前进程正在被追踪.
这就说明了为什么进程可以被调试.

7.2.6.死亡状态(dead)

X死亡状态(dead): 这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
我们无法看到这种状态,因为操作系统很快,对于一个已经死亡的状态,操作系统会迅速的回收它.我们无法看到.

7.2.7.僵尸进程(zombie)

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程.
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码.
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸进程,虽然是一种正常状态.但是如果父进程一直不回收子进程的资源,这就不正常了,他就一直浪费系统资源,造成资源浪费.

僵尸进程危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎 么样了。可父进程如果一直不读取子进程的退出结果,那子进程就一直处于Z状态.
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话 说,Z状态一直不退出,PCB一直都要维护.
那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费,因为数据结构 对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空 间!
这就造成了内存泄漏.

  1. 进程状态查看

ps aux / ps ajx

  1. 孤儿进程

子进程退出后,还在运行的父进程没有读取他的返回结果,子进程会变为僵尸进程.
假如子进程还在运行,父进程退出了,子进程退出后,由谁来读取它的返回结果?
父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号init进程领养,当然要有init进程回收喽。
这个1号进程就是操作系统.

演示:

操作系统为什么要领养孤儿进程,如果操作系统不领养,就没有进程回收他的结果,就会出问题,内存泄漏.

  1. 进程优先级

cpu资源分配的先后顺序,就是指进程的优先权(priority)
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能.
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整 体性能

为什么存在进程优先级?

因为资源少,需要排队,或者确立优先级.

Linux进程优先级的特点?

优先级本质就是PCB里面的一个整数(也有可能是几个),也是属性的一种.
Linux中是由两个整数确定的.PRI和NI

在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:

PRI and NI

PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小 进程的优先级别越高
nice值(NI)其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new)=PRI(old)+nice 当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
所以,调整进程优先级,在Linux下,就是调整进程nice值 nice其取值范围是-20至19,一共40个级别。

PRI vs NI

需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进 程的优先级变化。
可以理解nice值是进程优先级的修正修正数据

用top命令更改已存在进程的nice:

top (sudo top) 进入top后按“r”–>输入进程PID–>输入nice值
注意老的优先级是不变的 一直是80,上图中虽然PRI是79 但是我们再次把NI改为哦1的时候PRI又会变为81, 注意每一次设置NI的时候PRI都会先变为80.

其他概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级 . 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进.
  1. 进程切换

进程在运行的时候会产生很多的临时数据,会存在CPU的各种寄存器中,
一个进程在运行的时候不是一直占有CPU资源,现在的CPU一般都是采用时间片轮转,每个进程都有自己的时间片.
当一个进程时间片用完,需要释放CPU资源的时候,就要保留CPU寄存器中的数据, 这叫上下文保护;
当一个进程重新分配到时间片,会把是上下文保护保留的数据重新恢复到CPU的寄存器中, 这叫上下文恢复.
进程在切换的时候,要进行上下文保护,当进程在恢复运行的时候,就要进行上下文恢复.
寄存器是每个进程共有的,但是寄存器里面的数据是每个进程各自有一份的,叫进程上下文.
  1. 环境变量

12.1.基本概念

环境变量(environment variables)一般是指在操作系统中用来 指定操作系统运行环境的一些参数
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但 是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

12.2.常见环境变量

PATH : 指定命令的搜索路径

linux中的指令其实就是一个可执行程序,和我们写的程序编译出来的可执行程序其实一样的.
为什么我们自己写的程序需要加 ./mytest
但是一般指令系统指令不需要加 ./ 这就是因为有环境变量的存在.
首先要运行指令最重要的是要找到指令,指令发出后操作系统首先会在PATH环境变量规定的路劲下寻找,找到执行它,找不到便报错.
而我们自己写的程序没有在系统规定的路径下,也就只能添加路劲执行.
正是因为系统中有PATH这样一个环境变量,我们的指令会默认去PATH指定的路劲下去找,所有指令不需要加路劲就可以执行.

所有想要我们自己写的指令不添加路劲直接执行,有两种方法,

一是:把我们的可执行拷贝到PATH存在的路劲下
二是:修改PATH,给PATH添加一个搜索路径.

HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

HOSTNAME :主机名称

LOGNAME:当前登录主机的用户.

PWD:当前所在路径.

HISTSIZE :记载历史命令最多个数.

SHELL : 当前Shell,它的值通常是/bin/bash

USER: 当前登录用户

12.3.查看环境变量方法

echo $NAME //NAME:你的环境变量名称
env :查看所有的环境变量

12.4.环境变量相关的命令

1. echo: 显示某个变量值
2. export: 设置一个新的环境变量
3. env: 显示所有环境变量
4. unset: 清除环境变量
5. set: 显示本地定义的shell变量和环境变量

打印的比较多,不仅打印环境变量,本地变量也会打印.

12.5.环境变量的组织方式

每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串
每一个字符串都是一个环境变量.

12.6.代码中获取环境变量

getenv():直接获取

getenv("NAME") 函数,//NAME是环境变量的名称
还有putenv,以后讲解.
返回值是char* 类型的,可以直接打印.

char ** environ:环境变量表

这是系统给我们创建的环境变量表格,通过environ指向这个表格的起始端.供我们调用,是一个全局变量
和mian函数的第三个参数相同

  1. main函数的三个参数

13.1.argc 和argv[]:命令行参数表

这两个参数就是命令行列表,存放我们调用这个程序对应的可执行文件名称和对应的参数
给不同的参数就可以执行不同的代码 .
这就是为什么,我们的系统指令中可以给不同的选项执行不同的功能.

13.2.env:环境变量表

父进程通过env(环境变量表)将环境变量传递给子进程,并且以NULL结尾.
我们写一个程序打印一下:
执行之后可以清楚的看到env就是环境变量表,保存这从父进程/操作系统哪里继承过来的各种环境变量.

  1. 提问

Guess you like

Origin blog.csdn.net/zxf123567/article/details/129632345