linux进程管理学习笔记

    xiaoxiao2025-09-16  167

    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
    转载请注明原文地址: https://ju.6miu.com/read-1302700.html
    最新回复(0)