linux进程管理学习笔记

linux 进程管理




1 linux进程控制
进程的四个要素:
有一段程序供其执行
有专用的内核空间椎栈
内核中有一个tash_struct数据结构
有独立的用户空间


task_struct中包含了描述进程和线程的信息
pid_t pid  进程号 最大10亿
volatile long state 进程状态
        TASK_RUNNING  准备就绪
        TASK_INTERRUPTIBLE 处于等待中 等待条件为真是唤醒,信号/中断也可
TASK_UNINTERRUPTIBLE 条件为真是唤醒,信号/中断不可
TASK_STOPPED 进程中止执行(SIGSTOP/SIGTSTP时进入该状态)
      SIGCONT重新回到TASK_RUNNING
TASK_KILLABLE 进程进入睡眠状态 (SIGKILL唤醒)
TASK_TRACED 处于被调试状态
TASK_DEAD 时程退出时

     int exit_state 进程退出时的状态
EXIT_ZOMBIE
EXIT_DEAD


struct mm_struct *mm 进程用户空间描述指针
unsigned int policy 进程的调度策略
int prio 优先级
int static_prio 静态优先级
struct sched _rt_entity rt 进间片


current 指针指向当前正在运行的进程的task_struct


进程的创建
 vfork() sys_vfork()  do_fork()  copy_process()
 fork()  sys_fork()   do_fork()  copy_process()
进程的销毁
 exit() sys_exit() do_exit 




2 linux进程调度
  调度策略
   cfs调度类(kernel/sched_fair.c)
SCHED_NORMAL(SCHED_OTHER) 普通的分时进程
SCHED_BATCH 批处理进程
SCHED_IDLE 只在系统空闲时调度执行
   实时调度类(kernel/sched_rt.c)
SCHED_FIFO 先入先出的实时进程
SCHED_RR   时间片轮转的实进进程


  调度时机
schedule() 完成调度
示例代码如下:
/*
 * schedule() is the main scheduler function.
 */
asmlinkage void __sched schedule(void)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
struct rq *rq;
int cpu;


need_resched:
preempt_disable();
cpu = smp_processor_id();
rq = cpu_rq(cpu);
rcu_sched_qs(cpu);
prev = rq->curr;
switch_count = &prev->nivcsw;


release_kernel_lock(prev);
need_resched_nonpreemptible:


schedule_debug(prev);


if (sched_feat(HRTICK))
hrtick_clear(rq);


spin_lock_irq(&rq->lock);
update_rq_clock(rq);
clear_tsk_need_resched(prev);


if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
if (unlikely(signal_pending_state(prev->state, prev)))
prev->state = TASK_RUNNING;
else
deactivate_task(rq, prev, 1);
switch_count = &prev->nvcsw;
}


pre_schedule(rq, prev);


if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);


put_prev_task(rq, prev);
next = pick_next_task(rq);


if (likely(prev != next)) {
sched_info_switch(prev, next);
perf_event_task_sched_out(prev, next, cpu);


rq->nr_switches++;
rq->curr = next;
++*switch_count;


context_switch(rq, prev, next); /* unlocks the rq */
/*
* the context switch might have flipped the stack from 


under
* us, hence refresh the local variables.
*/
cpu = smp_processor_id();
rq = cpu_rq(cpu);
} else
spin_unlock_irq(&rq->lock);


post_schedule(rq);


if (unlikely(reacquire_kernel_lock(current) < 0))
goto need_resched_nonpreemptible;


preempt_enable_no_resched();
if (need_resched())
goto need_resched;
}
调度的两和方式:
1 主动式:在内核中直接调用schedule()
示例:主动放弃cpu
current->state=TASK_INTERRUPTIBLE;
schedule()l
2 被动式:用户抢占 
                                  从系统调用返回用户空间
 从中断处理程序返回用户空间
 内核抢占
   以下情况不允许内核抢占:
