zoukankan      html  css  js  c++  java
  • linux进程管理之概念(一)

    一、进程和线程的概念

    1.进程和线程的定义

      进程并不只是一段可以运行的代码,也包含了运行代码所需要的资源。

      在操作系统来看,进程是资源管理的最小单元,而我们又知道,线程是程序执行的最小单元。

      话说回来,Linux系统至少有一个进程,一个程序可以对应多个进程,一个进程只能对应一个程序,一个进程包含一个或多个线程。

      所以,一个进程的组成实体实际是两大部分:资源的集合和线程的集合。进程中的线程是动态的对象, 代表了进程指令的执行。资源,包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。线程有自己的私有数据:程序计数器,栈空间以及寄存器。

      总结来说,在linux系统下,进程主要具有以下四个要素:

      1)有一个程序供其运行。这段程序不一定是进程所专有,可以与其他进程一起使用;

      2)有起码的“私有财产”,这就是进程专用的系统堆栈空间;

      3)有“身份证”,也就是task_struct结构,也称之为“进程控制块”(PCB)。有了这个数据结构,进程才能成为内核调度的一个基本单位接受内核的调度。同时,这个结构又是进程的“财产登记卡”,记录着进程占用的各项资源。

      4)有独立的存储空间,意味着拥有专有的用户空间;还意味着除前述的系统空间堆栈外还有其专有的用户空间堆栈。(PS:进程的系统空间是不能独立的,除了各进程独有的系统堆栈空间外,任何进程都不可能直接改变用户空间的内容)。

      以上条件是必要条件,缺少其中一条,都不能称其为“进程”。如果只缺第四条,那就称为“线程”。

      在linux系统中,“进程”和“任务”是同一个意思,在内核的代码中常混用这两个名词和概念。例如每个进程都要有一个task_struct数据结构,而其号码却又是pid、唤醒一个睡眠进程的函数名为wake_up_process()。

      之所以有这样的情况是因为,linux源自Unix和i386系统结构,而unix中的进程在Intel的技术资料中称为“任务”,严格来说有点区别,但是对于系统的实现来说是一回事。

    2.task_struct的定义

      操作系统通过一个称作PCB(Process Control Block,进程控制块)的数据结构管理一个进程,也称为tesk_struct结构体,这个结构体包含了一个进程所需的所有信息。它定义在linux-2.6.38.8/include/linux/sched.h文件中。

      除了最起码的“财产”,即task_struct数据结构和系统堆栈之外,一个进程还要有一些附加的资源。例如,进程拥有堵路的存储空间,就要有用于虚拟内存管理的mm_struct数据结构以及附属的vm_area数据结构,以及相应的页面目录项和页面表,

      但这些都从属于task_struct资源。task_struct数据结构在这方面相当于登记卡的作用,其具体结构源代码如下:

    struct task_struct
    {
        /*
         * offsets of these are hardcoded elsewhere - touch with care
         */
        volatile long state;  /* -1 unrunnable, 0 runnable, >0 stopped */
        unsigned long flags;  /* per process flags, defined below */
        int sigpending;
        mm_segment_t addr_limit;   /* thread address space:
        0-0xBFFFFFFF for user-thead
        0-0xFFFFFFFF for kernel-thread
         */
        struct exec_domain *exec_domain;
        volatile long need_resched;
        unsigned long ptrace;
        int lock_depth; /* Lock depth */
        
        /*
         * offset 32 begins here on 32-bit platforms. We keep
         * all fields in a single cacheline that are needed for
         * the goodness() loop in schedule().
         */
        long counter;
        long nice;
        unsigned long policy;
        struct mm_struct *mm;
        int has_cpu, processor;
        unsigned long cpus_allowed;
        
        struct list_head run_list;
        unsigned long sleep_time;
        
        struct task_struct *next_task, *prev_task;
        struct mm_struct *active_mm;
        /* task state */
        struct linux_binfmt *binfmt;
        int exit_code, exit_signal;
        int pdeath_signal;  /*  The signal sent when the parent dies  */
        
        unsigned long personality;
        int dumpable:1;
        int did_exec:1;
        pid_t pid;
        pid_t pgrp;
        pid_t tty_old_pgrp;
        pid_t session;
        pid_t tgid;
        /* boolean value for session group leader */
        int leader;
        /*
         * pointers to (original) parent process, youngest child, younger sibling,
         * older sibling, respectively.  (p->father can be replaced with
         * p->p_pptr->pid)
         */
        struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
        struct list_head thread_group;
        /* PID hash table linkage. */
        struct task_struct *pidhash_next;
        struct task_struct **pidhash_pprev;
        wait_queue_head_t wait_chldexit; /* for wait4() */
        struct semaphore *vfork_sem; /* for vfork() */
        unsigned long rt_priority;
        unsigned long it_real_value, it_prof_value, it_virt_value;
        unsigned long it_real_incr, it_prof_incr, it_virt_incr;
        struct timer_list real_timer;
        struct tms times;
        unsigned long start_time;
        long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
        /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
        unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
        int swappable:1;
        /* process credentials */
        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;
        /* limits */
        struct rlimit rlim[RLIM_NLIMITS];
        unsigned short used_math;
        char comm[16];
        /* file system info */
        int link_count;
        struct tty_struct *tty; /* NULL if no tty */
        unsigned int locks; /* How many file locks are being held */
        /* ipc stuff */
        struct sem_undo *semundo;
        struct sem_queue *semsleeping;
        /* CPU-specific state of this task */
        struct thread_struct thread;
        /* filesystem information */
        struct fs_struct *fs;
        /* open file information */
        struct files_struct *files;
        /* signal handlers */
        spinlock_t sigmask_lock; /* Protects signal and blocked */
        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;
        
        /* Thread group tracking */
        u32 parent_exec_id;
        u32 self_exec_id;
        /* Protection of (de-)allocation: mm, files, fs, tty */
        spinlock_t alloc_lock;
    };

    下面对结构中几个重要的成分做介绍:

      1)state(第6行)

      该变量表示进程当前运行的状态,具体定义如下:

    1 #define TASK_RUNNING              0
    2 #define TASK_INTERRUPTIBLE        1
    3 #define TASK_UNINTERRUPTIBLE      2
    4 #define TASK_ZOMBIE               4  //僵尸进程
    5 #define TASK_STOPPED              8

      状态TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE均表示进程处于睡眠状态。但是TASK_UNINTERRUPTIBLE表示进程处于“深度睡眠”,而不受“信号”(signal,也称软中断)的打扰,而TASK_INTERRUPTIBLE则可以因信号的到来而被唤醒。内核中提供了不同的函数,让一个进程进入不同深度的睡眠或将进程从睡眠中唤醒。具体地说,函数sleep_on()和wake_up()用于深度睡眠,而interruptible_sleep_on()和wake_up_interruptible()则用于浅度睡眠。深度睡眠一般只用于临界区和关键性的部位,而“可中断”的睡眠那就是通用的了。特别地,当进程在“阻塞性”的系统调用中等待某一事件发生时,应该进入可中断睡眠,否则就不能对别的中断做出反应,别的进程就不能通过发一个信号来“杀掉”这个进程了。

      TASK_RUNNING状态并不是表示一个进程正在执行中,或者说这个进程就是“当前进程”,而是表示这个进程可以被调度执行而成为当前进程。当进程处于这样的可执行(或就绪)状态时,内核就将该进程的task_struct结构通过其队列头(见第30行)挂入一个“运行队列”。

      TASK_ZOMBIE状态表示进程已经“去世”而户口尚未注销。

      TASK_STOPPED主要用于调试的目的,进程接收到 一个SIGSTOP信号后就将运行状态改成     TASK_STOPPED而进入“挂起”状态,然后在接收到SIGCONT信号时又恢复继续运行。

      2)flags(第7行)

      flags反应进程状态相关信息,但并不是运行状态,而是与管理有关的其他信息。

       

    复制代码
     1 #define PF_ALIGNWARN        0x00000001      /*print alignment warning msgs*/
     2 #define PF_STARTING         0x00000002      /*being created*/
     3 #define PF_EXITING          0x00000004      /*getting shut down*/
     4 #define PF_FORKNOEXEC       0x00000040      /*forked but did not exec*/
     5 #define PF_SUPERPRIV        0x00000100      /*uses super-user privileges*/
     6 #define PF_DUMPCORE         0x00000200      /*dumped core*/
     7 #define PF_SIGNALED         0x00000400      /*killed by signal*/
     8 #define PF_MEMALLOC         0x00000800      /*Allocating memory*/
     9 #define PF_VFORK            0x00001000      /*wake up parent in mm_release*/
    10 #define PF_USEDFPU          0x00100000      /*task used FPU this quantum(SMP)*/
    复制代码

      3)sigpending(第8行)

      表示进程收到了“信号”但是尚未处理。

      4)counter(第23行)

      与进程调度有关

      5)add_limit

      虚拟地址空间的上限,对进程而言是其用户空间的上限,所以是0xbfff ffff;对内核线程而言则是系统空间额的上限,所以是0xffff ffff

      6)binfnt

      应用程序的文件格式。

      7)pgrp,session,leader

      当一个用户登录时,就开始了一个进程组(session),此后创建的进程都属于这同一个session。

      8)user

      指向一个user_struct结构,该数据结构代表进程所属的用户。

      9)rlim

      这是一个结构数组,表明进程岁各种资源的使用数量所受的限制。

    3.task_struct如何在linux中被管理

      task_struct可以以三种方式被管理,他们分别是:树,哈希表和链表,具体如下图,其中圆代表一个个进程的task_struct。

  • 相关阅读:
    json to dictionary
    ie下 频繁异步加载导致崩溃
    如何实现数据在表内部置顶
    js货币转换,replaceAll写法
    js奇怪的问题
    mssql中public和db_owner权限下拿到webshell或者系统权限
    关于Alexa Toolbar的破解方法
    如何备份和还原 IIS
    WIN下IIS备份与恢复操作
    汽车品牌标志数据库
  • 原文地址:https://www.cnblogs.com/zzdbullet/p/9809586.html
Copyright © 2011-2022 走看看