Linux中与“进程相关”的数据结构分析

【摘要】本文以逐行分析源代码的方式,详细解读了在linux中与进程相关的几个重要数据结构,便于大家研读内核代码。

1.1 目录

  • 进程相关数据结构
    1. struct task_struct
    2. struct cred
    3. struct pid_link
    4. struct pid
    5. struct signal_struct
    6. struct rlimit

1.2 task_struct结构体定义剖析

struct task_struct 
{
    volatile long state;
    void *stack;	//进程内核栈,进程通过alloc_thread_info函数分配它的内核栈,
    				//通过free_thread_info函数释放所分配的内核栈
    
    atomic_t usage;	//进程描述符使用计数,被置为2时,表示进程描述符正在被使用而且其相应的进程处于活动状态
    unsigned int flags;     
    unsigned int ptrace;	//成员ptrace被设置为0时表示不需要被跟踪
    unsigned long ptrace_message;
    siginfo_t *last_siginfo; 
    int lock_depth;	//用于表示获取大内核锁的次数,如果进程未获得过锁,则置为-1
 
#ifdef CONFIG_SMP
#ifdef __ARCH_WANT_UNLOCKED_CTXSW
    int oncpu;	//在SMP上帮助实现无加锁的进程切换
#endif
#endif

    /*
    进程调度相关:
       1) prio: 调度器考虑的优先级保存在prio,由于在某些情况下内核需要暂时提高进程的优先级,
         因此需要第三个成员来表示(除了static_prio、normal_prio之外),由于这些改变不是持久的,
         因此静态(static_prio)和普通(normal_prio)优先级不受影响。
       2) static_prio: 用于保存进程的"静态优先级",静态优先级是进程"启动"时分配的优先级,
         它可以用nice、sched_setscheduler系统调用修改,否则在进程运行期间会一直保持恒定。
       3) normal_prio: 表示基于进程的"静态优先级"和"调度策略"计算出的优先级,因此,即使
         普通进程和实时进程具有相同的静态优先级(static_prio),其普通优先级(normal_prio)
         也是不同的。进程分支时(fork),新创建的子进程会继承普通优先级。   
    */
    int prio, static_prio, normal_prio;
    
    /*
     rt_priority: 表示实时进程的优先级,需要明白的是,"实时进程优先级"和"普通进程优先级"有
       两个独立的范畴,实时进程即使是最低优先级也高于普通进程,最低的实时优先级为0,
       最高的优先级为99,值越大,表明优先级越高。
    */
    unsigned int rt_priority;
    /*
        5) sched_class: 该进程所属的调度类,目前内核中有实现以下四种: 
            5.1) static const struct sched_class fair_sched_class;
            5.2) static const struct sched_class rt_sched_class;
            5.3) static const struct sched_class idle_sched_class;
            5.4) static const struct sched_class stop_sched_class;        
    */
    const struct sched_class *sched_class;
    /*
    se: 用于普通进程的调用实体,调度器不限于调度进程,还可以处理更大的实体,这可以实现"组调度",
        可用的CPU时间可以首先在一般的进程组(例如所有进程可以按所有者分组)之间分配,接下来分配的
        时间在组内再次分配。这种一般性要求调度器不直接操作进程,而是处理"可调度实体",一个实体有
        sched_entity的一个实例标识,在最简单的情况下,调度在各个进程上执行,由于调度器设计为处
        理可调度的实体,在调度器看来各个进程也必须也像这样的实体,因此se在task_struct中内嵌了
        一个sched_entity实例,调度器可据此操作各个task_struct。
    */
    struct sched_entity se;
    struct sched_rt_entity rt;	//用于实时进程的调用实体

#ifdef CONFIG_PREEMPT_NOTIFIERS 
    struct hlist_head preempt_notifiers;	//preempt_notifiers结构体链表
#endif
 
    unsigned char fpu_counter;	//FPU使用计数 

#ifdef CONFIG_BLK_DEV_IO_TRACE
    unsigned int btrace_seq;	//一个针对Linux内核中块设备I/O层的跟踪工具
#endif

