1.什么是进程:
进程是操作系统基本的资源管理单位,进程又是执行的代码片段(一个程序创建一个或多个进程,一个进程又可执行多个程序代码,将一个代码片段加载到内存中并让其执行也就创建了一个进程),它有生命周期:包含了创建、执行、等待、退出等状态。
一个进程不仅仅只是占用了加载代码段的内存,也有其他资源集合,在Linux内核中 task_struct完整描述了一个进程所有的资源信息。
task_struct 包含了这些信息:
(1)标示符 : 描述本进程的唯一标识符,用来区别其他进程。
pid_t __pgrp; /* Accessed via process_group() */
pid_t tty_old_pgrp;
pid_t session;
pid_t tgid;
//标记符,反应进程的状态信息
unsigned long flags; /* per process flags, defined below */
其中 tgid为线程组标识符
(2)状态 :任务状态,退出代码,退出信号等。
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
其中state可以有以下取值:
<strong>#define TASK_RUNNING 0//进程要么正在执行,要么准备执行
#define TASK_INTERRUPTIBLE 1 //可中断的睡眠,可以通过一个信号唤醒
#define TASK_UNINTERRUPTIBLE 2 //不可中断睡眠,不可以通过信号进行唤醒
#define __TASK_STOPPED 4 //进程停止执行
#define __TASK_TRACED 8 //进程被追踪
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16 //僵尸状态的进程,表示进程被终止,但是父进程还没有获取它的终止信息,比如进程有没有执行完等信息。
#define EXIT_DEAD 32 //进程的最终状态,进程死亡
/* in tsk->state again */
#define TASK_DEAD 64 //死亡
#define TASK_WAKEKILL 128 //唤醒并杀死的进程
#define TASK_WAKING 256 //唤醒进程</strong>
(3)优先级 :相对于其他进程的优先级。
int prio, static_prio; //动态优先级,静态优先级
struct list_head run_list;
prio_array_t *array;
其中值越大,优先级越低
(4)程序计数器:程序中即将被执行的下一条指令的地址。
(5)内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
struct mm_struct *mm, *active_mm;
进程地址空间,描述了在用户空间中,代码段,数据段,堆,栈,mmap等所有相关信息
(6)上下文数据:进程执行时处理器的寄存器中的数据。
(7) I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
(8) 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
他人对task_struct 结构体的注释:
https://blog.csdn.net/qq_29503203/article/details/54618275
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;
};
2.进程的几种状态
用户进程属性的获取:
pid 进程号,进程的唯一编号。
CPU 只有一个,而进程有多个,因此,某个时刻只能执行一个进程,其它的进程则处于相应的其它状态:
运行状态:占有资源,执行。
就绪状态:除了 CPU资源外,其它资源都已经获取,等待调度算法来调度执行。
等待状态:除了 PCU 资源之外,还要等待其它资源或者时间,可中断等待(可以被信号打断)和
不可中断等待。
停止状态:正在被跟踪或者调度的进程。
僵死状态:用户资源已经回收,PCB 内核资源没有回收,已经不能执行。
3.进程的生命周期
a)创建
进程的创建主要用fork()函数,fork函数为每个父进程创建一个子进程。
在创建过程中,操作系统是完成以下事情;
用户空间中:实际上执行的操作是将程序的代码段,数据段,BSS 段从磁盘加载到内存,并且申请
堆栈空间。
内核空间中:为这个进程分配惟一的 PID 标识,同时,在内核中为这个进程申请进程控制块 PCB,
初始化相关的信息
b)执行过程
在进程执行过程中,要执行新的代码。实际上我们创建一个新进程,更多的是期望在这个进程中执
行新的代码,而不是原来的程序代码
int execl(const char *path, const char *arg, ...);
//参数:可执行程序的路径 ,参数列表(以 NULL 结束)
int execlp(const char *file, const char *arg, ...);
//第一个参数是文件名(要求系统在$PATH环境变量所列路径下搜索),后面是参数列表
c)退出过程
进程在以下几个情况会退出。
(1)被别人 Kill 掉。kill 一个命令。Kill -9 pid
(2)显式的执行 exit 系列函数。_exit。
(3)进程遇到 main 函数的},没有代码可以执行。另外,main 函数 return。
*注意:exit是一个系统函数,是退出这个进程。return是 C/C++关键字,退出这个函数
d)进程资源回收
进程退出有退出的状态,另外,进程资源的回收在 exit 的时候仅仅是释放了它的用户空间资源,而
内核空间资源 PCB 没有回收,转而由它的父亲进程通过 wait 相关的函数来回收。
子进程在退出时会给父亲进程发送一个信号(SIGCHLD),父亲进程可以显式的调用 wait/waitpid 等
待子进程结束并回收资源,回收资源时,也可以得到子进程退出的状态。
测试样例:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main()
{
pid_t pid;
pid=fork();
if(pid == -1)
{
perror("fork");
}
else if(pid == 0)
{
printf("child pid:%d\n",getpid());
exit(10);
}
else
{
int status;
pid_t w_pid;
sleep(20); //测试僵尸进程
w_pid=waitpid(-1,&status,0);
printf("w_pid:%d,status:%d\n",w_pid,status);
}
return 0;
}