免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: xuediao
打印 上一主题 下一主题

Linux 2.4 内核说明文档(进程与中断管理篇) [复制链接]

论坛徽章:
0
11 [报告]
发表于 2005-07-16 19:18 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

2.6.        内核时钟

现在我们来看看内核时钟。内核时钟常用来为细节函数(时钟处理函数)分派执行时间。主数据结构timer_list定义在include/linux/timer.h文件中:

struct timer_list {
struct list_head list;
unsigned long expires;
unsigned long data;
void (*function)(unsigned long);
volatile int running;
};

list字段用于链接内部链表,操作时由timerlist_lock同步锁保护。expires字段表示了处理函数在以data数据为参数被调用的时间数值。running字段用于SMP机上标识时钟处理函数当前是否运行在其他CPU上。
函数add_timer()和del_timer()用于从链表中增加和删除一个时钟。当时钟终止时,它自动被移出。在时钟被使用之前,它必须被init_timer函数初始化;在其被添加到链表之前,function字段和expires字段必须被设置。

论坛徽章:
0
12 [报告]
发表于 2005-07-21 17:58 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

不好意思,最近太忙了。现在补上!

论坛徽章:
0
13 [报告]
发表于 2005-07-21 18:01 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

2.7.        下半部机制

bottom half(又名下半部机制)是Linux提高系统中断响应和处理能力的有效机制。 发生中断时,处理器要停止当前正在执行的指令,而操作系统负责将中断发送到对应的设备驱动程序去处理。在中断的处理过程中,系统不能进行其他任何工作,因此,在这段时间内,设备驱动程序要以最快的速度完成中断处理,而其他大部分工作在中断处理过程之外进行。Linux 内核利用bottom half处理过程帮助实现中断的快速处理。在中断有效的情况下,有时需要将一个任务分割成小份,以便中断可以获取到资源及时处理,而任务则稍后再继续(例如,对数据进行后加工,唤醒等待该数据的进程等)。
Bottom halves是用于延缓内核任务执行的旧有机制,在linux 1.x中已经实现了。在2.0内核,提供了一个新机制,称为“任务队列”。
Bottom halves由global_bh_lock旋转锁控制,即是在任何CPU上每刻仅有一个bottom half工作。无论如何,当尝试执行处理程序时,如果global_bh_lock无效,则bottom half被标识为执行态,进程也就得以继续。
Linux中总共仅能注册32个bottom half,操作bottom half的函数如下:
        void init_bh(int nr, void (*routine)(void)):为运行状态宏注册bottom half处理函数。这些状态宏以XXXX_BH格式定义在include/linux/interrupt.h 文件中,例如TIMER_BH或者TQUEUE_BH。通常子系统初始化运行环境时就会调用此函数注册bottom half。
        void remove_bh(int nr):执行init_bh()相反的操作,删除指定宏。这里没有错误校验过程,因此可能会影响系统。通常子系统清除运行环境时就会调用这个函数。
        void mark_bh(int nr):将bottom half 标识为执行态。通常,一个中断处理函数就会这样做。
Bottom halves是一些全局的排斥锁,所以问题“什么时候bottom half处理函数会执行”就相当于“什么时候排斥锁会执行”。这个答案就是:a)在每一个schedule里面,b)在每一个中断或者系统调用返回时。

论坛徽章:
0
14 [报告]
发表于 2005-08-03 12:00 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

不好意思,最近忙着饭碗的事情,帖子中断了许久,给大家致歉!

这两周争取把这个章节全部贴上去
SirFang 该用户已被删除
15 [报告]
发表于 2005-08-03 13:13 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
16 [报告]
发表于 2005-08-03 13:40 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