    /*
    policy表示进程的调度策略,目前主要有以下五种:
        1) #define SCHED_NORMAL	0: 用于普通进程,它们通过完全公平调度器来处理
        2) #define SCHED_FIFO   1: 先来先服务调度,由实时调度类处理
        3) #define SCHED_RR     2: 时间片轮转调度,由实时调度类处理
        4) #define SCHED_BATCH  3: 用于非交互、CPU使用密集的批处理进程,通过完全公平调度器来处理,
        						   调度决策对此类进程给与"冷处理",它们绝不会抢占CFS调度器处理的
        						   另一个进程,因此不会干扰交互式进程,如果不打算用nice降低进程的
        						   静态优先级,同时又不希望该进程影响系统的交互性,最适合用该调度策略
        5) #define SCHED_IDLE   5: 可用于次要的进程,其相对权重总是最小的,也通过完全公平调度器来处
        						   理。要注意的是,SCHED_IDLE不负责调度空闲进程,空闲进程由内核提供
        						   单独的机制来处理.
    只有root用户能通过sched_setscheduler()系统调用来改变调度策略。
    */
    unsigned int policy;

    cpumask_t cpus_allowed;	//是一个位域,在多处理器系统上使用,用于控制进程可以在哪些处理器上运行

    /*
    RCU同步原语 
    */
#ifdef CONFIG_TREE_PREEMPT_RCU
    int rcu_read_lock_nesting;
    char rcu_read_unlock_special;
    struct rcu_node *rcu_blocked_node;
    struct list_head rcu_node_entry;
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */

#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
    struct sched_info sched_info;	//用于调度器统计进程的运行信息
#endif
    
    struct list_head tasks;	//通过它将当前进程的task_struct串联进内核的进程列表中,构建linux进程链表
    struct plist_node pushable_tasks;	//limit pushing to one attempt 

    /*
 	进程地址空间 
        1) mm: 指向进程所拥有的内存描述符 
        2) active_mm:指向进程运行时所使用的内存描述符
    	对于普通进程而言,这两个指针变量的值相同。但是,内核线程不拥有任何内存描述符,
    	所以它们的mm成员总是为NULL。当内核线程得以运行时,它的active_mm成员被初始化
    	为前一个运行进程的active_mm值。
    */
    struct mm_struct *mm, *active_mm;

    int exit_state;		//进程退出状态码

    /*
    判断标志:
       1)exit_code:用于设置进程的终止代号,这个值要么是_exit()或exit_group()系统调用参数(正常终止),
       			   要么是由内核提供的一个错误代号(异常终止)
       2) exit_signal:被置为-1时表示是某个线程组中的一员。只有当线程组的最后一个成员终止时,
       				  才会产生一个信号,以通知线程组的领头进程的父进程。
    */
    int exit_code, exit_signal; 
    
    int pdeath_signal;	//用于判断父进程终止时发送信号   
    
    /*
        4)  personality用于处理不同的ABI,它的可能取值如下: 
            enum 
            {
                PER_LINUX =        0x0000,
                PER_LINUX_32BIT =  0x0000 | ADDR_LIMIT_32BIT,
                PER_LINUX_FDPIC =  0x0000 | FDPIC_FUNCPTRS,
                PER_SVR4 =         0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
                PER_SVR3 =         0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
                PER_SCOSVR3 =     0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS | SHORT_INODE,
                PER_OSR5 =        0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
                PER_WYSEV386 =    0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
                PER_ISCR4 =       0x0005 | STICKY_TIMEOUTS,
                PER_BSD =         0x0006,
                PER_SUNOS =       0x0006 | STICKY_TIMEOUTS,
                PER_XENIX =       0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
                PER_LINUX32 =     0x0008,
                PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB,
                PER_IRIX32 =      0x0009 | STICKY_TIMEOUTS, 
                PER_IRIXN32 =     0x000a | STICKY_TIMEOUTS, 
                PER_IRIX64 =      0x000b | STICKY_TIMEOUTS, 
                PER_RISCOS =      0x000c,
                PER_SOLARIS =     0x000d | STICKY_TIMEOUTS,
                PER_UW7 =         0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
                PER_OSF4 =        0x000f,              
                PER_HPUX =        0x0010,
                PER_MASK =        0x00ff,
            };
    */
    unsigned int personality;

    unsigned did_exec:1;	//用于记录进程代码是否被execve()函数所执行
    unsigned in_execve:1;   //用于通知LSM是否被do_execve()函数所调用 
    unsigned in_iowait:1;	//用于判断是否进行iowait计数
    unsigned sched_reset_on_fork:1;	//用于判断是否恢复默认的优先级或调度策略

