您的位置:首页 > 运维架构 > Linux

linux进程管理学习笔记

2016-08-15 15:11 543 查看
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 ldrip, [pc, #-40]; b7190 

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

<__libc_enable_asynccancel>
   b71e4:e1a0c000 movip, r0
   b71e8:e8bd0003 pop{r0, r1}
   b71ec:e3a07008 movr7, #8
   b71f0:ef000000 svc0x00000000
   b71f4:e1a07000 movr7, r0
   b71f8:e1a0000c movr0, ip
   b71fc:eb006db8 bld28e4 

<__libc_disable_asynccancel>
   b7200:e1a00007 movr0, r7
   b7204:e8bd4080 pop{r7, lr}
   b7208:e3700a01 cmnr0, #4096; 0x1000
   b720c:312fff1e bxcclr
   b7210:eafd7a22 b15aa0 <__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 ldrr0, [pc, #16]; 1c <D+0x1c>
   8:ebfffffe bl0 <printk>
*p=a+5; 
   c:e3a03000 movr3, #0
  10:e3a0200b movr2, #11
#定位出错位置
#出错指令为D函数偏移为0x14的指令
#Code: e59f0010 eb543a5d e3a03000 e3a0200b (e5832000)
  14:e5832000 strr2, [r3]
}
  18:e8bd8008 pop{r3, pc}
  1c:00000000 andeqr0, r0, r0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: