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
- 浏览: 260518 次
- 性别:
- 来自: 济南
文章分类
- 全部博客 (303)
- c (31)
- c++ (16)
- java (18)
- c# (1)
- python (3)
- java web (6)
- oracle (7)
- sqlserver (2)
- mysql (2)
- android (24)
- android系统 (15)
- android多媒体部分 (15)
- android游戏 (12)
- linux (26)
- javaScript (1)
- ajax (1)
- node JS (2)
- html (5)
- apache (3)
- jboss (1)
- weblogic (0)
- 通信协议 (10)
- 云计算 (1)
- 分布式 (5)
- ejb (1)
- webservice (5)
- 设计模式 (16)
- JNI (6)
- swing (13)
- 版本控制 (1)
- UML (1)
- xml (4)
- spring (5)
- hibernate (5)
- struts1 (3)
- struts2 (4)
- ibatis (0)
- tomcat (2)
- 心得体会 (1)
- css (1)
- 嵌入式 (41)
- arm体系结构 (10)
发表评论
-
u-boot Makefile 文件分析
2013-06-01 21:44 2420Makefile文件分析 # #(C)Copyri ... -
uboot start.S文件分析
2013-06-03 22:18 1319U-boot第一个开始文件arch\arm\cpu\arm1 ... -
u-boot mkconfig文件分析
2013-05-31 21:29 1132Mkconfig文件分析 #!/bin/ ... -
链接地址学习笔记
2013-05-05 12:40 1279链接地址 启动过程 示例代码如下: ... -
DDR学习笔记
2013-05-11 14:19 1036DDR 15条地址线32k 128M*2(20)=2(2 ... -
nand flash学习笔记一
2013-05-13 21:05 958Nandflash 原理图上有data0-data7 ... -
openJTAG学习笔记一
2013-05-22 21:45 2171安装软件 光盘Windows\install目录下的 01.O ... -
字符设备驱动程序学习笔记一
2013-04-01 21:55 876linux 驱动程序 字符设备驱动程序 网络接口驱动程序 块设 ... -
字符设备驱动程序学习笔记二
2013-04-04 10:29 751字符驱动程序 1 设备号 字符设备通过字符设备文件来存取 ls ... -
字符设备驱动程序学习笔记三
2013-04-04 14:03 778memdev.h文件示例代码如下: #ifndef _MEM ... -
字符设备驱动程序学习笔记四
2013-04-05 11:12 581竟争与互斥 程序调试 1 ... -
GPIO学习笔记
2013-04-14 19:50 809用汇编点亮一个led 1看原理图GPK4=0,led亮G ... -
系统时钟学习笔记
2013-05-04 21:59 83312m晶振----->pll------>cpu ... -
UART学习笔记
2013-05-04 22:00 1149串口(UART) DIV_VAL=(PCLK/(bpsx1 ... -
linux内存管理学习笔记
2013-03-12 20:50 10631 linux内存管理 地址类型 物理地址 出现在cpu地址 ... -
嵌入式linux系统学习笔记
2013-03-06 21:39 962嵌入式linux内核制作 1 清除原有配置文件与中间文件 x8 ... -
原理图学习笔记一
2013-02-17 22:24 397画个草图也挺过瘾 -
进程间通信学习笔记一(管道通信)
2013-02-01 20:08 1406进程间通信(ipc) 应用场景: 数据传输 资源共享 通知事件 ... -
进程间通信学习笔记二(信号通信)
2013-02-16 21:39 783信号通信 用户按某些键时,产生信号 硬件异常产生信号 进程用k ... -
进程间通信学习笔记三(共享内存通信)
2013-02-16 21:40 602共享内存通信 被多个进程共享的一部分物理内存,是进程间共享数据 ...
相关推荐
这份"Linux学习笔记(强悍总结值得一看)"是Linux初学者的宝贵资源,也适合有经验的用户作为参考手册。以下是对笔记内容的详细概述: 1. **Linux常用命令**: Linux命令行是其强大的工具,掌握常用命令是Linux学习...
### Linux进程与线程创建详解 #### 进程与线程的概念 在深入探讨Linux下C语言编程中进程和线程的创建之前,我们先来理解一下进程与线程的基本概念。 - **进程**:是操作系统进行资源分配和调度的基本单位,每个...
【linux学习笔记-10】Linux进程相关系统调用(三).doc 【linux学习笔记-11】守护进程daemon.doc 【linux学习笔记-12】守护进程的日志实现.doc 【linux学习笔记-13】基本进程通信--文件锁.doc 【linux学习笔记-14】...
【linux学习笔记-8】Linux进程相关系统调用(1) 【linux学习笔记-9】Linux进程相关系统调用(2) 【linux学习笔记-10】Linux进程相关系统调用(3) 【linux学习笔记-11】守护进程daemon 【linux学习笔记-12】守护...
2,一直以来学习LINUXkernel的知识缺乏系统化,借对这本书的学习,系统化 的学习一下LINUXkernel。 3,自己一直在做一个toosmall,toosimple的单进程,特权模式,64bit保 护模式的称不上OS的OS,已经做完了...
学习linux的笔记,发到博文了,鉴于文档方便保存和查阅,发出来,免费的哈 ...文档列表: Linux笔记——vim常用操作及扩展补充...Linux笔记——linux进程 Linux笔记——SVN命令总结 rpm&yum包管理命令总结 The end wklken
本阅读笔记将深入探讨Linux进程的创建、进程控制以及守护进程的实现。 首先,让我们了解一下Linux中的进程创建。在Linux中,最常用的创建新进程的方式是使用`fork()`系统调用。`fork()`会创建一个与父进程几乎完全...
《兄弟连Linux教程1-16章学习笔记》是一份全面涵盖Linux基础知识的学习资料,特别适合初学者和希望快速提升Linux技能的人群。这个压缩包包含了一系列文本文件和辅助图片,旨在帮助用户深入理解Linux系统的基本操作和...
《马哥的Linux学习笔记》是一份针对初学者和进阶者精心编撰的Linux教程,旨在帮助读者全面掌握Linux操作系统的核心概念、命令行操作以及系统管理技能。这份笔记以清晰明了的语言和实例解析了Linux系统的各个方面,是...
Linux学习笔记——入门资料 Linux,作为一款开源、免费的操作系统,因其稳定性和安全性而备受开发者和系统管理员的青睐。这份“Linux学习笔记”旨在帮助初学者快速掌握Linux的基础知识和操作技能,从而轻松入门。 ...
在Linux操作系统的学习中,掌握命令行的使用是至关重要的。...总的来说,Linux学习笔记是一个全面了解和掌握Linux系统操作的基础教程,涵盖了从基本命令到高级管理的诸多方面,对提升Linux技能有很大帮助。
这份“Linux学习笔记.doc”文档可能会详细讲解以上各点,对于初学者来说是一份非常实用的学习资料。通过深入学习,你可以掌握Linux操作系统的核心技能,为后续的系统管理、开发或运维工作打下坚实基础。
Linux课堂学习笔记主要涵盖了Linux操作系统、ARM架构以及设备驱动程序等相关知识。这是一份非常有价值的教育资源,适合对嵌入式系统、Linux内核以及硬件驱动感兴趣的学员深入学习。 首先,让我们来了解一下Linux...
Linux学习笔记PDF文档.pdf是一个详尽的资源,旨在帮助初学者和有经验的用户深入理解Linux操作系统。Linux作为开源的类Unix系统,以其稳定、安全和可定制性在全球范围内广泛应用于服务器、桌面环境以及嵌入式设备。这...
Linux下C语言开发笔记整理涵盖了从基础知识到网络通信的多个方面,主要围绕在Unix/Linux系统环境下使用C语言进行软件开发的各项技术与理论。以下是从文件提供的信息中提炼的知识点。 ### Unix/Linux系统基本命令和...
Linux 和 Django 学习笔记 Linux 是一个开源的操作系统,它的核心组件是 Linux 内核。Linux 内核是 Linux 操作系统的核心部分,它负责管理计算机的硬件资源,提供了一个平台 для运行应用程序。 Linux 内核的开发...
总的来说,Linux与Ubuntu学习笔记涵盖了操作系统的基础知识、日常使用技巧、系统管理以及进阶开发和运维技能。通过学习,你可以从一个新手逐步成长为能够熟练驾驭Linux系统的专业人士,无论是在开发、测试还是运维...
Linux 学习笔记分享 (Linux 入门绝佳) Linux 是一个开源的操作系统,它的目录架构是非常重要的概念。在 Linux 中,目录架构是按照 FHS(Filesystem Hierarchy Standard)标准来组织的。下面是 Linux 中一些重要的...
Linux基础知识学习笔记(markdown格式) 包括:基础指令、yum、日期、时区、固定IP、ping、wget、vurl、nmap、nestat、进程管理、主机状态监控、磁盘信息监控、网络状态监控、上传、下载、用户、权限、解压、压缩、...