进程管理

linux系统的实现非常特别:他对线程和进程并不特别区分。对linux而言,线程只不过是一种特殊的进程罢了。

现代操作系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存。虽然实际上可能是许多进程正在访问一个处理器,但虚拟处理器给进程一种假象,让这些进程觉得自己在独享处理器。有趣的是,线程之间可以共享虚拟内存,但是每个都拥有各自的虚拟处理器。

在linux中,通常是调用fork()系统的结果,该系统通过复制一个现有进程来创建一个全新的进程。调用fork()的进程被称为父进程,新产生的进程称为子进程。在调用结束时,在返回点这个相同位置上,父进程恢复执行,子进程开始执行。fork()系统调用从内核返回两次:一次回到父进程,一次回到新产生的子进程。

在创建新的进程都是为了立即执行新的,不同的程序,而接着调用exec()这组函数就可以创建新的地址空间,并把新的程序载入其中。(exec函数就是提供一个在进程中启动另一个程序执行的方法)

最终,程序通过新的进程exit()系统调用退出执行。这个函数会终结今后才能并将其占用的资源释放掉。进程退出执行后被设置成僵死状态,知道他的父进程调用wait()或waitpid()为止。

fork()函数讲解

fork()函数通过系统调用一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但是如果出事参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的进程中,只有少数值与原来的进程的值不同,相当于克隆一个自己。

执行之后,有一个返回值,返回的就是一个id。fork调用的一个奇妙之处就是他仅仅被调用一次,却能够返回两次,他可能有三种不同的返回值:

1.在父进程中,fork返回新创建子进程的进程id

2.在子进程中,fork返回0

3.如果出现错误,fork返回一个负值

这两个进程志雄没有固定的先后顺序,那个进程先执行要看系统的进程调度策略。

进程描述符及任务结构

内核把进程的列表存放在任务队列的双向循环链表中。链表中的每一项都是一个task_struct,称为进程描述符的结构。

分配进程描述符

linux通过slab分配task_struct结构。各进程的task_struct存放在他们内核栈的尾端。由于现在用slab分配器动态生成task_struct,所以只需在栈底(对于向下增长的栈来说)或栈顶(向上增长的栈)创建一个新的结构struct

thread_info。每个任务的thread_info结构在他的内核栈的尾端分配。结构中task域中存放的是指向该向任务市级task_struct的指针。

进程描述符的存放

内核通过唯一的进程标识符pid来识别每个进程。为了与老板linux和unix兼容,最大值为32768。这个值越小,转一圈就越快,本来数值大的进程比数值小的进程迟运行,但这样一来就破坏了这个原则。在内核中,访问任务通常需要获取指向其task_struct的指针。实际上,内核中大部分处理进程的代码都是直接通过task_struct进行的。因此,通过current宏查询找到当前正在运行进程的进程描述符的速度就显得尤为重要。

硬件体系结构不同,该宏的实现也不同,他必须针对专门的硬件体系结构做处理。有的硬件体系结构可以拿出一个专门寄存器来存放指向当前进程task_struct的指针,用于加快访问速度。而有些像x86这样的体系结构(其寄存器并不富余),就只能在内核栈的尾端创建thread_info结构,通过计算偏移间接地查找task_struct结构。

进程上下文

可执行代码段是进程的重要组成部分。这些代码从一个可执行文件载入到进程的地址空间执行。一般程序在用户空间执行。当一个程序执行了系统调用或触发了某个异常,他就陷入了内核空间。此时,我们称内核“代表进程执行“并处于进程上下文中。(在中断上下文中,系统不代表进程执行,而是执行一个中断处理程序。不会有进程去干扰这些中断处理程序,所以此时不存在进程上下文)

进程家族树

Unix系统的进程之间存在一个明显的继承关系,在Linux系统中也是如此。所有的进程都是PID为1的init进程的后代。内核在系统启动的最后阶段启动init进程。该进程读取系统的初始化脚本并执行其他的相关程序,最终完成系统启动的整个过程。

