LINUX系统调用原理-既应用层如何调用内核层函数之软件中断
SWI:software interrupt 软件中断
ARM Linux系统利用SWI指令来从用户空间进入内核空间,还是先让我们了解下这个SWI指令吧。SWI指令用于产生软件中断,从而实现从用户模式变换到管理模式,CPSR保存到管理模式的SPSR,执行转移到SWI向量。在其他模式下也可使用SWI指令,处理器同样地切换到管理模式
内核在执行系统调用的时候处于进程上下文,可以休眠,也可以被抢占,所以必须保证系统调用是可重入的。SWI软件中断也具上面特性,这时从硬件上确保了操作系统多线程多进程的正常运作,而不能因为执行一个SWI指令导致其它进程或线程流停下来了。而在系统调用的实现方式上SWI办演的角色是:应用层一个进程或线程调用用系统调用进入内核层(SWI陷入),这个时候CPU进入管理模式一个调度周期,同时其它进程或线程还是正常的调度的。如果这个时候内核层由于资源原因导致调用此系统调用的进程或线程(内核层只有进程概念,应用层有进程和线程概念)休眠或阻塞,在这个调度周期时间完之后会保存当前休眠或阻塞状态。当前资源满足后休眠或阻塞结束,等下个调度周斯,当时阻塞或休眠进程继续运行,根据运行结果从SWI陷入的管理模式软中断返回一个结果给调用此系统调用的应用线程或进程,此时应用层线程或进程继续运行根据返回结果。(应用层陷入内核说明在内核解度是同一个进程号,对于这个SWI调用事件的流程,(线程对于内核解度应该也是进程 ,不是清楚这儿。))。注意:多个系统用 多个SWI 多个线程 都是同时由OS调度策略在多CPU上同时和时间片运行的。不会因一个SWI就打乱步伐。这个解度说明SWI只是应用层陷入内核的一个手段。它可以被中断 和休眠 和阻塞
LINUX系统分两种模式:用户模式(USER模式) 和内核模式(管理模式) 这两种模式ARMCPU运行硬件上的不同模式。通用它调度器和定时器中断让进程和线程在这两种模式自己动动态运行。
SWI命令格式:SWI{cond} immed_24
Swi参数传递有两种格式:
1)、指令中24位的立即数指定了用户请求的服务类型,参数通过通用寄存器传递。如:
MOV R0,#34
SWI 12
2)、指令中的24位立即数被忽略,用户请求的服务类型有寄存器R0的只决定,参数通过其他的通用寄存器传递。如:
MOV R0, #12
MOV R1, #34
SWI 0
下面是看别人博客对LINUX系统一些经典总结:
计算机工作的三大法宝(存储程序计算机、函数调用堆栈、中断)、计算机工作的两把宝剑(中断上下文和进程上下文)
asmlinkage大都用在系统调用中。有一些情况下是需要明确的告诉编译器,我们是使用stack来传递参数的,比如X86中的系统调用,是先将参数压入stack以后调用sys_*函数的,所以所有的sys_*函数都有asmlinkage来告诉编译器不要使用寄存器来编译,
linux内核中所有的系统调用函数都用sys_开头
函数定义中的FASTCALL宏,用于通知编译器,使用寄存器来传递参数。
如果上面两个宏都没有,则使用默认传参规则,前4个参数通过R0~R3寄存器传递,其余更多的参数通过栈传递。
系统调用必须仔细检查传入参数的有效性,尤其是用户提供的指针,必须确保:
*指针指向的内存区域属于用户空间,进程不能哄骗内核去读内核空间的数据。
*指针指向的内存区域属于进程的地址空间,不能哄骗内核去读其他进程的数据。
*进程不能绕过内存访问权限。
内核在执行系统调用的时候处于进程上下文,可以休眠,也可以被抢占,所以必须保证系统调用是可重入的。
内核在执行系统调用的时候处于进程上下文。current指针指向当前任务,即引发系统调用的那个进程。在进程上下文中,内核可以休眠并且可以被抢占。这表明即使是在内核空间中,当前进程也可以被其他进程抢占。因为新的进程可以执行相同的系统调用,所以必须保证系统调用是可重入的。当系统调用返回时,控制权仍然在system_call()中,它最终会负责切换到用户空间并让用户继续执行下去。
进程状态:
task_struct中的state描述进程的当前状态。进程的状态一共有5种,而进程必然处于其中一种状态:
1)TASK_RUNNING(运行)——进程是可执行的,它或者正在执行,或者在运行队列中等待执行。这是进程在用户空间中执行唯一可能的状态;也可以应用到内核空间中正在执行的进程。
2)TASK_INTERRUPTIBLE(可中断)——进程正在睡眠(也就是说它被阻塞)等待某些条件的达成。一旦这些条件达成,内核就会把进程状态设置为运行,处于此状态的进程也会因为接收到信号而提前被唤醒并投入运行。
3)TASK_UNINTERRUPTIBLE(不可中断)——除了不会因为接收到信号而被唤醒从而投入运行外,这个状态与可打断状态相同。这个状态通常在进程必须在等待时不受干扰或等待事件很快就会发生时出现。由于处于此状态的任务对信号不作响应,所以较之可中断状态,使用得较少。
4)TASK_ZOMBIE(僵死)——该进程已经结束了,但是其父进程还没有调用wait4()系统调用。为了父进程能够获知它的消息,子进程的进程描述符仍然被保留着。一旦父进程调用了wait4(),进程描述符就会被释放。
TASK_STOPPED(停止)——进程停止执行,进程没有投入运行也不能投入运行。通常这种状态发生在接收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态。
需要调整进程的状态,最好使用set_task_state(task, state)函数,在必要的时候,它会设置内存屏障来强制其他处理器作重新排序(SMP)。
在Linux系统中,所有的进程都是PID为1的init进程的后代
进程调度:
1.什么是调度
现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程)。这个管理程序就是调度程序。
它的功能说起来很简单:决定哪些进程运行,哪些进程等待决定每个进程运行多长时间此外,为了获得更好的用户体验,运行中的进程还可以立即被其他更紧急的进程打断。
总之,调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用CPU(即尽量少的切换进程,进程切换过多,CPU的时间会浪费在切换上);另一方面,保证各个进程能公平的使用CPU(即防止一个进程长时间独占CPU的情况)。
2.调度实现原理
2.1.关于进程的优先级进程的优先级有2种度量方法
一种是nice值,nice值的范围是-20~+19,值越大优先级越低,也就是说nice值为-20的进程优先级最大。
一种是实时优先级,实时优先级的范围是0~99,与nice值的定义相反,实时优先级是值越大优先级越高。实时进程都是一些对响应时间要求比较高的进程,因此系统中有实时优先级高的进程处于运行队列的话,它们会抢占一般的进程的运行时间。
2.2.关于时间片
有了优先级,可以决定谁先运行了。但是对于调度程序来说,并不是运行一次就结束了,还必须知道间隔多久进行下次调度。于是就有了时间片的概念。时间片是一个数值,表示一个进程被抢占前能持续运行的时间。也可以认为是进程在下次调度发生前运行的时间(除非进程主动放弃CPU,或者有实时进程来抢占CPU)。时间片的大小设置并不简单,设大了,系统响应变慢(调度周期长);设小了,进程频繁切换带来的处理器消耗。默认的时间片一般是10ms