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

2020-05-21 00:00:00 函数 队列 调用 系统 进程


[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

接下来的两节:
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。

文章来源CU社区:Linux 2.4 内核说明文档(进程与中断管理篇)

相关文章