系统中的每个进程必须有一个父进程,相应的,每个进程也可以拥有0个或多个子进程,拥有同一个父进程的所有进程被称为兄弟,进程间的关系存放在进程描述符中。每个task_struct都包含一个指向其父进程task_struct,叫做parent的指针。还包括一个称为children的子进程链表。

进程创建

Unix的进程创建很特别。许多其他的操作系统都提供了产生进程的机制。首先在新的地址空间创建进程,读入可执行文件,最后开始执行。Unix采用了与众不同的实现方式。他把上述步骤分解到两个不同的函数中:fork()和exec()一族。首先fork()拷贝当前进程创建一个子进程。exec()负责读取可执行文件并将其载入到地址空间开始运行。这两个函数组合起来的效果和其他系统使用的单一函数效果相似。

写时复制

传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单且效率低下,因为你把所有的数据都复制了一份。linux的fork()使用的写时复制页实现。写时拷贝是一种可以推迟甚至免除拷贝的技术。当多个调用者获取资源时,他们会获取相同的指针指向相同的资源。知道某个调用者视图修改内容后才试图复制一份副本给调用者,而其他调用者看到的仍是最初未改变的资源。fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符。在一般情况下,进程创建后都会马上运行一个可执行的文件,这种优化可以避免拷贝大量根本就不会被使用的数据。由于Unix强调进程快速执行的能力,所以这个有的话是很重要的。

fork()

linux通过clone()系统调用实现fork()。这个调用通过一系列的参数标志来指明父、子进程需要的共享资源。fork()、vfork()和_clone()库函数底层都根据各自需要的参数标志去调用clone(),然后又clone()调用do_fork()。

vfork()

除了不拷贝父进程的页表外,vfork()系统调用和fork()的功能相同。子进程作为父进程的一个单独线程在他的地址空间里运行,父进程阻塞,直到子进程退出或执行exec()。子进程不能像地址空间写入。(系统最好不要调用v_fork())

线程在linux中的实现

linux实现线程的机制非常独特。从内核角度来说,他并没有线程这个概念。linux把所有的线程都当做进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。

每个线程都拥有唯一隶属于自己的task_struct,所以在内核中,他看起来就像是一个普通的进程。

加入我们有一个包含四个线程的进程,在提供专门的线程支持的系统中,通常会有一个包含指向四个不同线程的指针的进程描述符。线程本身再去描述他独占的资源。相反,linux仅仅创建四个进程并分配四个普通的task_struct结构。建立四个进程时,指定他们共享的某些资源。

创建线程

创建线程和普通进程的方式类似,只不过在调用clone()时候需要传递一些参数标志来指明需要的共享的资源。父子俩共享地址空间、文件系统资源、文件描述符和信号处理程序。

内核线程

内核经常需要在后台执行一些操作。这种任务可以通过内核线程完成--独立运行在内核空间的标准进程。内核线程和普通进程间的区别是内核线程没有独立的地址空间。他只能在内核空间运行,从来不切换到用户空间去。内核进程和普通进程一样,可以被调度,也可以被抢占。

linux确实会把一些任务交给内核线程去做,想flush和ksofirqd这些任务就是明显的例子。在装有linux系统的机子上运行ps

-ef命令,你可以看到内核线程,有很多!这些线程在系统启动时由另外一些内核线程创建。内核线程也只能由其他内核线程创建。内核是通过kthreadd内核进程中衍生出新的内核线程来自动处理这一点的。新的任务是由kthread内核进程通过clone()系统调用而创建的。新的进程将运行threadfn函数,给其传递的参数为data。新创建的进程处于不可运行状态,如果不通过调用wake_up_process()明确的唤醒他,他不会主动运行。内核线程启动后就一直运行直到调用do_exit()退出,或内核的其他部分调用kthread_stop()退出。

