实验要求:
1.编写用户态程序,程序中需设计能引起进程状态发生变化的流程。
2.编写内核态模块,定时获取输入参数指定进程的运行状态。
3.通过内核态的记录指定进程运行状态变化的情况,需记录三种以上。
实验原理:
1. linux系统中进程的状态及其切换条件,linux系统状态转换图及每种状态转换的触发条件,找到对应进程状态定义的宏定义。
Linux系统中的进程主要有以下5种状态:
(1)TASK_RUNNING(可运行状态)。正在运行的进程或在可运行进程队列(run_queue)中等待运行的进程处于该状态。它实际上包含一般操作系统原理教材中所谓进程三种基本状态中的运行态和就绪两种状态。
当CPU空闲时,进程调度程序只在处于该状态的进程中选择优先级最高的进程运行。Linux中运行态的进程可以进一步细分为3种:内核运行态、用户运行态和就绪态。
(2)TASK_INTERRUPTIBLE(可中断阻塞状态)。处于可中断阻塞状态的进程排成一个可中断阻塞状态进程队列,该队列中的阻塞进程在资源有效时,能被信号或中断唤醒进入到运行态队列。
(3)TASK_UNINTERRUPTIBLE(不可中断阻塞状态)。不可中断指的是进程不响应信号。处于不可中断阻塞状态的进程排成一个不可中断阻塞状态进程队列。该队列中的阻塞进程,不可被其他进程唤醒,只有被使用wake_up()函数明确唤醒时才能转换到可运行的就绪状态。
(4)TASK_STOP/TASK_TRACED(暂停状态)。当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号,让进程转换到可运行状态。
(5)TASK_DEAD-EXIT_ZOMBIE(僵尸状态)。表示进程停止但尚未消亡的一种状态。此时进程已经结束运行并释放掉大部分资源,但父进程尚未收回其PCB。在进程退出时,将状态设为TASK_ZOMBIE,然后发送信号给父进程,由父进程再统计其中的一些数据后,释放它的task_struct结构。处于该状态的进程已经终止运行,但是父进程还没有询问其状态。
转换图:
宏定义:
2. 用户态程序中显式的改变进程运行状态的变化
调用sleep函数可以进入阻塞态,空循环为运行状态,gdb设置断点并在断点停止运行为暂停状态
参考代码:
exam.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
printf("pid=%d\n",getpid());//得到进程号
sleep(6);//进入睡眠状态
printf("睡眠完成,开始运行\n");
while(1){}//一直运行
}
timer.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include<linux/init.h>
#include<linux/interrupt.h>
#include<linux/sched.h>
#include <linux/version.h>
#include <linux/ktime.h>
#include <linux/kallsyms.h>
#include <linux/sched.h>
struct hrtimer timer;
long laststate=0xff;
//unsigned int laststate=0xff;
static int pid;
module_param(pid,int,0644);
static void print_task_state(void)
{
struct task_struct *p;
p=pid_task(find_vpid(pid),PIDTYPE_PID);
if (p->state != laststate){
laststate=p->state;
if(p->state==0)
//printk("%x\t%s\n",p->state,ktime_get());
//printk("%x\t\n",p->state);
printk("R (running)\n");
else if(p->state==1)
printk("S (sleeping)\n");
//else if(p->state==0x6C)
//printk("t (tracing stop)\n");
else
printk("t (tracing stop)\n");
}
}
enum hrtimer_restart hrtimer_func(struct hrtimer *t)
{
//printk("----hrtimer_func----\n");
print_task_state();
//实现循环定时,5秒一次
hrtimer_forward_now(t, ktime_set(5,1000));
return HRTIMER_RESTART;
}
static int __init test_drv_init(void)
{
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
timer.function = hrtimer_func;
hrtimer_start(&timer, ktime_set(1,1000), HRTIMER_MODE_REL);
return 0;
}
static void __exit test_drv_exit(void)
{
hrtimer_cancel(&timer);
printk("----test_drv_exit----\n");
}
module_init(test_drv_init);
module_exit(test_drv_exit);
MODULE_LICENSE("GPL");
timer.c对应的Makefile文件:
#Makefile文件注意:假如前面的.c文件起名为first.c,那么这里的Makefile文件中的.o文
#件就要起名为first.o 只有root用户才能加载和卸载模块
obj-m:=timer.o #产生task_struct模块的目标文件
#目标文件 文件 要与模块名字相同
CURRENT_PATH:=$(shell pwd) #模块所在的当前路径
LINUX_KERNEL:=$(shell uname -r) #linux内核代码的当前版本
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #编译模块
#[Tab] 内核的路径 当前目录编译完放哪 表明编译的是内核模块
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理模块