操作系统课内实验报告
2.3.1 structtask_struct
进程的内核数据结构是task_struct结构,它使用两个指针next_task和prev_task将各个进程连成一个循环双链表,相应的指针p_opptr、p_pptr、p_cptr、p_ysptr和p_osptr来表示进程之间的家族关系。
structtask_struct //进程描述符。 {
long state; //任务的运行状态(-1 不可运行,0 可运行(就绪),>0 已停止)。 long counter; // 任务运行时间计数(递减)(滴答数),运行时间片。
long priority; //运行优先数。任务开始运行时counter = priority,越大运行越长。 long signal; //信号。是位图,每个比特位代表一种信号,信号值=位偏移值+1。 structsigactionsigaction[32]; // 信号执行属性结构,对应信号将要执行的操作和标志信息。 long blocked; //进程信号屏蔽码(对应信号位图)。 intexit_code; //任务执行停止的退出码,其父进程会取。 unsigned long start_code; // 代码段地址。
unsigned long end_code; // 代码长度(字节数)。
unsigned long end_data; // 代码长度 + 数据长度(字节数)。 unsigned long brk; // 总长度(字节数)。 unsigned long start_stack; // 堆栈段地址。 long pid; // 进程标识号(进程号)。 long father; // 父进程号。 long pgrp; // 父进程组号。 long session; // 会话号。 long leader; // 会话首领。
unsigned short uid; //用户标识号(用户id)。 unsigned short euid; // 有效用户id。 unsigned short suid; // 保存的用户id。 unsigned short gid; // 组标识号(组id)。 unsigned short egid; // 有效组id。 unsigned short sgid; // 保存的组id。 long alarm; // 报警定时值(滴答数)。 long utime; // 用户态运行时间(滴答数)。 long stime; // 系统态运行时间(滴答数)。 long cutime; // 子进程用户态运行时间。 long cstime; // 子进程系统态运行时间。 long start_time; // 进程开始运行时刻。
unsigned short used_math; // 标志:是否使用了协处理器。 inttty;// 进程使用tty的子设备号。-1 表示没有使用。 unsigned short umask; // 文件创建属性屏蔽位。 structm_inode * pwd; // 当前工作目录i节点结构。 structm_inode * root; // 根目录i节点结构。
structm_inode * executable; // 执行文件i节点结构。
unsigned long close_on_exec; // 执行时关闭文件句柄位图标志。(参见include/fcntl.h) struct file * filp[NR_OPEN]; // 进程使用的文件表结构。
structdesc_structldt[3]; // 本任务的局部表描述符。0-空,1-代码段cs,2-数据和堆栈段ds&ss。
structtss_structtss; // 本进程的任务状态段信息结构。 2.3.2 task
task 定义为指向structtask_struct结构的指针数组,这个数组的每个项代表系统中的一
9
操作系统课内实验报告
个任务。数组的大小是NR_TASKS,它规定了系统中同时可以运行的任务的数量。
externstructtask_struct *task[NR_TASKS]; 2.3.3 tarray_freelist
自由时间片列表tarray_freelist拥有一个说明task 数组中自由位置的列表,即当前没有被使用的空位置。
structtask_struct **tarray_freelist; 2.3.4 struct--linux_binprm
内核中为可执行程序的装入定义了一个数据结构linux_binprm,以便将运行一个可执行文件时所需的信息组织在一起。
/usr/src/linux/include/linux/binfmts.h
structlinux_binprm{
charbuf[BINPRM_BUF_SIZE];
struct page * page[MAX_ARG_PAGES]; structmm_struct * mm;
unsigned long p; /* current top of mem */ intsh_bang;
struct file * file; inte_uid, e_gid;
kernel_cap_tcap_inheritable, cap_permitted, cap_effective; void * security; intargc, envc;
char * filename; /* Name of binary as seen by procps */ char * interp; /* Name of the binary really executed. Most of the time same as filename, but could be different for binfmt_{misc,script} */ unsignedinterp_flags; unsignedinterp_data;
unsigned long loader, exec; };
#ifdef __KERNEL__#define MAX_ARG_PAGES 32
linux_binprm->page[]
每个参数的最大长度为一个物理页面,所以linux_binprm中有个页面指针数组,数组的大小为允许的最大参数个数MAX_ARG_PAGES,目前这个常数定义为32。 2.3.5进程状态
TASK_RUNNING:运行状态;
TASK_INTERRUPTIBLE:等待状态可以让其他进程唤醒; TASK_UNINTERRUPTIBLE:等待状态但不让其他进程唤醒; TASK_ZOMBIE:进程已经结束执行但尚未消亡;
TASK_STOPPED:进程暂停用其他进程的信号才能唤醒; TASK_SWAPPING:被换出的进程。
10
操作系统课内实验报告
2.4常量和出错信息的意义
#define NR_TASKS 64 // 系统中同时最多任务(进程)数。
#define HZ 100 // 定义系统时钟滴答频率(1 百赫兹,每个滴答10ms) #define FIRST_TASK task[0] // 任务0 比较特殊,所以特意给它单独定义一个符号。 #define LAST_TASK task[NR_TASKS-1] // 任务数组中的最后一项任务。 #define TASK_RUNNING 0 // 进程正在运行或已准备就绪。 #define TASK_INTERRUPTIBLE 1 // 进程处于可中断等待状态。
#define TASK_UNINTERRUPTIBLE 2 // 进程处于不可中断等待状态,主要用于I/O 操作等待。
#define TASK_ZOMBIE 3 // 进程处于僵死状态,已经停止运行,但父进程还没发信号。
#define TASK_STOPPED 4 // 进程已停止。
2.5各模块/函数详细框图
2.5.1 do_fork模块
当进程调用fork时,该进程从概念上被分成两部分—祖先和子孙可以自由选择不同飞路径。在fork之后,祖先进程和其子进程几乎是等同的,它们所有的变量都有相同的值,它们打开的文件都相同,等等。但是如果祖先进程改变了一个变量的值,子进程看不到这个变化,反之亦然,子进程是祖先进程的一个拷贝,但它们并不共享内存内容。
linux保留了传统的fork并增加了一个更加通用的函数__clone。鉴于fork创建一个新进程的子孙进程后。子孙进程虽然是祖先进程的拷贝,但是它们并不共享内容,__clone允许你定义祖先进程和子进程所应该共享的内容,如果你没有给__clone提供它所能识别的五个标志,子孙进程和祖先进程就不会共享任何内容,这就和传统fork类似。如果你提供了全部的五个标志,子孙进程就可以和祖先进程共享任何内容,这就和传统的线程类似,其他标记的不同组合可以使你完成介于两者之间的功能。
给新进程一个 分配新的进程控制快并给新的进程控制快赋值 设置进程之间的家族信息 把新进程放到Pidhash表中 把进程设置为TASK_RUNNING状态 图2.1 do fork 函数流程图 11
操作系统课内实验报告
long do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr) {
struct task_struct *p; int trace = 0; long nr;
if (unlikely(clone_flags & CLONE_STOPPED)) { /*
*如果在clone_flags中设置了CLONE_PID标志,就返回一个错误(-EPERM)。
*因为CLONE_PID有特殊的作用,当这个标志为1时,父,子进程(线程)共用一个进程号 *即子进程虽然有自己的task_struct结构,却使用父进程的pid. *但是只有0号进程(即系统中的空线程)才允许使用这个标志。 */ } /*
* When called from kernel_thread, don't do user tracing stuff. */
if (likely(user_mode(regs)))
p = copy_process(clone_flags, stack_start, regs, stack_size,
12
static int __read_mostly count = 100;
if (count > 0 && printk_ratelimit()) { }
char comm[TASK_COMM_LEN]; count--;
printk(KERN_INFO \
\
get_task_comm(comm, current), clone_flags & CLONE_STOPPED);
trace = tracehook_prepare_clone(clone_flags);