进程终结

进程的析构是自身引起的。它发生在进程调用exit()系统调用时,即可显示的调用这个系统调用,也可能隐式的从某个程序的主函数返回。当进程接受到他及不能处理也不能忽略的异常是,他可能被动的终结,不管怎么终结的,该任务大部分还是要靠do_exit().

删除进程描述符

在调用了do_exit()之后,尽管线程已经僵死不能再运行了,但是系统还保留了他的进程描述符。前面说过,这也昂可以让系统有办法在子进程终结后仍能获取他的信息。因此,进程终结时所需要的清理工作和进程描述符的删除工作被分开执行。在父进程获取已终结的子进程的信息后,或通知内核他并不关注哪些信息后,子进程的task_struct结构才被释放。

wait()这一族函数都是通过唯一的一个系统调用wait4()来实现的,他的标准动作是挂起调用它的进程,直到其中一个子进程退出,此时函数会返回该子进程的PID。此外,调用该函数时提供的指针会包含子函数退出是的退出代码。

孤儿进程造成的进退维谷

如果父进程在子进程之前退出,必须有机制保证子进程能找到一个新的父亲,否则这些称为孤儿的进程就会在退出时永远处于僵死状态,白白地耗费内存。前面的部分已经有所暗示,对于这个问题,解决方法是给子进程在当前进程组内找一个线程作为父亲,如果不行,那就让init做他们的父进程。在do_exit()中会调用exit_notify(),该函数会调用forget_original_parent(),而后者会调用find_new_reaper()来执行寻父过程。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

linux的进程调度

从最初到2.4内核系列,linux的调度程序相当简陋,在众多可运行程序或者多处理器的环境下都难以胜任。在2.5,开始采用一种叫做O(1)调度程序的新调度程序。这里主要感谢静态时间片算法和针对每一处理器的运行队列,他帮助我们摆脱了先前调度程序设计上的限制。

策略

IO消耗型和处理器消耗型的进程

进程可以被分为IO消耗型和处理器消耗型。前者指进程的大部分时间用来提交IO请求或者是等待IO请求。因此,这样的进程经常处于可运行状态,但通常都是运行短短的一会,因为更多的时间是IO阻塞。相反,处理器消费型进程把时间大多用在执行代码上面。除非被抢占,否则他们通常都一直不停的运行。这种处理器消耗型的进程,调度策略往往是尽量降低他们的调度频率,而延长其运行时间。调度算法通常要在两个矛盾的目标中间寻找平衡:响应时间短和高吞吐量。

进程优先级

linux采取了两种不同的优先级范围。第一种是nice值,他的范围是-20~19,默认值为0.越大的nice值移位这更低的优先级--nice似乎意味着你对系统中的其他进程更“优待”。第二种范围是实时优先级,其值是可配的,默认情况下他的变化范围是0~99。与nice值相反,越高的实时优先级数值意味着进程优先级越高。任何实时进程优先级都高于普通的进程。

时间片

IO消耗型不需要长的时间片,而处理器消耗型的进程则希望越长越好(比如这样可以让他们的告诉缓存命中率更高)

调度算法

调度器类

linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性的选择调度算法。这种模块化结构被称为调度器类,他允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程。每个调度器都有一个优先级,基础调度器的代码定义在kernel/sched.c中,他会按照优先级顺序遍历调度类,拥有一个可执行进程的最高优先级调度器类胜出,去选择下面要执行的那一个程序。

公平调度(CFS)

在这种调度下,每个进程获得1/n的处理器时间,n是指进程的数量。任何进程所获得的处理器时间是由他自己和其他所有可运行进程nice值的相对差值决定的。nice值对时间片的作用不再是算数加权,而是几何加权。任何nice值相对应的绝对时间不再是一个绝对值,而是处理器的使用比。CFS称为公平调度器是因为他确保给每个进程公平的处理器使用比。正如我们知道的,CFS不是完美的公平,他只是近乎完美的多任务。但是他确实在多进程环境下,降低了调度延迟带来的不公平性。