    /*
    进程标识符(PID):
    	在CONFIG_BASE_SMALL配置为0的情况下,PID的取值范围是0到32767,即系统中的进程数最大为32768个。
    	#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)。
    	在Linux系统中,一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的PID,
    	并被存放在tgid成员中。只有线程组的领头线程的pid成员才会被设置为与tgid相同的值。
    	注意,getpid()系统调用返回的是当前进程的tgid值而不是pid值。
    */
    pid_t pid;
    pid_t tgid;

#ifdef CONFIG_CC_STACKPROTECTOR 
    unsigned long stack_canary;//防止内核堆栈溢出,在GCC编译内核时,需要加上-fstack-protector选项
#endif
 
    struct task_struct *real_parent;  //指向其父进程,如果创建它的父进程不再存在,则指向PID为1的init进程
    struct task_struct *parent;  //指向其父进程,当它终止时,必须向它的父进程发送信号。它的值通常与real_parent相同  
    struct list_head children;     //子进程链表
    struct list_head sibling;     //兄弟链表
    struct task_struct *group_leader;    //指向其所在进程组的领头进程 
     
    struct list_head ptraced;
    struct list_head ptrace_entry; 
    struct bts_context *bts;
    struct pid_link pids[PIDTYPE_MAX];	//PID散列表和链表
    struct list_head thread_group;	//线程组中所有进程的链表

    /*
    do_fork函数 
        1) set_child_tid、clear_child_tid
        如果copy_process函数的clone_flags参数的值被置为CLONE_CHILD_SETTID或CLONE_CHILD_CLEARTID,则会把child_tidptr参数的值分别复制到set_child_tid和clear_child_tid成员。这些标志说明必须改变子进程用户态地址空间的child_tidptr所指向的变量的值。
    */
    struct completion *vfork_done;  //在执行do_fork()时,如果给定特别标志,则vfork_done会指向一个特殊地址   
    int __user *set_child_tid;         
    int __user *clear_child_tid;         

    /*
    记录进程的I/O计数(时间)
        1) utime用于记录进程在"用户态"下所经过的节拍数(定时器)
        2) stime用于记录进程在"内核态"下所经过的节拍数(定时器)
        3) utimescaled用于记录进程在"用户态"的运行时间,但它们以处理器的频率为刻度
        4) stimescaled用于记录进程在"内核态"的运行时间,但它们以处理器的频率为刻度
    */
    cputime_t utime, stime, utimescaled, stimescaled;   
    cputime_t gtime;	//以节拍计数的虚拟机运行时间(guest time)
    cputime_t prev_utime, prev_stime; //先前的运行时间
    
    /*
     nvcsw:自愿(voluntary)上下文切换计数
     nivcsw:非自愿(involuntary)上下文切换计数
    */
    unsigned long nvcsw, nivcsw; 

    struct timespec start_time;    //进程创建时间      
    struct timespec real_start_time;	//进程睡眠时间,常用于/proc/pid/stat
    struct task_cputime cputime_expires;//用来统计进程或进程组被跟踪的处理器时间,
    									//其中的三个成员对应着cpu_timers[3]的三个链表
    struct list_head cpu_timers[3];

#ifdef CONFIG_DETECT_HUNG_TASK 
    unsigned long last_switch_count;	//nvcsw和nivcsw的总和
#endif
    
    struct task_io_accounting ioac;

#if defined(CONFIG_TASK_XACCT)
    u64 acct_rss_mem1;     
    u64 acct_vm_mem1;     
    cputime_t acct_timexpd;     
#endif

    unsigned long min_flt, maj_flt; //缺页统计 

    /*
      进程权能 
    */
    const struct cred *real_cred;     
    const struct cred *cred;     
    struct mutex cred_guard_mutex;     
    struct cred *replacement_session_keyring;  

    char comm[TASK_COMM_LEN]; //程序名
    int link_count, total_link_count; 
    struct fs_struct *fs; //用来表示进程与文件系统的联系,包括当前目录和根目录
    struct files_struct *files;	//表示进程当前打开的文件

#ifdef CONFIG_SYSVIPC 
    struct sysv_sem sysvsem;	//进程通信
#endif

    struct thread_struct thread;  //处理器特有数据
    struct nsproxy *nsproxy; //命名空间 
    struct signal_struct *signal;	//指向进程的信号描述符
    struct sighand_struct *sighand; //指向进程的信号处理程序描述符

    sigset_t blocked;		//表示被阻塞信号的掩码
    sigset_t real_blocked;	//表示临时掩码
    sigset_t saved_sigmask;     
    struct sigpending pending;//存放私有挂起信号的数据结构
    unsigned long sas_ss_sp;	//信号处理程序备用堆栈的地址
    size_t sas_ss_size;	//表示堆栈的大小

