(一)进程管理之进程

操作系统:linux
处理器:arm
内核版本:4.x

目录:


进程

我们日常生活中,手机上会安装各种APP,比如微信、QQ等程序。当系统执行它时,需要很多资源,除了程序代码,还包括于各种依赖文件、内存、处理器等。程序代码运行后,包含在系统中使用到的一些资源,就被当成系统中的一个进程。
在终端敲入ps aux命令可以看到当前的进程。
这里写图片描述


线程

系统中进程是对资源的抽象,而实际任务调度的最小单元是线程。在linux系统上,进程和线程并没有特殊区分。线程即是一种特殊的进程:当他们使用的系统资源大部分都是共享时,就可以当成线程。
使用线程的好处有很多:比如减少系统在不同任务间切换带来的开销(在同一个程序中),提高系统的运行效率等。
可以使用C库中的clone、pthread_create等API,在用户空间创建线程。
终端中ps命令加上-T选项就可以看到系统中的线程。

这里写图片描述
可以看到,几个线程他们的pid号都相等,这是为了遵循POSIX标准,但是内核可以用SPID(tgid)把他们区分开来。

pid_t pid;
pid_t tgid;

每个task都包含这两个变量。pid既然是一个变量,那么肯定有数量限制,可以通过读取proc节点获取当前最大pid值(cat /proc/sys/kernel/pid_max)。

后续文章为了行文方便,就不细分进程线程。


task_struct

在linux系统中,使用task_struct来描述进程所拥有的资源,他有几KB的大小。

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
        /*
         * For reasons of header soup (see current_thread_info()), this
         * must be the first element of task_struct.
         */
        struct thread_info              thread_info;
#endif
        /* -1 unrunnable, 0 runnable, >0 stopped: */
        volatile long                   state;        
……
……
……
#endif
#ifdef CONFIG_LIVEPATCH
        int patch_state;
#endif
#ifdef CONFIG_SECURITY
        /* Used by LSM modules for access restriction: */
        void                            *security;
#endif

        /*
         * New fields for task_struct should be added above here, so that
         * they are included in the randomized portion of task_struct.
         */
        randomized_struct_fields_end

        /* CPU-specific state of this task: */
        struct thread_struct            thread;

        /*
         * WARNING: on x86, 'thread_struct' contains a variable-sized
         * structure.  It *MUST* be at the end of 'task_struct'.
         *
         * Do not put anything below here!
         */
};

这个结构体中存放的信息很多:进程状态、进程信息、内核栈、进程调度、进程地址空问、进程ID、组管理、用户管理、工作目录、根目录、文件描述符、信号信息、信号处理程序等。
每个进程都会有一个内核栈来存储信息,arm平台一般32位芯片内核栈大小为8KB,64位芯片内核栈大小为16KB。在2.6以前的内核,task_struct直接存储在内核栈的底部。


thread_info

在2.6版本之后,出于性能和资源的考虑,描述进程信息的task_struct使用内存接口动态分配(slab分配器)。而内核栈中保留thread_info,thread_info仍然固定存放在内核栈的底部。SP寄存器的值经过位操作,就可以得到thread_info地址,thread_info中存放了task_struct的指针,这样就可以找到对应的进程信息。
这里写图片描述
上图很好展现了arm平台sp、thread_info、task_struct之间的关系。
在arm64平台上内核开发者利用了硬件的新特性,因为用户态进入内核态时,寄存器会被保存在pt_regs数组中(存放在内核栈顶部),用户态的sp指针暂时不会使用,所以利用他来存放当前task_struct的位置。这使得不再需要用thread_info来找到当前task_struct。
这里写图片描述

static __always_inline struct task_struct *get_current(void)
{
        unsigned long sp_el0;

        asm ("mrs %0, sp_el0" : "=r" (sp_el0));

        return (struct task_struct *)sp_el0;
}

sp_el0即用户态的sp寄存器,get_current直接利用他来得到当前的task_struct地址。
因为这个改变,thread_info也被大大删减。
ARM32平台:

struct thread_info {
        unsigned long           flags;          /* low level flags */
        int                     preempt_count;  /* 0 => preemptable, <0 => bug */
        mm_segment_t            addr_limit;     /* address limit */
        struct task_struct      *task;          /* main task structure */
        __u32                   cpu;            /* cpu */
        __u32                   cpu_domain;     /* cpu domain */
        struct cpu_context_save cpu_context;    /* cpu context */
        __u32                   syscall;        /* syscall number */
        __u8                    used_cp[16];    /* thread used copro */
        unsigned long           tp_value[2];    /* TLS registers */
#ifdef CONFIG_CRUNCH
        struct crunch_state     crunchstate;
#endif
        union fp_state          fpstate __attribute__((aligned(8)));
        union vfp_state         vfpstate;
#ifdef CONFIG_ARM_THUMBEE
        unsigned long           thumbee_state;  /* ThumbEE Handler Base register */
#endif
};

ARM64平台:

struct thread_info {
        unsigned long           flags;          /* low level flags */
        mm_segment_t            addr_limit;     /* address limit */
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
        u64                     ttbr0;          /* saved TTBR0_EL1 */
#endif
        int                     preempt_count;  /* 0 => preemptable, <0 => bug */
};

猜你喜欢

转载自blog.csdn.net/qq_33160790/article/details/80716162