linux调度的实现(包括四个组成部分)

1.时间记账

所有的调度器都必须对进程运行时间做记账。多数Unix系统,正如我们前面所说,分配一个时间片给每一个进程。那么当每次系统时钟街拍发生时,时间片都会被减少一个节拍周期。当一个进程的时间片被减少到0时,他就会被另一个尚未减到0的时间片可运行进程抢占。

(1)调度器实体结构(就是优先级队列,只不过他的底层是红黑树)

CFS不再有时间片的概念,但是他也必须维护每个进程运行的时间记账,因为他需要确保每个进程只在公平分配给他的处理器时间内运行。调度器实体结构作为一个se的成员变量,嵌入在进程描述符struct

task_struct内。

(2)虚拟实时

vruntime变量存放进程的虚拟时间,该运行时间(花在运行上的时间和)的计算是经过了所有可运行进程总数的标准化。虚拟时间是以ns为单位的,所以vruntime和定时器节拍不在相关。虚拟运行时间可以帮助我们逼近CFS模型所追求的“理想多任务处理器”。如果我们真有这样一个理想的处理器,那么我们就不在需要vruntime了,因为优先级相同的所有进程的虚拟运行时都是相同的--所有任务都将接受到相等的处理器份额。但是因为处理器无法实现完美的多任务,他必须一次运行每个任务。因此CFS使用vruntime变量来记录一个程序到底运行了多长时间以及他应该在运行多久。

2.进程选择

CFS会试图利用一个简单的规则去均衡进程的虚拟运行时间:当CFS需要选择下一个运行进程时,他会挑一个具有最小vruntime的进程。这其实就是CFS调度算法的核心:选择具有最小vruntime的任务。(CFS使用红黑树来组织可运行进程队列,并利用其迅速找到最小vruntime值的进程)

1.挑选下一个进程

红黑树中最左下角的那个进程即为最小vruntime进程。

2.向树中加入进程

就是红黑树的插入过程。只不过有增加了一个缓存变量,如果插入的过程一直是向左的,就保持该变量为1,如果有一个向右,则把其改为0.(增加进程发生在进程变为可运行状态或是通过fork()调用第一次创建进程的时候)

3.从树中删除进程

就是红黑树的删除过程。还要判断是否删除为最左节点,如果是,则需要把缓存改为下一个节点。发生在进程阻塞的时候。

3.调度器入口内核

进程调度的主要入口点是函数schedule(),他正是内核其他部分用于调用进程调度器的入口:选择哪个进程可以运行,何时将其投入运行。Schedule()函数通常需要和一个具体的调度器类相关联,也就是说他会找到一个最高优先级的调度器类--后者需要有自己的可运行队列,然后问后者谁才是写一个该运行的进程。

4.睡眠和唤醒

进程把自己标记成休眠状态,从可执行红黑树中移除,放入等待队列中.然后调用schedule()选择和执行一个其他进程.唤醒的过程刚好相反:进程被设置为可执行状态,然后再从等待队列中放入可执行红黑树中.

1.等待队列

休眠通过等待队列进行处理.等待队列是由等待某些事件发生的进程组成的简单链表.等待队列可以静态创建,也可以动态创建.进程把自己放入等待队列中并设置成不可执行状态.当与等待队列相关的事件发生的时候,队列上进程会被唤醒.

2.唤醒

唤醒操作通过函数wake_up()进行,他会唤醒指定的等待队列上的所有进程。他调用函数try_to_wake_up()该函数负责将进程设置为TASK_RUNNING状态,调用enqueue_task()将此进程放入红黑树中。

抢占和上下文,切换