    int (*notifier)(void *priv);	//设备驱动程序常用notifier指向的函数来阻塞进程的某些信号
    void *notifier_data;	//指的是notifier所指向的函数可能使用的数据。
    sigset_t *notifier_mask;	//标识这些信号的位掩码

    struct audit_context *audit_context; //进程审计 
#ifdef CONFIG_AUDITSYSCALL
    uid_t loginuid;
    unsigned int sessionid;
#endif

    seccomp_t seccomp;	//secure computing 
     
     /*
      用于copy_process函数使用CLONE_PARENT标记时 
     */
       u32 parent_exec_id;
       u32 self_exec_id;

    spinlock_t alloc_lock;//用于保护资源分配或释放的自旋锁 

#ifdef CONFIG_GENERIC_HARDIRQS 
    struct irqaction *irqaction;	//中断 
#endif

#ifdef CONFIG_TRACE_IRQFLAGS
    unsigned int irq_events;
    int hardirqs_enabled;
    unsigned long hardirq_enable_ip;
    unsigned int hardirq_enable_event;
    unsigned long hardirq_disable_ip;
    unsigned int hardirq_disable_event;
    int softirqs_enabled;
    unsigned long softirq_disable_ip;
    unsigned int softirq_disable_event;
    unsigned long softirq_enable_ip;
    unsigned int softirq_enable_event;
    int hardirq_context;
    int softirq_context;
#endif
     
    spinlock_t pi_lock;	//task_rq_lock函数所使用的锁 

    //基于PI协议的等待互斥锁
#ifdef CONFIG_RT_MUTEXES 
    struct plist_head pi_waiters; 
    struct rt_mutex_waiter *pi_blocked_on;
#endif

#ifdef CONFIG_DEBUG_MUTEXES 
    struct mutex_waiter *blocked_on;	//死锁检测
#endif

#ifdef CONFIG_LOCKDEP
#define MAX_LOCK_DEPTH 48UL
    u64 curr_chain_key;
    int lockdep_depth;
    unsigned int lockdep_recursion;
    struct held_lock held_locks[MAX_LOCK_DEPTH];
    gfp_t lockdep_reclaim_gfp;
#endif
    
    void *journal_info;	//JFS文件系统
    struct bio *bio_list, **bio_tail; 	//块设备链表
    struct reclaim_state *reclaim_state;	//内存回收
    struct backing_dev_info *backing_dev_info;	//存放块设备I/O数据流量信息
    struct io_context *io_context;	//I/O调度器所使用的信息 

    //CPUSET功能 
#ifdef CONFIG_CPUSETS
    nodemask_t mems_allowed;     
    int cpuset_mem_spread_rotor;
#endif

    //Control Groups 
#ifdef CONFIG_CGROUPS 
    struct css_set *cgroups; 
    struct list_head cg_list;
#endif

    // futex同步机制 
#ifdef CONFIG_FUTEX
    struct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPAT
    struct compat_robust_list_head __user *compat_robust_list;
#endif
    struct list_head pi_state_list;
    struct futex_pi_state *pi_state_cache;
#endif 
#ifdef CONFIG_PERF_EVENTS
    struct perf_event_context *perf_event_ctxp;
    struct mutex perf_event_mutex;
    struct list_head perf_event_list;
#endif

    //非一致内存访问(NUMA  Non-Uniform Memory Access)
#ifdef CONFIG_NUMA
    struct mempolicy *mempolicy;    /* Protected by alloc_lock */
    short il_next;
#endif

    atomic_t fs_excl;	//文件系统互斥资源
    struct rcu_head rcu;	//RCU链表 
    struct pipe_inode_info *splice_pipe;	//管道

#ifdef    CONFIG_TASK_DELAY_ACCT
    struct task_delay_info *delays;	//延迟计数
#endif

#ifdef CONFIG_FAULT_INJECTION
    int make_it_fail;	//fault injection
#endif

    struct prop_local_single dirties;	//FLoating proportions 

    // Infrastructure for displayinglatency 
#ifdef CONFIG_LATENCYTOP
    int latency_record_count;
    struct latency_record latency_record[LT_SAVECOUNT];
#endif
     
    // time slack values,常用于poll和select函数 
    unsigned long timer_slack_ns;
    unsigned long default_timer_slack_ns;

    struct list_head    *scm_work_list;	//socket控制消息(control message)

    // ftrace跟踪器
#ifdef CONFIG_FUNCTION_GRAPH_TRACER 
    int curr_ret_stack; 
    struct ftrace_ret_stack    *ret_stack; 
    unsigned long long ftrace_timestamp;  
    atomic_t trace_overrun; 
    atomic_t tracing_graph_pause;
#endif
#ifdef CONFIG_TRACING 
    unsigned long trace; 
    unsigned long trace_recursion;
#endif  
};
task_struct结构体成员解析:
  • state:进程执行时,它会根据具体情况改变运行状态。进程状态是进程调度和对换的依据。Linux中的进程主要有如下状态:
    1. TASK_RUNNING(可运行态),处于这种状态的进程,只有两种状态:
      • 正在运行:正在运行的进程就是当前进程,由current所指向的进程。
      • 准备运行:准备运行的进程只要得到CPU就可以立即投入运行,CPU是这些进程唯一等待的系统资源,系统中有一个运行队列(run_queue),用来容纳所有处于可运行状态的进程,调度程序执行时,从中选择一个进程投入运行。
    2. TASK_INTERRUPTIBLE(可中断的等待状态),针对等待某事件或其他资源的睡眠进程设置的,在内核发送信号给该进程表明事件已经发生时,进程状态变为TASK_RUNNING,只要调度器选中该进程即可恢复执行。
    3. TASK_UNINTERRUPTIBLE(不可中断的等待状态),处于该状态的进程正在等待某个事件(event)或某个资源,它位于系统中的某个等待队列(wait_queue)中,处于不可中断等待态的进程是因为硬件环境不能满足而等待,例如等待特定的系统资源,它任何情况下都不能被打断,只能用特定的方式来唤醒它,例如唤醒函数wake_up()等,它们不能由外部信号唤醒,只能由内核亲自唤醒。
    4. TASK_ZOMBIE(僵死),进程虽然已经终止,但由于某种原因,父进程还没有执行wait()系统调用,终止进程的信息也还没有回收。顾名思义,处于该状态的进程就是死进程,这种进程实际上是系统中的垃圾,必须进行相应处理以释放其占用的资源。
    5. TASK_STOPPED(暂停),此时的进程暂时停止运行来接受某种特殊处理。通常当进程接收到SIGSTOP、SIGTSTP、SIGTTIN或 SIGTTOU信号后就处于这种状态。例如,正接受调试的进程就处于这种状态。
    6. TASK_TRACED,本质上说,这属于TASK_STOPPED状态,用于从停止的进程中,将当前被调试的进程与常规的进程区分开来
    7. TASK_DEAD,父进程wait系统调用发出后,当子进程退出时,父进程负责回收子进程的全部资源,子进程进入TASK_DEAD状态。
    8. TASK_SWAPPING: 换入/换出
  • flags:进程当前的状态标志,注意和上文的运行状态区分
    • #define PF_ALIGNWARN 0x00000001: 显示内存地址未对齐警告
    • #define PF_PTRACED 0x00000010: 标识是否是否调用了ptrace
    • #define PF_TRACESYS 0x00000020: 跟踪系统调用
    • #define PF_FORKNOEXEC 0x00000040: 已经完成fork,但还没有调用exec
    • #define PF_SUPERPRIV 0x00000100: 使用超级用户(root)权限
    • #define PF_DUMPCORE 0x00000200: dumped core
    • #define PF_SIGNALED 0x00000400: 此进程由于其他进程发送相关信号而被杀死
    • #define PF_STARTING 0x00000002: 当前进程正在被创建
    • #define PF_EXITING 0x00000004: 当前进程正在关闭
    • #define PF_USEDFPU 0x00100000: Process used the FPU this quantum(SMP only)
    • #define PF_DTRACE 0x00200000: delayed trace (used on m68k)
  • ptrace:成员ptrace被设置为0时表示不需要被跟踪,它的可能取值如下:
    • 1)#define PT_PTRACED 0x00000001
      1. #define PT_DTRACE 0x00000002: delayed trace (used on m68k, i386)
      1. #define PT_TRACESYSGOOD 0x00000004
      1. #define PT_PTRACE_CAP 0x00000008: ptracer can follow suid-exec
      1. #define PT_TRACE_FORK 0x00000010
      1. #define PT_TRACE_VFORK 0x00000020
      1. #define PT_TRACE_CLONE 0x00000040
      1. #define PT_TRACE_EXEC 0x00000080
      1. #define PT_TRACE_VFORK_DONE 0x00000100
      1. #define PT_TRACE_EXIT 0x00000200
  • 拓展链接:
    • http://oss.org.cn/kernel-book/ch04/4.3.htm
    • http://www.eecs.harvard.edu/~margo/cs161/videos/sched.h.html
    • http://memorymyann.iteye.com/blog/235363
    • http://blog.csdn.net/hongchangfirst/article/details/7075026
    • http://oss.org.cn/kernel-book/ch04/4.4.2.htm
    • http://blog.csdn.net/npy_lp/article/details/7335187
    • http://blog.csdn.net/npy_lp/article/details/7292563

