深入理解linux内核---进程(上)

    进程是计算机科学史上成功的概念之一,OS教科书中这样定义:进程是程序执行时的一个实例。从内核观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的实体。Linux使用轻量级进程(LWP)对多线程应用程序提供更好的支持。

    进程描述符:task_struct,定义在/linux-2.6.xx/include/linux/sched.h

struct task_struct {
	/* 进程状态 */
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	/* 进程的基本信息 */
	struct thread_info *thread_info;
	/* 进行的动态优先权和静态优先权 */
	int prio, static_prio;
	/* 进程所在运行队列。每个优先级对应一个运行队列 */
	struct list_head run_list;
	/* 指向当前运行队列的prio_array_t */
	prio_array_t *array;
	/* 进程的平均睡眠时间 */
	unsigned long sleep_avg;
	/**
	 * timestamp-进程最近插入运行队列的时间。或涉及本进程的最近一次进程切换的时间
	 * last_ran-最近一次替换本进程的进程切换时间。
	 */
	unsigned long long timestamp, last_ran;
	/**
	 * 进程的调度类型:sched_normal,sched_rr或者sched_fifo
	 */
	unsigned long policy;
	/**
	 * time_slice-在进程的时间片中,还剩余的时钟节拍数。
	 * first_time_slice-如果进程肯定不会用完其时间片,就把该标志设置为1.
	 *            xie.baoyou注:原文如此,应该是表示任务是否是第一次执行。这样,如果是第一次执行,并且在开始运行
	 *                         的第一个时间片内就运行完毕,那么就将剩余的时间片还给父进程。主要是考虑到有进程
	 *                         会大量的动态创建子进程时,而子进程会立即退出这种情况。如果不还给父进程时间片,会对这种进程不公平。
	 */
	unsigned int time_slice, first_time_slice;
	/**
	 * 通过此链表把所有进程链接到一个双向链表中。
	 */
	struct list_head tasks;

	/**
	 * mm:指向内存区描述符的指针
	 */
	struct mm_struct *mm, *active_mm;

	unsigned did_exec:1;
	/**
	 * 进程PID
	 */
	pid_t pid;
	/**
	 * 线程组领头线程的PID。
	 */
	pid_t tgid;
	/* 
	 * pointers to (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with 
	 * p->parent->pid)
	 */
	/**
	 * 指向创建进程的进程的描述符。
	 * 如果进程的父进程不再存在,就指向进程1的描述符。
	 * 因此,如果用户运行一个后台进程而且退出了shell,后台进程就会成为init的子进程。
	 */
	struct task_struct *real_parent; /* real parent process (when being debugged) */
	/**
	 * 指向进程的当前父进程。这种进程的子进程终止时,必须向父进程发信号。
	 * 它的值通常与real_parent一致。
	 * 但偶尔也可以不同。例如:当另一个进程发出监控进程的ptrace系统调用请求时。
	 */
	struct task_struct *parent;	/* parent process */
	/**
	 * 链表头部。链表指向的所有元素都是进程创建的子进程。
	 */
	struct list_head children;	/* list of my children */
	/**
	 * 指向兄弟进程链表的下一个元素或前一个元素的指针。
	 */
	struct list_head sibling;	/* linkage in my parent's children list */
	/**
	 * P所在进程组的领头进程的描述符指针。
	 */
	struct task_struct *group_leader;	/* threadgroup leader */

	/* PID/PID hash table linkage. */
	/**
	 * PID散列表。通过这四个表,可以方便的查找同一线程组的其他线程,同一会话的其他进程等等。
	 */
	struct pid pids[PIDTYPE_MAX];

	struct completion *vfork_done;		/* for vfork() */
	/**
	 * 子进程在用户态的地址。这些用户态地址的值将被设置或者清除。
	 * 在do_fork时记录这些地址,稍后再设置或者清除它们的值。
	 */
	int __user *set_child_tid;		/* CLONE_CHILD_SETTID */
	int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID */

	/**
	 * 进程的实时优先级。
	 */
	unsigned long rt_priority;
	/**
	 * 以下三对值用于用户态的定时器。当定时器到期时,会向用户态进程发送信号。
	 * 每一对值分别存放了两个信号之间以节拍为单位的间隔,及定时器的当前值。
	 */
	unsigned long it_real_value, it_real_incr;
	cputime_t it_virt_value, it_virt_incr;
	cputime_t it_prof_value, it_prof_incr;
	/**
	 * 每个进程的动态定时器。用于实现ITIMER_REAL类型的间隔定时器。
	 * 由settimer系统调用初始化。
	 */
	struct timer_list real_timer;
	/**
	 * 进程在用户态和内核态下经过的节拍数
	 */
/* file system info */
	/**
	 * 文件系统在查找路径时使用,避免符号链接查找深度过深,导致死循环。
	 * link_count是__do_follow_link递归调用的层次。
	 * total_link_count调用__do_follow_link的总次数。
	 */
	int link_count, total_link_count;

/* filesystem information */
	/**
	 * 与文件系统相关的信息。如当前目录。
	 */
	struct fs_struct *fs;
/* open file information */
	/**
	 * 指向文件描述符的指针
	 */
	struct files_struct *files;
/* namespace */
	struct namespace *namespace;
/* signal handlers */
	/**
	 * 指向进程的信号描述符的指针
	 */
	struct signal_struct *signal;
	/**
	 * 指向进程的信号处理程序描述符的指针
	 */
	struct sighand_struct *sighand;

	/**
	 * blocked-被阻塞的信号的掩码
	 * real_blocked-被阻塞信号的临时掩码(由rt_sigtimedwait系统调用使用)
	 */
	sigset_t blocked, real_blocked;
	/**
	 * 存放私有挂起信号的数据结构
	 */
	struct sigpending pending;

	/**
	 * 信号处理程序备用堆栈的地址
	 */
	unsigned long sas_ss_sp;
	/**
	 * 信号处理程序备用堆栈的大小
	 */
	size_t sas_ss_size;
	/**
	 * 指向一个函数的指针,设备驱动程序使用这个函数阻塞进程的某些信号
	 */
	int (*notifier)(void *priv);};

进程状态:

    可运行状态(TASK_RUNNING),

    可中断的等待状态(TASK_INTERRUPTIBLE),

    不可中断的等待状态(TASK_UNINTERRUPTIBLE),

    暂停状态(TASK_STOPPED),

    跟踪状态(TASK_TRACED),

    僵死状态(TASK_ZOMBIE),

    僵死撤销状态(EXIT_CODE).

设置方式:task->state = state;  宏  set_current_state(state);  set_task_state(task,state);

进程描述符处理:Linux动态分配连续的8K的空间(两个页框)存放与进程相关的thread_info结构与进程内核态栈,使用联合体thread_info表示

union thread_union{

struct thread_info thread_info;

unsigned long stack[2048];

};

这样内核就很容易从esp的值获得当前在CPU上正在运行进程的thread_info结构的地址,进而获得进程描述符task_struct地址。屏蔽掉esp低13位就可以获得thread_union结构的基地址,这项工作由current_thread_info()函数完成,在asm-i386/thread_info.h中如下

static inline struct thread_info *current_thread_info(void)
{
	struct thread_info *ti;
	__asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1)));
	return ti;
}

这是一段内联汇编,输出部是“=r” (ti)返回thread_info的地址存在寄存器中,输入部是(~(THREAD_SIZE-1))相当于待屏蔽结构体低位置零,指令为esp与输入想与。

runqueue队列与进程优先级数组定义在/linux/kernel/sched.c中,具体如下

/**
 * 进程优先级数组。每个CPU对应一个此结构。
 */
struct prio_array {
	/**
	 * 链表中进程描述符的数量。
	 */
	unsigned int nr_active;
	/**
	 * 优先权数组。当且仅当某个优先权的进程链表不为空时设置相应的位标志。
	 */
	unsigned long bitmap[BITMAP_SIZE];
	/**
	 * 140个优先权队列的头结点。
	 */
	struct list_head queue[MAX_PRIO];
};
runqueue队列会在进程调度一章介绍先不详细了解,enqueue_task(struct task_struct *p, prio_array_t *array)将某一进程加入某一优先级队列中。

    linux内核利用pid_hash(指针数组类型为hlist_head*)加速从进程pid到进程描述符的转换,利用链表法解决hash冲突,hash函数为hash_long()里面用了2的32次的黄金分割附近的素数(斐波那契数列很神奇)。

    等待队列,等待队列头、等待队列元素定义在include/linux/wait.h中,wait_queue_head_t,wait_queue_t,相关操作定义在kernel/wait.c中,默认的唤醒函数default_wake_function中调用了try_to_wake_up来唤醒函数(在sched.c中)。

    进程资源限制存放在current->signal->rlim字段,该字段类型为rlimit结构体数组,每个资源限制对应一项,利用getrlimit()、setrlimit()系统调用可以修改资源限制。(/include/resource.h)

    内核中的双向链表待更新,记得有人说过,这样的使用才是真正的数据结构。

    

    介绍进程fork、clone、vfork与进程退出实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值