一、什么是进程
进程,就是程序的一个执行实例,或正在执行的程序。
详细介绍请看
点击打开链接
那么进程在Linux中有几种状态呢?如下:
1、R
处于运行或可运行状态,即进程正在运行或在运行队列(可执行队列)中等待。只有在该状态的进程才可能在CPU上运行,同一时刻可能有多个进程处于该状态。
(注:很多教科书上将正在CPU上执行的进程的状态定义为Running,将可执行但尚未被调度执行的进程状态定义为Ready,这2种状态在Linux下统一为R状态)
2、S
处于可中断的睡眠状态,即进程在休眠中,由于在等待某个事件的完成(或等待某个条件的形成或等待某个信号等)
(注:等待socket连接、等待信号量等)而被挂起;当这些事件发生时,对应的等待队列中的一个或多个进程将被唤醒。一般情况下,进程列表中绝大多数进程都处于该状态。
3、D
处于不可中断的睡眠状态,不可中断指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号,无法用kill命令杀死,进程必须等待直到有中断发生。
4、T
处于暂停或跟踪状态。进程收到SIGSTOP、SIGSTP、SIGTIN、SIGTOU等信号进入暂停状态(除非进程处于不可中断的睡眠状态);当接着向进程发送1个SIGCONT信号,进程可以从暂停状态恢复到运行或能运行状态。
当进程被跟踪时,它处于被跟踪状态。“被跟踪”指进程暂停下来,等待跟踪它的进程对它进行操作。例如在GDB调试中,对被跟踪的进程设置某个断点,进程执行到断点处停下来的时候就处于被跟踪状态。
暂停与跟踪状态还是有区别的,被跟踪状态相当于在暂停状态之上多了一层保护,处于被跟踪状态的进程不能响应SIGCONT信号而被唤醒,只能等到调试进程通过ptrace系统调用执行ptrace_cont、ptrace_detach等操作(通过ptrace系统调用的参数指定操作),或调试进程退出,被调试的进程才能恢复到R状态。
5、Z
处于僵死状态,也称退出状态。它指进程已经结束,放弃了几乎所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置来记载该进程的退出状态等信息(task_struct结构体[保存了该进程的退出码])供其他进程收集。
6、X
进程在退出过程中可能不会保留它的task_struct。例如某个进程是多线程程序中被detach过的进程;或者父进程通过设置SIGCHLD信号的Handler为SIG_IGN,显示的忽略了SIGCHLD信号。
此时该进程被置于exit_dead退出状态,这意味着接下来的代码立即会将该进程彻底释放。故exit_dead状态非常短暂,几乎不可能通过ps命令捕捉到。
二、模拟实现僵尸进程与孤儿进程
1、僵尸进程
Makefile文件:
1
2 .PHONY:jiangshi
3
4 jiangshi:jiangshi.c
5 gcc -o $@ $^
6
7 .PHONY:clean
8 clean:
9 rm -f jiangshi
jiangshi.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 int main(){
4 pid_t id=fork();
5 if(id<0){
6 perror("fork");
7 return 1;
8 }
9 else if(id>0){//parent
10 printf("parent[%d] is sleeping...\n",getpid());
11 sleep(30);
12 }
13 else{
14 printf("child[%d] is begin Z...\n",getpid());
15 sleep(5);
16 exit(EXIT_SUCCESS);
17 }
18 return 0;
19 }
2、孤儿进程
Makefile文件
1
2
3 .PHONY:guer
4
5 guer:guer.c
6 gcc -o $@ $^
7
8 .PHONY:clean
9 clean:
10 rm -f guer
guer.c
1 #include<unistd.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 int main(){
5 pid_t id=fork();
6 if(id<0){
7 perror("fork");
8 return 1;
9 }
10 else if(id==0){//child
11 printf("I am child,pid:%d\n",getpid());
12 sleep(10);
13 }
14 else{//parent
15 printf("I am parent,pid:%d\n",getpid());
16 sleep(3);
17 exit(0);
18 }
19 return 0;
20 }
三、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值
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;
};