1.3 cred结构体剖析

  • 定义位于\linux-2.6.32.63\include\linux\cred.h
//保存了当前进程的相关权限信息
struct cred 
{
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC    0x43736564
#define CRED_MAGIC_DEAD    0x44656144
#endif
    uid_t        uid;        	/* real UID of the task */
    gid_t        gid;        	/* real GID of the task */
    uid_t        suid;        	/* saved UID of the task */
    gid_t        sgid;        	/* saved GID of the task */
    uid_t        euid;        	/* effective UID of the task */
    gid_t        egid;        	/* effective GID of the task */
    uid_t        fsuid;        	/* UID for VFS ops */
    gid_t        fsgid;        	/* GID for VFS ops */
    unsigned    securebits;    	/* SUID-less security management */
    kernel_cap_t    cap_inheritable; 	/* caps our children can inherit */
    kernel_cap_t    cap_permitted;    	/* caps we're permitted */
    kernel_cap_t    cap_effective;    	/* caps we can actually use */
    kernel_cap_t    cap_bset;    		/* capability bounding set */
#ifdef CONFIG_KEYS
    unsigned char    jit_keyring;    /* default keyring to attach requested keys to */
    struct key    *thread_keyring; 		/* keyring private to this thread */
    struct key    *request_key_auth; 	/* assumed request_key authority */
    struct thread_group_cred *tgcred; 	/* thread-group shared credentials */
#endif
#ifdef CONFIG_SECURITY
    void        *security;    			/* subjective LSM security */
#endif
    struct user_struct *user;    		/* real user ID subscription */
    struct group_info *group_info;    	/* supplementary groups for euid/fsgid */
    struct rcu_head    rcu;        		/* RCU deletion hook */
};

1.4 pid_link结构体剖析

/* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX];

位/include/linux/pid.h有如下定义:

enum pid_type
{
    PIDTYPE_PID,
    PIDTYPE_PGID,
    PIDTYPE_SID,
    PIDTYPE_MAX
};

struct pid_link
{
    struct hlist_node node;
    struct pid *pid;
};

位/include/linux/types.h有如下定义:

struct hlist_node 
{
    struct hlist_node *next, **pprev;
};

struct pid
{
    //1. 指向该数据结构的引用次数
    atomic_t count;

    /*
    2. 该pid在pid_namespace中处于第几层
        1) 当level=0时,表示是global namespace,即最高层 
    */
    unsigned int level;
    
    /* lists of tasks that use this pid */
    //3. tasks[i]指向的是一个哈希表。譬如说tasks[PIDTYPE_PID]指向的是PID的哈希表
    struct hlist_head tasks[PIDTYPE_MAX];

    //4. 
    struct rcu_head rcu;

    /*
    5. numbers[1]域指向的是upid结构体
    numbers数组的本意是想表示不同的pid_namespace,一个PID可以属于不同的namespace
        1) numbers[0]表示global namespace
        2) numbers[i]表示第i层namespace
        3) i越大所在层级越低
    目前该数组只有一个元素, 即global namespace。所以namepace的概念虽然引入了pid,
    但是并未真正使用,在未来的版本可能会用到
    */
    struct upid numbers[1];
};
  • 拓展链接
    • http://blog.csdn.net/zhanglei4214/article/details/6765913

1.5 signal_struct结构体剖析

/*
NOTE! "signal_struct" does not have it's own locking, because a shared signal_struct always implies a shared sighand_struct, so locking sighand_struct is always a proper superset of the locking of signal_struct.
*/
struct signal_struct 
{
    atomic_t        count;
    atomic_t        live;

    /* for wait4() */
    wait_queue_head_t    wait_chldexit;    

    /* current thread group signal load-balancing target: */
    struct task_struct    *curr_target;

