`

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 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_creat8/* 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

分享到:
评论

相关推荐

    Linux学习笔记(强悍总结值得一看)_linux_linux学习笔记_

    这份"Linux学习笔记(强悍总结值得一看)"是Linux初学者的宝贵资源,也适合有经验的用户作为参考手册。以下是对笔记内容的详细概述: 1. **Linux常用命令**: Linux命令行是其强大的工具,掌握常用命令是Linux学习...

    Linux 进程 线程学习笔记

    ### Linux进程与线程创建详解 #### 进程与线程的概念 在深入探讨Linux下C语言编程中进程和线程的创建之前,我们先来理解一下进程与线程的基本概念。 - **进程**:是操作系统进行资源分配和调度的基本单位,每个...

    Linux学习笔记Linux学习资料Linux教程

    【linux学习笔记-10】Linux进程相关系统调用(三).doc 【linux学习笔记-11】守护进程daemon.doc 【linux学习笔记-12】守护进程的日志实现.doc 【linux学习笔记-13】基本进程通信--文件锁.doc 【linux学习笔记-14】...

    非常宝贵的LINUX学习笔记

    【linux学习笔记-8】Linux进程相关系统调用(1) 【linux学习笔记-9】Linux进程相关系统调用(2) 【linux学习笔记-10】Linux进程相关系统调用(3) 【linux学习笔记-11】守护进程daemon 【linux学习笔记-12】守护...

    深入理解LINUX内存管理学习笔记

    2,一直以来学习LINUXkernel的知识缺乏系统化,借对这本书的学习,系统化 的学习一下LINUXkernel。 3,自己一直在做一个toosmall,toosimple的单进程,特权模式,64bit保 护模式的称不上OS的OS,已经做完了...

    Linux学习笔记【博文整理系列】

    学习linux的笔记,发到博文了,鉴于文档方便保存和查阅,发出来,免费的哈 ...文档列表: Linux笔记——vim常用操作及扩展补充...Linux笔记——linux进程 Linux笔记——SVN命令总结 rpm&yum包管理命令总结 The end wklken

    linux 进程控制阅读笔记

    本阅读笔记将深入探讨Linux进程的创建、进程控制以及守护进程的实现。 首先,让我们了解一下Linux中的进程创建。在Linux中,最常用的创建新进程的方式是使用`fork()`系统调用。`fork()`会创建一个与父进程几乎完全...

    兄弟连linux教程1-16章学习笔记(全)

    《兄弟连Linux教程1-16章学习笔记》是一份全面涵盖Linux基础知识的学习资料,特别适合初学者和希望快速提升Linux技能的人群。这个压缩包包含了一系列文本文件和辅助图片,旨在帮助用户深入理解Linux系统的基本操作和...

    马哥的linux学习笔记

    《马哥的Linux学习笔记》是一份针对初学者和进阶者精心编撰的Linux教程,旨在帮助读者全面掌握Linux操作系统的核心概念、命令行操作以及系统管理技能。这份笔记以清晰明了的语言和实例解析了Linux系统的各个方面,是...

    Linux学习笔记——入门资料

    Linux学习笔记——入门资料 Linux,作为一款开源、免费的操作系统,因其稳定性和安全性而备受开发者和系统管理员的青睐。这份“Linux学习笔记”旨在帮助初学者快速掌握Linux的基础知识和操作技能,从而轻松入门。 ...

    linux学习笔记,linux命令整理

    在Linux操作系统的学习中,掌握命令行的使用是至关重要的。...总的来说,Linux学习笔记是一个全面了解和掌握Linux系统操作的基础教程,涵盖了从基本命令到高级管理的诸多方面,对提升Linux技能有很大帮助。

    linux学习基础笔记

    这份“Linux学习笔记.doc”文档可能会详细讲解以上各点,对于初学者来说是一份非常实用的学习资料。通过深入学习,你可以掌握Linux操作系统的核心技能,为后续的系统管理、开发或运维工作打下坚实基础。

    linux课堂学习笔记

    Linux课堂学习笔记主要涵盖了Linux操作系统、ARM架构以及设备驱动程序等相关知识。这是一份非常有价值的教育资源,适合对嵌入式系统、Linux内核以及硬件驱动感兴趣的学员深入学习。 首先,让我们来了解一下Linux...

    Linux学习笔记PDF文档.pdf

    Linux学习笔记PDF文档.pdf是一个详尽的资源,旨在帮助初学者和有经验的用户深入理解Linux操作系统。Linux作为开源的类Unix系统,以其稳定、安全和可定制性在全球范围内广泛应用于服务器、桌面环境以及嵌入式设备。这...

    linux下C语言开发笔记整理

    Linux下C语言开发笔记整理涵盖了从基础知识到网络通信的多个方面,主要围绕在Unix/Linux系统环境下使用C语言进行软件开发的各项技术与理论。以下是从文件提供的信息中提炼的知识点。 ### Unix/Linux系统基本命令和...

    linux 和ajango学习笔记

    Linux 和 Django 学习笔记 Linux 是一个开源的操作系统,它的核心组件是 Linux 内核。Linux 内核是 Linux 操作系统的核心部分,它负责管理计算机的硬件资源,提供了一个平台 для运行应用程序。 Linux 内核的开发...

    Linux与Ubuntu学习笔记

    总的来说,Linux与Ubuntu学习笔记涵盖了操作系统的基础知识、日常使用技巧、系统管理以及进阶开发和运维技能。通过学习,你可以从一个新手逐步成长为能够熟练驾驭Linux系统的专业人士,无论是在开发、测试还是运维...

    linux学习笔记分享 (Linux入门绝佳)

    Linux 学习笔记分享 (Linux 入门绝佳) Linux 是一个开源的操作系统,它的目录架构是非常重要的概念。在 Linux 中,目录架构是按照 FHS(Filesystem Hierarchy Standard)标准来组织的。下面是 Linux 中一些重要的...

    Linux基础知识学习笔记(markdown格式)

    Linux基础知识学习笔记(markdown格式) 包括:基础指令、yum、日期、时区、固定IP、ping、wget、vurl、nmap、nestat、进程管理、主机状态监控、磁盘信息监控、网络状态监控、上传、下载、用户、权限、解压、压缩、...

Global site tag (gtag.js) - Google Analytics