Linux 进程基本概念 什么是进程

 

操作系统(Operator System)

概念

操作系统,简称OS,是一个基本的程序集合,用来维护计算机基本的运行。操作系统主要由内核(进程管理、内存管理、文件管理、驱动管理)其他程序(如函数库、shell等组成)。OS的目的是为了让计算机与硬件交互,管理所有的软硬件资源来为用户(应用程序)提供一个良好的执行环境。通俗的来讲就是一款“职业”的管理软件,如图:

计算机管理硬件的方式:先将硬件用struct结构体描述起来,然后使用链表等高效的数据结构将他们组织起来。所以同理,操作系统管理进程的方式也是先将进程描述起来,再把进程组织起来!!

进程

什么是进程?

进程就是一个程序的执行实例,也就是正在执行的程序。在OS的眼里,进程就是一个担当分配系统资源(CPU时间、内存)的实体。

进程描述符-PCB

刚才我们提到OS管理硬件的方式是先描述、再组织。那么OS就是用一个进程控制块的数据结构(进程属性的集合)来描述进程信息的,也就是PCB(process control block),Linux操作系统下的PCB是 task_struct ,程序运行时它会被装载到RAM里来储存进程信息。

task_ struct内容

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

查看进程

ls /proc/进程PID

/proc 系统文件夹储存了进程的信息,我们可以通过 ls /proc 来查看,也可以用 ls /proc/进程PID 查看某个进程的进程信息

ps命令查找与进程相关的PID号:

ps a 显示现行终端机下的所有程序,包括其他用户的程序。

ps -A 显示所有程序。

ps c 列出程序时,显示每个程序真正的指令名称,而不包含路径,参数或常驻服务的标示。

ps -e 此参数的效果和指定"A"参数相同。

ps e 列出程序时,显示每个程序所使用的环境变量。

ps f 用ASCII字符显示树状结构,表达程序间的相互关系。

ps -H 显示树状结构,表示程序间的相互关系。

ps -N 显示所有的程序,除了执行ps指令终端机下的程序之外。

ps s 采用程序信号的格式显示程序状况。

ps S 列出程序时,包括已中断的子程序资料。

ps -t<终端机编号> 指定终端机编号,并列出属于该终端机的程序的状况。

ps u 以用户为主的格式来显示程序状况。

ps x 显示所有程序,不以终端机来区分

最常用的方法是ps aux,然后再通过管道使用grep命令过滤查找特定的进程,然后再对特定的进程进行操作。 

ps aux | grep test | grep -v grep

查看名为 test 的进程