[quote]原帖由 "SirFang"]我怎么记得linux下进程数是有限制的,不仅仅要足够的内存分配task_struct结构,还关系到GDT表,Linux不使用LDT,所以,GDT中的entry个数也就决定了进程总数。其中第0项不用,1~4项被指定为Kernel CS/DS, User active ..........[/quote 发表:


老兄说得没错,不过应该具体是这样的:

在Linux 2.2.x中,一些与进程管理相关的数据结构是在系统初始化的时候被初始化的。其中最重要的是gdt和进程表task。Gdt的初始化主要是确定需要为多少个进程保留空间,也就是需要多大的gdt。这是由一个宏定义NR_TASKS决定的。NR_TASKS的值就是系统的最大进程数,它是在编译的时候被确定的,gdt的大小是10+2+NR_TASKS*2。而全局描述符表寄存器gdtr长度域为16位,每项描述符为8字节,故可容纳的   最大描述符数 = 1《(16-3)=1《13 = 8192 个。

而在2.4以上,现在的2.6版本中,这个进程数事实上是可以更多的。不过大多数系统的默认依然是8191
SirFang 该用户已被删除
17 [报告]
发表于 2005-08-03 18:15 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
18 [报告]
发表于 2005-08-04 13:27 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

SirFang 兄对内部结构也是研究得比较深入啊,如果我这些咚咚里面还有什么地方需要修正,修改的,请你不要顾虑,随时提出来。大伙共同学习共同进步哈

再次感谢SirFang兄

论坛徽章:
0
19 [报告]
发表于 2005-08-13 22:48 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

接下来的两节:
2.8 任务队列
2.9 I386体系中系统调用实现

2.8.        任务队列
任务队列可以看作是以前的下半部机制的动态扩展。在源代码里面,有时以新下半部机制来称呼他们。以前的下半部机制有一下的限制:
1)        他们仅有一个固定的数目;
2)        每个下半部仅仅能够关联一个处理函数;
3)        下半部可以被旋转锁结束,所以他们不能阻塞;
所以,对于任务队列,任意数目的函数可以被关联并前后连续的处理。通过DECLARE_TASK_QUEUE宏可以创建一个新的任务队列,采用queue_task函数可以向其中增加一个任务而调用run_task_queue函数则会执行一个任务队列。作为创建你自己的任务队列,你可以使用Linux系统提前定义的任务队列,如下所述:
1)        tq_timer:定时器任务队列,在每个定时器中断或者释放tty设备时执行。当定时器处理函数在中断环境下运行时,tq_timer还运行于中断环境,并不能阻塞。
2)        tq_scheduler:调度任务队列,由调度程序触发,同样关闭tty设备时也会运行。一旦调度程序运行于进程重设定的上下文后,tq_scheduler任务可以做任何想作的事情,比如阻塞,处理上下文数据等等。
3)        tq_disk:用于底层阻塞设备启动真实的请求。这个任务队列为模块设计,除了设计本身的目的以外不能作为他用。
如果一个驱动使用它自身的任务队列,它就不需要调用run_tasks_queues函数去处理这个队列,除非是以下说明的情况。
tq_timer/tq_scheduler 任务队列不仅普通场合会被触发,而且其他场合(如关闭tty设备时)也会触发。如果我们记得驱动能够调度队列中的任务,并且这些任务仅仅在驱动的细节实例有效时做判断,,其原因非常简单的。这通常意味着应用关闭它。所以,驱动可能需要调用run_task_queue来激活它添加到队列中的任务,因为允许这些任务稍后执行是没有意义的,也就是说相关的数据结构可能被另一个实例释放或者重用。这就是run_task_queue在tq_timer 和tq_scheduler中多处使用而不是定时器中断和schedule分别调用。
2.9.        I386体系中系统调用实现
linux实现系统调用有两种机制:
        lcall7/lcall27调用方式;
        0x80号软中断;