上下文切换也就是从一个可执行进程切换到另一个可执行进程,有定义在kernel/sched.c中的context_switch()函数负责处理。每当一个新的进程被选出来准备投入运行的时候,schedule()就会调用该函数。他完成了两项基本的工作:

1.调用生命在中的switch_mm(),该函数负责把虚拟内存从上一个进程切换到新进程中。

2.调用声明在中的switch_to(),该函数负责从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息,还有其他任何与体系与结构相关的状态信息,都必须以每个进程为对象进行管理和保存。

内核必须知道在什么时候调用schedule()。如果仅靠用户程序代码显示的调用schedule(),他们可能永远的执行下去。相反,内核提供了一个need_resched标志来表明是否需要重新执行一次调度。当某个进程应该被抢占时,scheduler_tick()就会设置这个标志;当一个优先级高的进程进入可执行状态时候,try_to_wake_up()也会设置这个标志,内核检查该标志,确认其被设置,调用schedule()来切换到一个新的进程。该标志用于内核来讲是一个信息。他表示有其他进程应当被运行了,要尽快调度程序。

在返回用户空间以及从中断返回的时候,内核也会检查need_resched标志。如果已经被设置,内核会在继续执行之前调用调度程序。每个进程都包含一个need_resched标志,这是因为访问进程描述符内的数值要比访问一个全局变量块(因为current宏速度很快并且描述符通常都在告诉缓存中)

用户抢占:

内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用此时就会发生用户抢占。用户抢占发生在一下情况时产生:

1.从系统调用返回用户空间时。

2.从中断处理程序返回用户空间时。

内核抢占:

与其他大部分的Unix变体的操作系统不同,linux完整地支持内核抢占。在不支持内核抢占的内核中,内核代码可以一直执行,直到他完成为止。也就是说,调度程序没有办法在一个内核级的任务正在执行的时候重新调度。内核代码一直要执行到完成(返回用户空间)或明显阻塞为止。在2.6版的内核中,内核引入了抢占能力;现在,只要重新调度是安全的,内核就可以在任何时间抢占正在执行的任务。

那么什么时候重新调度才是安全的呢?只要没有锁,内核就可以进行抢占。为支持内核抢占所做的第一处变动,就是为每一个进程的thread_info引入preempt_count计数器。该计数器初始值为0,每当使用锁的时候数值加1,锁释放的时候数值减1.当数值为0的时候,内核就可以执行抢占。从中断返回内核空间的时候,内核会检查need_resched和preempt_count的值。如果need_resched被设置,并且preempt_count为0的话,

说明有一个更为重要的任务需要执行并且可以安全的抢占,此时,调度程序就会被调度。如果内核中的进程被阻塞了,或他显示的调用了schedule(),内核抢占也会显示的发生。内核抢断发生在:

1.中断处理程序正在执行,且返回内核空间之前。

2.内核代码再一次具有可抢占性的时候。

3.如果内核中的任务显示的调用schedule()。

4.如果内核中的任务阻塞(这同样也会导致调用schedule())

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

系统调用

一般情况下,应用程序通过用户空间实现的应用编程接口(API)而不是直接通过系统调用来编程。这很重要,因为应用程序使用的这种编程接口实际上并不需要内核提供的系统调用对应。

应用程序调用pringf()--》c库中的printf()--》c库中的write()--》write()系统调用。

系统调用(syscall)

要访问系统调用,通常通过C库中定义的函数调用来进行。通过返回一个long类型的返回值来表示成功或者错误。

asmlinkage long sys_getpid(void)

我们看一下如何定义系统调用。首先,注意函数声明中的asmlinkage限定词,这是一个编译指令,通知编译器仅从栈中提取该函数的参数。所有的系统调用都需要这个限定词。其次,函数返回类型是long。为了保证32位和64位系统的兼容,系统调用在用户空间和内核空间有不同的返回值类型,在用户空间为int,内核空间为long。

系统调用号