进程状态

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 */
};
  • R运行状态(running) :表示进程要么是在运行中要么在运行队列里
  • S睡眠状态(sleeping):进程在等待当前事件完成(这⾥里的睡眠有时候也叫做可中断睡眠(interruptible sleep)
  • D磁盘休眠状态(disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。 
  • T停止状态(stopped) :可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运⾏行。
  • X死亡状态(dead) :只是⼀个返回状态,你不会在任务列表里看到这个状态。
  • t(tracing stop追踪状态): ptrace拦截和修改系统调用
  • Z(zombie):僵死状态

下面主要为大家介绍一下僵死状态:

僵尸进程 Z(zombie)

僵尸进程是处于僵死状态的进程(只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程便进入Z状态)

产生原因:子进程先于父进程退出,父进程(使用wait()系统调用)没有读取到子进程退出的返回代码,这时候子进程为了保存退出原因,因此进入僵死态不会释放所有资源。僵死进程会以终止状态保持在进程表中,并且会⼀直在等待父进程读取其退出状态代码。

危害:维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,假如⼀个父进程创建了很多子进程,但是不回收,就会造成内存资源的浪费,资源泄露。

下面为大家举一个例子:

#include <stdio.h>                                              
#include <stdlib.h>                                             
                                                                
int main()                                                      
{                                                              
    pid_t pid = fork();                                         
    if(pid < 0){                                                
        perror("fork");                                         
        return 1;                                                                     
   }                                                           
   else if(pid > 0){ //parent                                   
       printf("parent[%d] is sleeping\n", getpid());           
       sleep(30);                                              
   }                                                           
   else{                                                       
        printf("chile[%d] is begin Z\n", getpid());             
        sleep(5);                                               
        exit(EXIT_SUCCESS);                                     
   }                                                           
    return 0;                                                   
}

创建一个30秒的进程,子进程5秒后退出,子进程成为僵尸进程:

孤儿进程

相对于僵尸进程,那么必须提到的就是孤儿进程,同僵尸进程相反,假如父进程先于子进程退出,子进程便进入后台运行,成为孤儿进程,孤儿进程随后会被1号init进程领养并回收,也就是将其父进程变为init进程。

什么是1号init进程?

在Linux中有两种特殊的进程: 

进程0:Linux引导中创建的第一个进程,完成加载系统后,演变为进程调度、交换及存储管理进程。

进程1:init 进程,由0进程创建,完成系统的初始化。是系统中所有其它用户进程的祖先进程。

如何创建0号进程?

由于在创建进程时,程序一直运行在内核态,而进程运行在用户态,因此创建0号进程涉及到特权级的变化,即从特权级0变到特权级3,Linux是通过模拟中断返回来实现特权级的变化以及创建0号进程,通过将0号进程的代码段选择子以及程序计数器EIP直接压入内核态堆栈,然后利用iret汇编指令中断返回跳转到0号进程运行。

代码如下:

 move_to_user_mode(); //创建0号进程,开始进入0号进程,切换到特权级3运行
 if (!fork()){
        init();
    }//创建1号进程

下面为大家演示一下孤儿进程:

#include <stdio.h>                                              
#include <unistd.h>                                             
#include <stdlib.h>                                             
                                                               
int main()                                                      
{                                                               
    pid_t pid = fork();                                                            
                                                              
    if(pid < 0){                                                
        perror("fork");                                         
        exit(1);                                                
    }                                                           
    else if(pid == 0){                                          
        printf("I am child, pid is %d\n", getpid());            
        sleep(10);                                              
    }                                                           
    else{                                                       
        printf("I am parent, pid is %d\n", getpid());           
        sleep(5);                                               
        exit(0);                                                
    }                                                           
    return 0;                                                   
}

结果:

进程优先级

为什么要有进程优先级?

        因为进程的功能各有不同因此对cpu资源的要求也各有不同,因此针对进程的调度就有了优先级,我们把cpu资源分配的先后顺序叫做进程的优先权,优先权决定了cpu资源的优先分配权,优先权高的进程有优先执行权利。还可以把不重要的进程安排到某个指定的cpu上,可以大大改善系统整体性能。

查看系统进程

ps -l

 

  •     UID:代表执行者的身份
  •     PID:代表进程代号
  •     PPID:代表进程是由哪个进程发展衍生而来的,亦即父进程的代号
  •     PRI:代表这个进程可悲执行的优先级,其值越小越早被执行
  •     NI:代表这个进程的nice值

PRI and NI

看看系统怎么说?

PRI:进程优先级(程序被CPU执行的先后顺序,越小进程优先级越高)

NI:nice值(进程可被执行的优先级修正数值) 

取值范围-20至19,共40个级别

PRI(new) = PRI(old) + NI(nice) 

意义:nice为负时,该程序优先级值会变小,优先级变高,越快被执行。所以调整进程优先级在Linux下也就是调整nice值

进程的nice值并不是进程的优先级,只是进程nice值会影响到进程的优先级变化,nice值是进程优先级的附加/修正数据

修改进程优先级:

#include <stdio.h>                
int main()                        
{                                                       
    printf("pid is %d\n", getpid());                       
    while(1){                                           
        sleep(1);                                       
    }                                                   
    return 0;                     
}  

nice 和 renice 和 top

  • nice:开始执行程序就指定nice值

  • renice :调整已存在进程的nice

  • top:更改已存在进程的nice
  • 进⼊入top后按“r”–>输⼊入进程PID–>输⼊入nice值

 

进程其他概念:

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

环境变量

    概念:在操作系统中用来指定操作系统运行环境的一些参数,如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态、静态库在哪里,但是照样可以链接成功,生成可执⾏行程序,原因就是有相关环境变量帮助编译器进行查找。

    特征:环境变量在系统中通常具有全局特性

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

环境变量的组织方式

如图:每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串

通过代码如何获得环境变量  

1.通过第三方变量environ获取(extern char **environ;)    

   libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件之中,所以在使用时需要extern声明

#include <stdio.h>
      
int main(int argc, char *argv[])
{    
    extern char **environ;
    int i = 0;
    for(; environ[i]; i++){
        printf("%s\n", environ[i]);
    }
   return 0;                                                  
}

2.通过main函数第三个参数获取

#include <stdio.h>                                              

int main(int argc, char *argv[], char *env[])                       
{                                                               
    int i = 0;                                                  
    for(; env[i]; i++){                                         
        printf("%s\n", env[i]);                                 
    }                                                           
    return 0;                                                   
}     

3.getenv("PATH")  (或者putenv)

常用getenv和putenv函数来访问特定的环境变量。

#include <stdio.h>
#include <stdlib.h>
      
int main()
{    
    printf("%s\n", getenv("PATH"));
    return 0;                                                  
}

环境变量通常具有全局属性,可以被子进程继承下去

#include <stdio.h>
#include <stdlib.h>
     
int main()
{    
    char * env = getenv("MYENV");
    if(env){
        printf("%s\n", env);
    }
   return 0;                                                  
}    

运行后为什么没有任何结果?原因是环境变量MYENV根本不存在。

现在我们导出环境变量后再次运行:发现结果成功

下面再为大家演示一下删除环境变量

 

 

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值