内核正在进行中断处理
   正在处理中断上下文的bottom half处理
   进程正持有spinlock自旋锁、writelock/readlock读写锁
   正在执行调度程序schedule
  内核抢占计数 preempt_count 在进程的thread_info结构中设置
  
  调度标志 TIF_NEED_RESCHED 表明是否需要重新执行一次调度
      在某个进程耗进时间片时
      当一个优先级高的进程进入可执行状态时


  调度步骤
清理当前运行中的进程
先择下一个要运行的进程 (pick_next_task)
设置新进程的运行环境
进程上下文切换
  
  pick_next_task示例代码如下:
/*
 * Pick up the highest-prio task:
 */
static inline struct task_struct *
pick_next_task(struct rq *rq)
{
const struct sched_class *class;
struct task_struct *p;


/*
* Optimization: we know that if all tasks are in
* the fair class we can call that function directly:
*/
        /*如果属于公平调度*/
if (likely(rq->nr_running == rq->cfs.nr_running)) {
p = fair_sched_class.pick_next_task(rq);
if (likely(p))
return p;
}
/*属于实时调度*/
class = sched_class_highest;
for ( ; ; ) {
p = class->pick_next_task(rq);
if (p)
return p;
/*
* Will never be NULL as the idle class always
* returns a non-NULL p:
*/
class = class->next;
}
}
3 linux系统调用
   用户进程是不能访问内核的,在linux内核中有一组用于实现各种系统功能的
函数,就是系统调用
   2.6.32内核中有364个系统调用,其目录arch/arm/include/asm/unistd.h


   工作原理:
   void main(){
creat("testfile",0666);
  }
  程序用适当的值填充寄存器 (适当值可以在unistd.h中查找)
  调用特殊指令跳转到内核某一固定值,内核根据应用程序所填充的固定值来
  找到相应的函数执行
  (特殊指令:   intel cpu中,由中断0x80实现 arm中是swi/svc
   固定的位置:arm中 ENTRY(vector_swi)<entry-common.S>
   相应的函数: 内核根据应用程序传递的系统调用号, 从系统调用表中查找相应
      的函数
  )


  示例代码:
   create的实现原理
   在编译工具链中
   /opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/lib
   反汇编查看
   [root@localhost lib]# arm-linux-objdump -D -S libc.so.6 >log