    /* shared signal handling: */
    struct sigpending    shared_pending;

    /* thread group exit support */
    int   group_exit_code;

    /* 
    overloaded:
    notify group_exit_task when ->count is equal to notify_count,
    everyone except group_exit_task is stopped during signal delivery 
    of fatal signals, group_exit_task processes the signal.
    */
    int            notify_count;
    struct task_struct    *group_exit_task;

    /* thread group stop support, overloads group_exit_code too */
    int            group_stop_count;
    unsigned int   flags; /* see SIGNAL_ flags below */

    /* POSIX.1b Interval Timers */
    struct list_head posix_timers;

    /* ITIMER_REAL timer for the process */
    struct hrtimer 	real_timer;
    struct pid 		*leader_pid;
    ktime_t 		it_real_incr;

    /*
    ITIMER_PROF and ITIMER_VIRTUAL timers for the process, we use CPUCLOCK_PROF and CPUCLOCK_VIRT for indexing array as these values are defined to 0 and 1 respectively
    */
    struct cpu_itimer it[2];

    /*
    Thread group totals for process CPU timers. See thread_group_cputimer(), et al, for details.
    */
    struct thread_group_cputimer cputimer;

    /* Earliest-expiration cache. */
    struct task_cputime cputime_expires;

    struct list_head cpu_timers[3];

    struct pid *tty_old_pgrp;

    /* boolean value for session group leader */
    int leader;

    struct tty_struct *tty; /* NULL if no tty */

    /*
    Cumulative resource counters for dead threads in the group, and for reaped dead child processes forked by this group.
    Live threads maintain their own counters and add to these in __exit_signal, except for the group leader.
    */
    cputime_t utime, stime, cutime, cstime;
    cputime_t gtime;
    cputime_t cgtime;
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
    cputime_t prev_utime, prev_stime;
#endif
    unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw;
    unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
    unsigned long inblock, oublock, cinblock, coublock;
    unsigned long maxrss, cmaxrss;
    struct task_io_accounting ioac;

    /*
    Cumulative ns of schedule CPU time fo dead threads in the group, not including a zombie group leader, (This only differs from jiffies_to_ns(utime + stime) if sched_clock uses something other than jiffies.)
    */
    unsigned long long sum_sched_runtime;

    /*
    We don't bother to synchronize most readers of this at all, because there is no reader checking a limit that actually needs to get both rlim_cur and rlim_max atomically, 
    and either one alone is a single word that can safely be read normally.
    getrlimit/setrlimit use task_lock(current->group_leader) to protect this instead of the siglock, because they really have no need to disable irqs.
    struct rlimit 
    {
        rlim_t rlim_cur;    //Soft limit(软限制): 进程当前的资源限制
        rlim_t rlim_max;    //Hard limit(硬限制): 该限制的最大容许值(ceiling for rlim_cur)    
    };
    rlim是一个数组,其中每一项保存了一种类型的资源限制,RLIM_NLIMITS表示资源限制类型的类型数量
    要说明的是,hard limit只针对非特权进程,也就是进程的有效用户ID(effective user ID)不是0的进程
    */
    struct rlimit rlim[RLIM_NLIMITS];

#ifdef CONFIG_BSD_PROCESS_ACCT
    struct pacct_struct pacct;    /* per-process accounting information */
#endif
#ifdef CONFIG_TASKSTATS
    struct taskstats *stats;
#endif
#ifdef CONFIG_AUDIT
    unsigned audit_tty;
    struct tty_audit_buf *tty_audit_buf;
#endif

    int oom_adj;    /* OOM kill score adjustment (bit shift) */
};

拓展链接

  • http://blog.csdn.net/walkingman321/article/details/6167435

1.6 rlimit结构体剖析

位\linux-2.6.32.63\include\linux\resource.h有如下定义:

struct rlimit 
{
    //Soft limit(软限制): 进程当前的资源限制
    unsigned long    rlim_cur;

    //Hard limit(硬限制): 该限制的最大容许值(ceiling for rlim_cur)    
    unsigned long    rlim_max;
};