在linux中,每个系统调用被赋予一个系统调用号。这样,通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候,这个系统调用号就可以用来指明到底要执行哪一个系统调用;进程不会提及系统调用的名称。系统调用号相当重要,一旦分配就不能再有任何变更,否则编译好的应用程序就会崩溃。此外,如果一个系统调用被删除,他所占的系统调用号也不能被回收。(为了防止原来编译过的程序不能使用)

系统调用处理程序

应用程序需要通知内核进行系统调用,通知内核的机制是靠软中断实现的:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。在x86系统上预定义的软中断是终端号128,通过int$0x80指令触发该中断。这条指令会触发一个异常导致系统切换到内核态并执行第128号异常处理程序,而该程序正是系统调用处理程序。

指定恰当的系统调用

因为所有的系统调用陷入内核的方式都一样,所以仅仅是陷入内核空间时不够的。因此必须把系统调用号一并传给内核。在x86上,系统调用号是通过eax寄存器传递给内核的。在陷入内核之前,用户空间就把相对应的系统调用号放入eax中。这样系统调用处理程序一旦运行就可以从eax中得到数据。

参数传递

除了系统调用号以外,大部分系统调用都还需要一些外部的参数输入。所以在发生陷入的时候,应该把这些参数从用户空间传递给内核,最简单的方法就是像传递系统调用号一样,把这些参数也存放在寄存器中。(ebx,ecx。。。)给用户空间返回值也通过寄存器传递。(eax)

系统调用的实现

参数验证

系统调用必须仔细检查他们所有的参数是否合法有效。如不检查,那么系统的安全和稳定将面临问题。在接受一个用户空间的指针之前,内核必须保证:

1.指针指向的内存区域属于用户空间。进程决不能哄骗内核去读内和空间的数据。

2.指针指向的内部才能区域在进程的地址空间里。进程决不能哄骗内核去读其他进程的数据。

3.如果是读,该内存应被标记为可读;如果是写,该内存应被标记为可写。进程决不能绕过内存访问权限。

系统调用上下文

内核在执行系统调用的时候处于进程上下文。current指针指向当前任务,即引发系统调用的那个进程。在进程上下文中,内核可以休眠并且可以被抢占。这两点很重要。首先,能休眠说明系统调用可以使用内核提供的绝大部分功能。休眠的能力可以给内核编程带来极大的便利。(中断处理程序不能休眠,这使得中断处理程序所能进行的操作较之运行在进程上下文中的系统调用所能进行的操作受到了极大的限制)在进程上下文中能够被抢占其实表明,想用户空间内的进程一样,当前的进程同样可以被其他进程抢占。

从用户空间访问系统调用

通常,系统调用靠c库支持。用户程序通过包含标准头文件并和C库链接,就可以使用系统调用。如果你仅仅写出系统调用,glibc库恐怕并不提供支持。值得庆幸的是,linux本身提供了一组宏,用于直接对系统调用进行访问。他会设置好今存其并调用陷入指令。这样,应用程序就可以直接调用系统调用。

linux 进程 系统调用,linux进程与系统调用相关推荐

  1. 【Linux 内核】进程管理 ( 进程状态 | 进程创建 | 进程终止 | 调用 exit 系统调用函数主动退出 | main 函数返回自动退出 | kill 杀死进程 | 执行异常退出 )

    文章目录 一.进程状态 二.进程创建 三.进程终止 ( 调用 exit 系统调用函数主动退出 | main 函数返回自动退出 | kill 杀死进程 | 执行异常退出 ) 一.进程状态 Linux 进 ...

  2. Linux 2.6 劫持系统调用 隐藏进程

    Linux 2.6 劫持系统调用 隐藏进程 http://hi.baidu.com/widebright/item/e64b1c09b8a557dcdce5b060 很久以前写过一个在Windows系 ...

  3. 【Linux、进程隐藏】在Linux环境下添加系统调用实现进程隐藏

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 [进程隐藏]在Linux环境下添加系统调用实现进程隐藏 前言 一.环境设置: 二.实现方法步骤: 1.思路图 2.利用strace命令 ...

  4. linux内核的进程管理,Linux内核设计与实现——进程管理

    主要内容 进程 进程描述符及任务结构 进程创建 线程在linux中的实现 进程终结 1. 进程 进程不仅仅是一段可执行程序代码,还包含其他资源,如打开的文件,挂起的信号,内核内部数据,处理器状态,一个 ...

  5. Linux从程序到进程

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机如何执行进程呢?这可以说是计算机运行的核心问题.即使我们已经编写好程序,但程 ...

  6. Linux 内核进程管理之进程ID

    Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一.该数据结构 ...

  7. Linux内核分析——第五章 系统调用

    第五章 系统调用 5.1 与内核通信 1.系统调用在用户空间进程和硬件设备之间添加了一个中间层,该层主要作用有三个: (1)为用户空间提供了一种硬件的抽象接口 (2)系统调用保证了系统的稳定和安全 ( ...

  8. linux下的daemon进程

    转自:http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html #include <unistd.h> int dae ...

  9. linux编程取消wait函数,Linux编程基础之进程等待(wait()函数).pdf

    Linux编程基础之进程等待(wait()函数) 编程过程中,有时需要让一个进程等待另一个进程 ,最常见的是父进程等待自己的子进程 ,或者父进程回收自己 的子进程资源包括僵尸进程.这里简单介绍一下系统 ...

最新文章

  1. window docker mysql_windows使用docker运行mysql等工具(二)安装运行mysql
  2. leetcode-44. Wildcard Matching
  3. 一文读懂如何用LSA、PSLA、LDA和lda2vec进行主题建模
  4. Eclipse里不同的project,右键选择属性property facet里看到的list 内容是否相同
  5. python 线程(一)理论部分
  6. 在python中可以使用if作为变量名_变量,注释,缩进,细数Python优雅风 | Python基础连载(二)...
  7. 我乃平常客,本持平常心| 2021 年中总结
  8. 【数据结构和算法笔记】:数据结构概述
  9. 转:extjs里的fieldset不居中的解决办法(记录)
  10. 海归博士程序员光鲜背后:下车间写代码,体验炼钢灼人的热度
  11. 【无标题】梦笔记2022-02-20
  12. java 二进制文件修改_Java读写二进制文件操作
  13. 母婴商品销量分析(附Python源码及Tableau文件)
  14. ISA-95/B2MML教程 : 从用例到XML消息的整合实践
  15. 简单几步让WinUSB设备变为多端点设备
  16. 1088: 手机短号 (多实例)C语言
  17. 外汇救市无异加印人民币
  18. 软件测试工程师,是学习Java还是Python比较好呢?
  19. 织梦进入mysql_织梦phpmyadmin怎么进入
  20. 孩子学python_【二孩家庭要注意什么】二孩家庭要怎么教育_二孩家庭的孩子最好间隔几岁 - 妈妈网百科...

热门文章

  1. 为儿童设计的Android 4.0平板MEEP!
  2. Mac电脑如何卸载Sophos防病毒软件?
  3. VHDL实现与门,或门,非门。
  4. dnf服务器维护中 启动游戏失败,dnf1月29日启动游戏失败怎么办 dnf提示客户端版本非法解决方法...
  5. java计算机毕业设计-物料采购合同管理系统-源程序+mysql+系统+lw文档+远程调试
  6. 有效降低医院网站改版对SEO的影响
  7. 泡妞之必备QQ聊天用语
  8. touchpad android 7.1,发布了8年的惠普TouchPad平板,已可刷入安卓9 Pie
  9. 卖500元个人版通用权限管理系统组件源码终于引来了下50万C#.NET软件开发订单的大客户...
  10. OneNote 2007 无法启动的问题