Linux附带的程序使用0x80方式,同时外来程序如UNIX (Solaris, UnixWare 7等)使用lcall7机制。由于历史原因,lcall7机制包含了lcall27机制,但是处理函数却命名为lcall7_func,所以这是令人误解的。
当系统启动时,arch/i386/kernel/traps.c:trap_init函数被调用,设置IDT,这样向量0x80就指向了arch/i386/kernel/entry.S文件中描述的系统调用向量表的地址。
当一个用户空间的应用触发系统调用时,参数由寄存器传递,并且应用执行'int 0x80'指令。这个指令进入内核模式,然后处理器跳转到system_cal入口。具体如下:
1)        保存寄存器;
2)        为KERNEL_DS设置%ds 和 %es,这样所有相关数据(和额外的段)就在内核地址空间被建立。
3)        如果%eax的值大于NR_syscalls (当前是 256),返回ENOSYS错误。
4)        如果任务满足tsk.>;ptrace & PF_TRACESYS 条件,则执行专门处理。这用于支持strace 或者debugger这样的程序。
5)        调用sys_call_table+4*(syscall_number from %eax)。这个表在同一个文件arch/i386/kernel/entry.S中初始化,指向各自的系统调用处理函数,在linux下这些函数以sys_开头。这些C系统调用处理函数在堆栈中获取他们的参数。
6)        进入系统调用(system call return path)。这个部分不仅被0x80使用,而且被lcall7, lcall27使用。它处理任务tasklets,检查是否需要调用schedule,检查是否有信号待处理,如果是,则处理这些信号。
Linux系统为系统调用提供6个参数支持。他们分别寄存于%ebx, %ecx, %edx, %esi, %edi,%ebp。系统调用号存储到%eax。

论坛徽章:
0
20 [报告]
发表于 2005-08-14 21:40 |只看该作者

Linux 2.4 内核说明文档(进程与中断管理篇)

再有一节:
2.10.        原子操作

2.10.        原子操作
目前有两种类型的原子操作:bitmaps 和 atomic_t。对于维护“已分配”或者“释放”单位(这些单位来自于某些大型的以数字标识每个单位的集合)的概念,bitmaps非常便利。释放inodes和块就是例子。这也在某些简单锁中有广泛的应用,例如提供打开设备的互斥访问。这样的例子可以在arch/i386/kernel/microcode.c中被找到。
/*
* Bits in microcode_status. (31 bits of room for future expansion)
*/
#define MICROCODE_IS_OPEN 0 /* set if device is in use */
static unsigned long microcode_status;
Linux下没有必要像清零BSS一样初始化microcode_status。
/*
* We enforce only one user at a time here with open/close.
*/
static int microcode_open(struct inode *inode, struct file *file)
{
if (!capable(CAP_SYS_RAWIO))
return .EPERM;

/* one at a time, please */
if (test_and_set_bit(MICROCODE_IS_OPEN, &microcode_status))
return .EBUSY;

MOD_INC_USE_COUNT;
return 0;
}
关于bitmaps的操作有:
        void set_bit(int nr, volatile void *addr):在由地址addr指向的bitmap中添加位nr。
        void clear_bit(int nr, volatile void *addr):在由地址addr指向的bitmap中清除位nr。
        void change_bit(int nr, volatile void *addr):在由地址addr指向的bitmap中绑定位nr。
        int test_and_set_bit(int nr, volatile void *addr):设置位nr并返回以前的值。
        int test_and_clear_bit(int nr, volatile void *addr):清除位nr并返回以前的值。
        int test_and_change_bit(int nr, volatile void *addr):绑定位nr并返回以前的值。
这些操作都使用了LOCK_PREFIX宏,该宏在SMP内核上求总线锁指令前缀的值,在UP上不做任何实际操作。这保证了SMP环境下的访问原子数。
有时位操作不是便利的,作为替代我们采用算法操作,增加、减小、自增、自减。典型的案例就是引用计数。它由atomic_t数据类型和一下步骤实现:
        atomic_read(&v):返回atomic_t变量v的值;
        atomic_set(&v, i):将atomic_t变量v的值设置为整型i;
        void atomic_add(int i, volatile atomic_t *v):将atomic_t变量的值增加i;
        void atomic_sub(int i, volatile atomic_t *v):将atomic_t变量减少i;
        int atomic_sub_and_test(int i, volatile atomic_t *v):将atomic_t变量减少i,如果新的值是0则返回1,否则返回0;
        void atomic_inc(volatile atomic_t *v):atomic_t变量增加1;
        void atomic_dec(volatile atomic_t *v):atomic_t变量减少1;
        int atomic_dec_and_test(volatile atomic_t *v):atomic_t变量减少1,如果新的值是0则返回1,否则返回0;
        int atomic_inc_and_test(volatile atomic_t *v):atomic_t变量增加1,如果新的值是0则返回1,否则返回0;
int atomic_add_negative(int i, volatile atomic_t *v):atomic_t变量增加i,如果结果是负数,返回1;否则返回0。这个操作用于实现semaphores。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP