进程基本概念

基本概念

  进程是程序的一个执行实例,是一个正在执行的程序。
  进程能分配处理器并由处理器执行的实体。
  如果从内核的角度看的话:进程是分配系统资源的单位。当一个程序被加载到内存之后并为他分配一个PCB(进程控制块),就称之为进程。在Linux中PCB就是一个名字叫做task_struct的结构体,我们叫他“进程描述符”。它里面有进程执行的所有信息,所以CPU对task_struct进行管理就相当于在对进程进行管理。

  • 程序:程序是静止的,是磁盘上的一个文件。
  • 课本知识:程序的一个执行实例,正在执行的程序等(动态的)。
  • 内核观点:担当分配系统资源(CPU时间、内存)的实体。
  • 程序一旦被操作系统加载到内存中,那么程序就变成了进程。
  • 进程信息放在一个叫进程控制块(PCB)的数据结构中,可以理解为进程属性的集合。
  • Linux中描述进程的结构体叫做task_struct。
  • task_struct是Linux内核的一种数据结构,会被装载到RAM(内存)里并且包含着进程的信息

  在linux中,每一个进程都有一个进程描述符,这个“进程描述符”是一个结构体名字叫做task_struct,在task_struct里面保存了许多关于进程控制的信息。
  task_struct是Linux内核的一种数据结构,它会被装载到RAM里并包含进程的信息。每个进程都把它的信息放在task_struct这个数据结构里面,而task_struct包含以下内容:

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

  因此,可以说进程是由程序代码和相关数据还有进程控制块组成。对于一个单处理器计算机,在任何时间最多只有一个进程在运行,正在运行的这个进程状态为运行态。

task_struct定义:

struct task_struct {
volatile long state;        //说明了该进程是否可以执行,还是可中断等信息
unsigned long flags;        //Flage 是进程号,在调用fork()时给出
int sigpending;             //进程上是否有待处理的信号
mm_segment_t addr_limit;    //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
                            //0-0xBFFFFFFF for user-thead
                            //0-0xFFFFFFFF for kernel-thread
//调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
volatile long need_resched;
int lock_depth;             //锁深度
long nice;                  //进程的基本时间片
//进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
unsigned long policy;
struct mm_struct *mm;       //进程内存管理信息
int processor;
//若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
unsigned long cpus_runnable, cpus_allowed;
struct list_head run_list;          //指向运行队列的指针
unsigned long sleep_time;           //进程的睡眠时间
//用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
struct task_struct *next_task, *prev_task;
struct mm_struct *active_mm;
struct list_head local_pages;       //指向本地页面      
unsigned int allocation_order, nr_local_pages;
struct linux_binfmt *binfmt;        //进程所运行的可执行文件的格式
int exit_code, exit_signal;
int pdeath_signal;                  //父进程终止时向子进程发送的信号
unsigned long personality;
//Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
int did_exec:1; 
pid_t pid;                          //进程标识符,用来代表一个进程
pid_t pgrp;                         //进程组标识,表示进程所属的进程组
pid_t tty_old_pgrp;                 //进程控制终端所在的组标识
pid_t session;                      //进程的会话标识
pid_t tgid;
int leader;                         //表示进程是否为会话主管
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
struct list_head thread_group;      //线程链表
struct task_struct *pidhash_next;   //用于将进程链入HASH表
struct task_struct **pidhash_pprev;
wait_queue_head_t wait_chldexit;    //供wait4()使用
struct completion *vfork_done;      //供vfork() 使用
unsigned long rt_priority;          //实时优先级,用它计算实时进程调度时的weight值

//it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
//设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
//it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
//当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
//信号SIGPROF,并根据it_prof_incr重置时间.
//it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
//状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
//it_virt_incr重置初值。
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_value;
struct timer_list real_timer;       //指向实时定时器的指针
struct tms times;                   //记录进程消耗的时间
unsigned long start_time;           //进程创建的时间
//记录进程在每个CPU上所消耗的用户态时间和核心态时间
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; 
//内存缺页和交换信息:
//min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
//设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
//cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
//在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1;                //表示进程的虚拟地址空间是否允许换出
//进程认证信息
//uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
//euid,egid为有效uid,gid
//fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
//系统的访问权限时使用他们。
//suid,sgid为备份uid,gid
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups;                        //记录进程在多少个用户组中
gid_t groups[NGROUPS];              //记录进程所在的组
//进程的权能,分别是有效位集合,继承位集合,允许位集合
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
struct rlimit rlim[RLIM_NLIMITS];   //与进程相关的资源限制信息
unsigned short used_math;           //是否使用FPU
char comm[16];                      //进程正在运行的可执行文件名
//文件系统信息
int link_count, total_link_count;
//NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
struct tty_struct *tty;
unsigned int locks;
//进程间通信信息
struct sem_undo *semundo;           //进程在信号灯上的所有undo操作
struct sem_queue *semsleeping;      //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
//进程的CPU状态,切换时,要保存到停止进程的task_struct中
struct thread_struct thread;
//文件系统信息
struct fs_struct *fs;
//打开文件信息
struct files_struct *files;
//信号处理函数
spinlock_t sigmask_lock;
struct signal_struct *sig;      //信号处理函数
sigset_t blocked;               //进程当前要阻塞的信号,每个信号对应一位
struct sigpending pending;      //进程上是否有待处理的信号
unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
u32 parent_exec_id;
u32 self_exec_id;

spinlock_t alloc_lock;
void *journal_info;
};

进程的创建和终止

  • 进程的创建:
      当一个新进程添加到那些正在被管理的进程集合中去时,操作系统需要建立用于管理该进程的数据结构,并在内存中给它分配地址空间,这些行为构成了一个新进程的创建过程。通常会有以下4个事件导致创建一个新进程:

描述

  • 进程的终止:
      任何一个计算机都必须为进程提供表示其完成的方法,批量处理作业中应该包含一个Halt指令或用于终止操作系统显示服务调用来终止。在前一种情况下,Halt指令将产生一个中断,警告操作系统一个进程已经完成。对交互式应用程序,用户的行为将指出何时进程完成。在有些操作系统中,进程可以被创建它的进程终止,或当父进程终止而终止。以下是进程终止的几种情况:

描述

查看进程

描述

  • PID:pid_t getpid(void);得到进程的PID
    PID是程序被操作系统加载到内存成为进程后动态分配的资源。
    每次程序执行的时候,操作系统都会重新加载,PID在每次加载的时候都是不同的。

  • PPID:pid_t getppid(void);得到进程的PPID
    PPID是程序的父进程号。

  • 一个进程创建的另一个新进程称为子进程。相反地,创建子进程的进程称为父进程。对于一个普通的用户进程,它的父进程就是执行它的那个Shell,对于Linux而言,Shell就是bash。

  • 所有进程的祖先为init进程。

创建一个进程:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int mian(){
    while(1){
        sleep(1);
    }
    return 0;
}

描述

通过系统调用获取进程标识符

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int mian(){
    pid_t pid = getpid();
    printf("pid:%d\n",pid);
    pid_t ppid = getppid();
    printf("ppid:%d\n",ppid);
    while(1){
        sleep(1);
    }
    return 0;
}

描述

ps axj | grep ./a.out 

描述

通过系统调用创建进程

fork特点:

  1. 一次调用有两个返回值,父进程返回子进程PID,子进程返回0。

  2. 父进程和子进程都从fork执行结束后的位置继续执行。

  3. 子进程以父进程为模板(PCB,数据和代码)。写时拷贝

  4. 父子进程执行的先后顺序不确定,取决于操作系统调度器。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int mian(){
    int ret = fork();
    printf("pid: %d, ppid: %d, ret: %d\n", getpid(), getppid(), ret);
    while(1){
        sleep(1);
    }
    return 0;
}

描述

使用if进行分流:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int mian(){
    int ret = fork();
    if(ret < 0){
        perror("fork");
        return 1;
    }
    else if(ret == 0){
        printf("I am child: pid: %d, ppid: %d, ret: %d\n", getpid(), getppid(), ret);
    }
    else{
        printf("I am father: pid: %d, ppid: %d, ret: %d\n", getpid(), getppid(), ret);
    }
    while(1){
        sleep(1);
    }
    return 0;
}

描述

进程状态

描述

  • R(就绪):表明进程要么在运行中,要么在运行队列中

  • S(挂起):进程在等待事件完成(可中断睡眠)

  • D(深度睡眠):进程通常会等待I/O结束(不可中断睡眠)

  • T(暂停):可以通过发送SIGSTOP信号停止进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。

  • t(跟踪)

  • X(死亡):只是一个返回状态。

  • z(僵尸状态):它的父进程尚未调用wait函数

描述

停止9037号进程:

描述

恢复:

描述

僵尸进程

  • 当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。

  • 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
    int ret = fork();
    if(ret < 0){
        perror("fork");
        return 1;
    }
    else if(ret > 0){
        printf("parent[%d] is sleeping...\n", getpid());
        sleep(30);
    }
    else{
        printf("child[%d] is begin z...\n",getpid());
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

描述

描述

僵尸进程会占用系统资源,如果很多,则会严重影响服务器的性能
若一直不回收,有可能造成内存泄漏

孤儿进程

  • 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int mian(){
    int ret = fork();
    if(ret < 0){
        perror("fork");
        return 1;
    }
    else if(ret == 0){
        printf("I am child: pid: %d, ppid: %d, ret: %d\n", getpid(), getppid(), ret);
        sleep(10);
    }
    else{
        printf("I am father: pid: %d, ppid: %d, ret: %d\n", getpid(), getppid(), ret);
        sleep(3);
        exit(0);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值