Linux提供资源限制(resources limit )机制,对进程使用系统资源施加限制,该机制利用了task_struct中的rlim数组中的位置标识了受限制资源的类型,这也是内核需要定义预处理器常数,将资源与位置关联起来的原因,以下是所有的常数及其含义:

  1. RLIMIT_CPU: CPU time in ms
    CPU时间的最大量值(秒),当超过此软限制时向该进程发送SIGXCPU信号

  2. RLIMIT_FSIZE: Maximum file size
    可以创建的文件的最大字节长度,当超过此软限制时向进程发送SIGXFSZ

  3. RLIMIT_DATA: Maximum size of the data segment
    数据段的最大字节长度

  4. RLIMIT_STACK: Maximum stack size
    栈的最大长度

  5. RLIMIT_CORE: Maximum core file size
    设定最大的core文件,当值为0时将禁止core文件,非0时将设定产生的最大core文件大小为设定的值

  6. RLIMIT_RSS: Maximum resident set size
    最大驻内存集字节长度(RSS),如果物理存储器供不应求,则内核将从进程处取回超过RSS的部份

  7. RLIMIT_NPROC: Maximum number of processes
    每个实际用户ID所拥有的最大子进程数,更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX中返回的值

  8. RLIMIT_NOFILE: maximum number of open files
    每个进程能够打开的最多文件数。更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX中的返回值

  9. RLIMIT_MEMLOCK: Maximum locked-in-memory address space
    The maximum number of bytes of virtual memory that may be locked into RAM using mlock() and mlockall().
    不可换出页的最大数目

  10. RLIMIT_AS: Maximum address space size in bytes
    The maximum size of the process virtual memory (address space) in bytes. This limit affects calls to brk(2), mmap(2) and mremap(2), which fail with the error ENOMEM upon exceeding this limit. Also automatic stack expansion will fail (and generate a SIGSEGV that kills the process when no alternate stack has been made available). Since the value is a long, on machines with a 32-bit long either this limit is at most 2 GiB, or this resource is unlimited.
    进程占用的虚拟地址空间的最大尺寸

  11. RLIMIT_LOCKS: Maximum file locks held
    文件锁的最大数目

  12. RLIMIT_SIGPENDING: Maximum number of pending signals
    待决信号的最大数目

  13. RLIMIT_MSGQUEUE: Maximum bytes in POSIX mqueues
    消息队列的最大数目

  14. RLIMIT_NICE: Maximum nice prio allowed to raise to
    非实时进程的优先级(nice level)

  15. RLIMIT_RTPRIO: Maximum realtime priority
    最大的实时优先级

  • 因为涉及内核的各个不同部分,内核必须确认子系统遵守了相应限制。需要注意的是,如果某一类资源没有使用限制(这是几乎所有资源的默认设置),则将rlim_max设置为RLIM_INFINITY,例外情况包括下列:

    • 打开文件的数目(RLIMIT_NOFILE): 默认限制在1024。
    1. 每用户的最大进程数(RLIMIT_NPROC): 定义为max_threads / 2,max_threads是一个全局变量,指定了在把 1/8 可用内存用于管理线程信息的情况下,可以创建的进程数目。在计算时,提前给定了20个线程的最小可能内存用量。
  • init进程在Linux中是一个特殊的进程,init的进程限制在系统启动时就生效了
    \linux-2.6.32.63\include\asm-generic\resource.h

/*
 * boot-time rlimit defaults for the init task:
 */
#define INIT_RLIMITS                            \
{                                    \
    [RLIMIT_CPU]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
    [RLIMIT_FSIZE]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
    [RLIMIT_DATA]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
    [RLIMIT_STACK]        = {       _STK_LIM,   _STK_LIM_MAX },    \
    [RLIMIT_CORE]        = {              0,  RLIM_INFINITY },    \
    [RLIMIT_RSS]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
    [RLIMIT_NPROC]        = {              0,              0 },    \
    [RLIMIT_NOFILE]        = {       INR_OPEN,       INR_OPEN },    \
    [RLIMIT_MEMLOCK]    = {    MLOCK_LIMIT,    MLOCK_LIMIT },    \
    [RLIMIT_AS]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
    [RLIMIT_LOCKS]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
    [RLIMIT_SIGPENDING]    = {         0,           0 },    \
    [RLIMIT_MSGQUEUE]    = {   MQ_BYTES_MAX,   MQ_BYTES_MAX },    \
    [RLIMIT_NICE]        = { 0, 0 },                \
    [RLIMIT_RTPRIO]        = { 0, 0 },                \
    [RLIMIT_RTTIME]        = {  RLIM_INFINITY,  RLIM_INFINITY },    \
}
  • 在proc文件系统中,对每个进程都包含了对应的一个文件,可以通过执行cat /proc/self/limits查看当前的rlimit值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leon_George

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值