查看log文件:
部分内容如下:
000b71b0 <creat>:
   b71b0: e51fc028 ldr ip, [pc, #-40] ; b7190 


<pipe2+0x20>
   b71b4: e79fc00c ldr ip, [pc, ip]
   b71b8: e33c0000 teq ip, #0
   b71bc: 1a000006 bne b71dc <creat+0x2c>
   #程序用适当的值填充寄存器 
   #在unistd.h中定义如下:
   #define __NR_osf_old_creat  8 /* not implemented */
   
   b71c0: e1a0c007 mov ip, r7
   b71c4: e3a07008 mov r7, #8
   #调用特殊指令跳转到内核某一固定值,内核根据应用程序所填充的固定值来
   #找到相应的函数执行 calls.s中执行对应系统调用
   b71c8: ef000000 svc 0x00000000
   b71cc: e1a0700c mov r7, ip
   b71d0: e3700a01 cmn r0, #4096 ; 0x1000
   b71d4: 312fff1e bxcc lr
   b71d8: eafd7a30 b 15aa0 <__syscall_error>
   b71dc: e92d4083 push {r0, r1, r7, lr}
   b71e0: eb006d8f bl d2824 


<__libc_enable_asynccancel>
   b71e4: e1a0c000 mov ip, r0
   b71e8: e8bd0003 pop {r0, r1}
   b71ec: e3a07008 mov r7, #8
   b71f0: ef000000 svc 0x00000000
   b71f4: e1a07000 mov r7, r0
   b71f8: e1a0000c mov r0, ip
   b71fc: eb006db8 bl d28e4 


<__libc_disable_asynccancel>
   b7200: e1a00007 mov r0, r7
   b7204: e8bd4080 pop {r7, lr}
   b7208: e3700a01 cmn r0, #4096 ; 0x1000
   b720c: 312fff1e bxcc lr
   b7210: eafd7a22 b 15aa0 <__syscall_error>
   b7214: e320f000 nop {0}
   b7218: e320f000 nop {0}
   b721c: e320f000 nop {0}




   添加系统调用:
1 添加新的内核函数
在kernel/sys.c中添加函数
   /*asmlinkage 使用栈传递参数
     添加到文件最后
   */
   asmlinkage int sys_add(int a,int b){
return a+b;
   }
2 更新头文件unsitd.h
 添加如下代码:
 #define __NR_add(__NR_SYSCALL_BASE+365)
3 针对这个新函数更新系统调用表calls.S
          添加如下代码:
 CALL(sys_add)
        4 重新编译内核
  5 编写应用程序测试系统调用
 示例代码如下:
 #include <stdio.h>
 #include <linux/unistd.h>
 
          main(){
int result;
  result=SYSCALL(361,1,2);
printf("result=",result);
 }
   
4 proc文件系统
   proc文件系统是在用户态检查内核状态的机制
  查看当前内存使用情况
  [root@localhost proc]# cat meminfo 
  
  proc文件目录结构
  apm 高级电源管理信息
  bus 总线以及总线上的设备
  devices 可用的设备信息
  driver 已经启用的驱动程序
  interrupts 中断信息
  ioports  端口使用信息
  version 内核版本


  创建proc文件
  struct proc_dir_entry * create_proc_entry(const char *name,mode_t 


mode,struct proc_dir_entry *parent)
  name 要创建的文件名
  mode 要创建的文件属性
  parent 这个文件的父目录


  创建目录
  struct proc_dir_entry *proc_mkdir(const char *name,struct 


proc_dir_entry *parent)
  name 要创建的文件名
  parent:这个目录的父目录


 删除目录/文件
 void remove_proc_entry(const char *name,struct proc_dir_entry *parent)
 name 要删除的文件名或目录名
 parent 所在的父目录
 


 读写回调函数
 read_proc
   int read_func(char *buffer,char **stat,off_toff,int count,int 


*peof,void *data)
   buffer 返回给用户的信息
   stat 一般不使用
   off 偏移量
   count 用户要取的字节数
   peof 读到文件尾时,需将其置1
   data 一般不使用
   
 wirte_proc
   int write_func(struct file *file,const char *buffer,unsigned long 


count,void *data)
  file 其proc文件对应的file结构
  buffer 待写的数据所在的位置
  count 待写数据的大小
  data 一般不使用
 


  实现一个proc文件的流程
  1 调用create_proc_entry创建一个struc proc_dir_entry
  2 对创建的struct proc_dir_entry进行赋值 read_proc mode owner
    size write_proc
  


5 linux内核异常
  应用程序中,空指针异常(段错误)
  在内核中,如果出现空指针异常,可能会出现死机
  oops信息
   小于c0000000(3g)
  分析步骤
  1 错误码原因提示
  2 调用栈
  3 寄存器
示例代码如下:
oops.c文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>


int D(){
int *p=NULL;
int a=6;
printk("Fuction D\n");
*p=a+5;
}


int C(){
printk("Function C\n");
D();
}


int B(){
printk("Fuction B\n");
C();
}


int A(){
printk("Fuction A\n");
B();
}


int opps_init(){
printk("hi\n");
A();
return 0;
}


void opps_exit(){


}


module_init(opps_init);
module_exit(opps_exit);


Makefile文件如下:


ifeq ($(KERNELRELEASE),)
KERNELDIR ?=/opt/FriendlyARM/mini6410/linux/linux-2.6.38
PWD :=$(shell pwd)


modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
        rm -fr *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions 


*.order Module*
.PHONY: modules modules_install clean


else
        obj-m :=oops.o
endif




编译程序:make
将oops.ko文件下载到开发板中
在开发板中加载模块:insmod oops.ko


加载模块提示如下信息:
hi
Fuction A
Fuction B
Function C
Fuction D
#明确出错原因,无法访问空指针
Unable to handle kernel NULL pointer dereference at virtual address 


00000000
pgd = cd574000
[00000000] *pgd=5d536831, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1] PREEMPT
last sysfs file: /sys/devices/virtual/vc/vcsa4/dev
Modules linked in: oops(P+) fa_cpu_pfn(P)
CPU: 0    Tainted: P             (2.6.38-FriendlyARM #14)
#根据pc寄存器确定出错位置
#出错指令为D函数偏移为0x14的指令
PC is at D+0x14/0x20 [oops]
LR is at D+0xc/0x20 [oops]
#出错地址的指令为bf006014
pc : [<bf006014>]    lr : [<bf00600c>]    psr: 60000013
sp : cd543ec8  ip : 00002f00  fp : 00000000
r10: 0000001c  r9 : 00000022  r8 : 00000000
r7 : bf006068  r6 : 00000001  r5 : 00000000  r4 : bf006128
r3 : 00000000  r2 : 0000000b  r1 : bf0060d0  r0 : 0000000d
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 00c5387d  Table: 5d574008  DAC: 00000015
Process insmod (pid: 987, stack limit = 0xcd542268)
Stack: (0xcd543ec8 to 0xcd544000)
3ec0:                   cd543ebc bf006078 cd542000 c01684d8 c07268b4 


00000001
3ee0: bf006128 bf006128 bf006170 bf006128 00000000 00000001 cd46e900 


00000001
3f00: 0000001c c01ae270 bf006134 c01682b4 c01abf98 c051b76c bf00624c 


000afa95
3f20: cd5370a8 d08b2000 00005d80 d08b63ac d08b6260 d08b7ccc cd65b240 


00000268
3f40: 000003e8 00000000 00000000 00000020 00000021 00000009 00000000 


00000007
3f60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 


c01e0078
3f80: cd4bd9c0 00000001 00000069 beffae34 00000080 c0172788 cd542000 


00000000
3fa0: 00000000 c01725e0 00000001 00000069 000bc040 00005d80 000afa95 


7fffffff
3fc0: 00000001 00000069 beffae34 00000080 beffae38 000afa95 beffae38 


00000000
3fe0: 00000001 beffaadc 0001cb50 401f1664 60000010 000bc040 5fffe821 


5fffec21
[<bf006014>] (D+0x14/0x20 [oops]) from [<bf006078>] 


(init_module+0x10/0x1c [oops])
[<bf006078>] (init_module+0x10/0x1c [oops]) from [<c01684d8>] 


(do_one_initcall+0xbc/0x190)
[<c01684d8>] (do_one_initcall+0xbc/0x190) from [<c01ae270>] 


(sys_init_module+0x158c/0x1754)
[<c01ae270>] (sys_init_module+0x158c/0x1754) from [<c01725e0>] 


(ret_fast_syscall+0x0/0x30)
Code: e59f0010 eb543a5d e3a03000 e3a0200b (e5832000) 
---[ end trace 78b4b200a26b57f5 ]---
Segmentation fault


反汇编部分信息如下:


oops.ko:     file format elf32-littlearm




Disassembly of section .text:


00000000 <D>:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>


int D(){
   0: e92d4008 push {r3, lr}
int *p=NULL;
int a=6;
printk("Fuction D\n");
   4: e59f0010 ldr r0, [pc, #16] ; 1c <D+0x1c>
   8: ebfffffe bl 0 <printk>
*p=a+5;
   c: e3a03000 mov r3, #0
  10: e3a0200b mov r2, #11
#定位出错位置
#出错指令为D函数偏移为0x14的指令
#Code: e59f0010 eb543a5d e3a03000 e3a0200b (e5832000)
  14: e5832000 str r2, [r3]
}
  18: e8bd8008 pop {r3, pc}
  1c: 00000000 andeq r0, r0, r0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值