第一次作业:基于Linux操作系统深入源码进程模型分析

1.Linux操作系统的简易介绍

  Linux系统一般有4个主要部分:内核、shell、文件系统和应用程序。内核、shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序、管理文件并使用系统。    

(1)内核

  内核是操作系统的核心,具有很多最基本功能,如虚拟内存、多任务、共享库、需求加载、可执行程序和TCP/IP网络功能。Linux内核的模块分为以下几个部分:存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通信、系统的初始化和系统调用等。

(2)shell

  shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行,是一个命令解释器。另外,shell编程语言具有普通编程语言的很多特点,用这种编程语言编写的shell程序与其他应用程序具有同样的效果。

(3)文件系统

  文件系统是文件存放在磁盘等存储设备上的组织方法。Linux系统能支持多种目前流行的文件系统,如EXT2、EXT3、FAT、FAT32、VFAT和ISO9660。

(4)应用程序

  标准的Linux系统一般都有一套都有称为应用程序的程序集,它包括文本编辑器、编程语言、XWindow、办公套件、Internet工具和数据库等。

2.Linux操作系统的进程组织

(1)什么是进程

  进程是处于执行期的程序以及它所包含的所有资源的总称,包括虚拟处理器,虚拟空间,寄存器,堆栈,全局数据段等。

  在Linux中,每个进程在创建时都会被分配一个数据结构,称为进程控制(Process Control Block,简称PCB)。PCB中包含了很多重要的信息,供系统调度和进程本身执行使用。所有进程的PCB都存放在内核空间中。PCB中最重要的信息就是进程PID,内核通过这个PID来唯一标识一个进程。PID可以循环使用,最大值是32768。init进程的pid为1,其他进程都是init进程的后代。

  除了进程控制块(PCB)以外,每个进程都有独立的内核堆栈(8k),一个进程描述符结构,这些数据都作为进程的控制信息储存在内核空间中;而进程的用户空间主要存储代码和数据。

查看进程:

(2)进程创建

  进程是通过调用::fork(),::vfork()【只复制task_struct和内核堆栈,所以生成的只是父进程的一个线程(无独立的用户空间)。】和::clone()【功能强大,带了许多参数。::clone()可以让你有选择性的继承父进程的资源,既可以选择像::vfork()一样和父进程共享一个虚拟空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。】系统调用创建新进程。在内核中,它们都是调用do_fork实现的。传统的fork函数直接把父进程的所有资源复制给子进程。而Linux的::fork()使用写时拷贝页实现,也就是说,父进程和子进程共享同一个资源拷贝,只有当数据发生改变时,数据才会发生复制。通常的情况,子进程创建后会立即调用exec(),这样就避免复制父进程的全部资源。

    #fork():父进程的所有数据结构都会复制一份给子进程(写时拷贝页)。当执行fork()函数后,会生成一个子进程,子进程的执行从fork()的返回值开始,且代码继续往下执行

以下代码中,使用fork()创建了一个子进程。返回值pId有两个作用:一是判断fork()是否正常执行;二是判断fork()正常执行后如何区分父子进程。

 1 #代码示例:
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5
 6 int main (int argc, char ** argv) {
 7     int flag = 0;
 8     pid_t pId = fork();
 9     if (pId == -1) {
10         perror("fork error");
11         exit(EXIT_FAILURE);
12     } else if (pId == 0) {
13         int myPid = getpid();
14         int parentPid = getppid();
15
16         printf("Child:SelfID=%d ParentID=%d \n", myPid, parentPid);
17         flag = 123;
18         printf("Child:flag=%d %p \n", flag, &flag);
19         int count = 0;
20         do {
21             count++;
22             sleep(1);
23             printf("Child count=%d \n", count);
24             if (count >= 5) {
25                 break;
26             }
27         } while (1);
28         return EXIT_SUCCESS;
29     } else {
30         printf("Parent:SelfID=%d MyChildPID=%d \n", getpid(), pId);
31         flag = 456;
32         printf("Parent:flag=%d %p \n", flag, &flag); // 连地址都一样,说明是真的完全拷贝,但值已经是不同的了..
33         int count = 0;
34         do {
35             count++;
36             sleep(1);
37             printf("Parent count=%d \n", count);
38             if (count >= 2) {
39                 break;
40             }
41         } while (1);
42     }
43
44     return EXIT_SUCCESS;
45 } 

(3)进程撤销

  进程通过调用exit()退出执行,这个函数会终结进程并释放所有的资源。父进程可以通过wait4()查询子进程是否终结。进程退出执行后处于僵死状态,直到它的父进程调用wait()或者waitpid()为止。父进程退出时,内核会指定线程组的其他进程或者init进程作为其子进程的新父进程。当进程接收到一个不能处理或忽视的信号时,或当在内核态产生一个不可恢复的CPU异常而内核此时正代表该进程在运行,内核可以强迫进程终止。

(4)进程管理

  内核把进程信息存放在叫做任务队列(task list)的双向循环链表中(内核空间)。链表中的每一项都是类型为task_struct,称为进程描述符结构(process descriptor),包含了一个具体进程的所有信息,包括打开的文件,进程的地址空间,挂起的信号,进程的状态等。

  Linux通过slab分配器分配task_struct,这样能达到对象复用和缓存着色(通过预先分配和重复使用task_struct,可以避免动态分配和释放所带来的资源消耗)。

struct task_struct
{
volatile long state;
pid_t pid;
unsigned long timestamp;
unsigned long rt_priority;
struct mm_struct *mm, *active_mm
}

对于向下增长的栈来说,只需要在栈底(对于向上增长的栈则在栈顶)创建一个新的结构struct thread_info,使得在汇编代码中计算其偏移量变得容易。

#在x86上,thread_info结构在文件<asm/thread_info.h>中定义如下:struct thread_info{
             struct task_struct              *任务
             struct exec_domain              *exec_domain;
             unsigned long                   flags;
             unsigned long                   status;
             __u32                           cpu;
             __s32                           preempt_count;
             mm_segment_t                    addr_limit;
             struct restart_block            restart_block;
             unsigned long                   previous_esp;
             _u8                             supervisor_stack[0];
    };

  内核把所有处于TASK_RUNNING状态的进程组织成一个可运行双向循环队列。调度函数通过扫描整个可运行队列,取得最值得执行的进程投入执行。避免扫描所有进程,提高调度效率。

#进程调度使用schedule()函数来完成,下面我们从分析该函数开始,代码如下:
1 asmlinkage __visible void __sched schedule(void)
2 {
3     struct task_struct *tsk = current;
4
5     sched_submit_work(tsk);
6     __schedule();
7 }
8 EXPORT_SYMBOL(schedule);#在第4段进程调度中将具体讲述功能实现

(5)进程内核堆栈

  Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:thread_info和进程的内核堆栈。

  进程处于内核态时使用不同于用户态堆栈,内核控制路径所用的堆栈很少,因此对栈和描述符来说,8KB足够了。

3.Linux操作系统的进程状态转换

有以下进程状态:

进程状态的转换:

具体转换分析:

(1)进程的初始状态

进程是通过fork系列的系统调用(fork、clone、vfork)来创建的,内核(或内核模块)也可以通过kernel_thread函数创建内核进程。这些创建子进程的函数本质上都完成了相同的功能——将调用进程复制一份,得到子进程。(可以通过选项参数来决定各种资源是共享、还是私有。)那么既然调用进程处于TASK_RUNNING状态(否则,它若不是正在运行,又怎么进行调用?),则子进程默认也处于TASK_RUNNING状态。另外,在系统调用调用clone和内核函数kernel_thread也接受CLONE_STOPPED选项,从而将子进程的初始状态置为 TASK_STOPPED。

(2)进程状态变迁

进程自创建以后,状态可能发生一系列的变化,直到进程退出。而尽管进程状态有好几种,但是进程状态的变迁却只有两个方向——从TASK_RUNNING状态变为非TASK_RUNNING状态、或者从非TASK_RUNNING状态变为TASK_RUNNING状态。也就是说,如果给一个TASK_INTERRUPTIBLE状态的进程发送SIGKILL信号,这个进程将先被唤醒(进入 TASK_RUNNING状态),然后再响应SIGKILL信号而退出(变为TASK_DEAD状态)。并不会从TASK_INTERRUPTIBLE状态直接退出。进程从非TASK_RUNNING状态变为TASK_RUNNING状态,是由别的进程(也可能是中断处理程序)执行唤醒操作来实现的。执行唤醒的进程设置被唤醒进程的状态为TASK_RUNNING,然后将其task_struct结构加入到某个CPU的可执行队列中。于是被唤醒的进程将有机会被调度执行。

而进程从TASK_RUNNING状态变为非TASK_RUNNING状态,则有两种途径:

  • 响应信号而进入TASK_STOPED状态、或TASK_DEAD状态;
  • 执行系统调用主动进入TASK_INTERRUPTIBLE状态(如nanosleep系统调用)、或TASK_DEAD状态(如exit 系统调用);或由于执行系统调用需要的资源得不到满足,而进入TASK_INTERRUPTIBLE状态或TASK_UNINTERRUPTIBLE状态(如select系统调用)。

4.Linux操作系统的进程调度

 毋庸置疑,我们使用schedule()函数来完成进程调度,接下来就来看看进程调度的代码以及实现过程吧。

1 asmlinkage __visible void __sched schedule(void)
2 {
3     struct task_struct *tsk = current;
4
5     sched_submit_work(tsk);
6     __schedule();
7 }
8 EXPORT_SYMBOL(schedule);

  第3行获取当前进程描述符指针,存放在本地变量tsk中。第6行调用__schedule(),代码如下(kernel/sched/core.c):

 1 static void __sched __schedule(void)
 2 {
 3     struct task_struct *prev, *next;
 4     unsigned long *switch_count;
 5     struct rq *rq;
 6     int cpu;
 7
 8 need_resched:
 9     preempt_disable();
10     cpu = smp_processor_id();
11     rq = cpu_rq(cpu);
12     rcu_note_context_switch(cpu);
13     prev = rq->curr;
14
15     schedule_debug(prev);
16
17     if (sched_feat(HRTICK))
18         hrtick_clear(rq);
19
20     /*
21      * Make sure that signal_pending_state()->signal_pending() below
22      * can‘t be reordered with __set_current_state(TASK_INTERRUPTIBLE)
23      * done by the caller to avoid the race with signal_wake_up().
24      */
25     smp_mb__before_spinlock();
26     raw_spin_lock_irq(&rq->lock);
27
28     switch_count = &prev->nivcsw;
29     if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
30         if (unlikely(signal_pending_state(prev->state, prev))) {
31             prev->state = TASK_RUNNING;
32         } else {
33             deactivate_task(rq, prev, DEQUEUE_SLEEP);
34             prev->on_rq = 0;
35
36             /*
37              * If a worker went to sleep, notify and ask workqueue
38              * whether it wants to wake up a task to maintain
39              * concurrency.
40              */
41             if (prev->flags & PF_WQ_WORKER) {
42                 struct task_struct *to_wakeup;
43
44                 to_wakeup = wq_worker_sleeping(prev, cpu);
45                 if (to_wakeup)
46                     try_to_wake_up_local(to_wakeup);
47             }
48         }
49         switch_count = &prev->nvcsw;
50     }
51
52     if (prev->on_rq || rq->skip_clock_update < 0)
53         update_rq_clock(rq);
54
55     next = pick_next_task(rq, prev);
56     clear_tsk_need_resched(prev);
57     clear_preempt_need_resched();
58     rq->skip_clock_update = 0;
59
60     if (likely(prev != next)) {
61         rq->nr_switches++;
62         rq->curr = next;
63         ++*switch_count;
64
65         context_switch(rq, prev, next); /* unlocks the rq */
66         /*
67          * The context switch have flipped the stack from under us
68          * and restored the local variables which were saved when
69          * this task called schedule() in the past. prev == current
70          * is still correct, but it can be moved to another cpu/rq.
71          */
72         cpu = smp_processor_id();
73         rq = cpu_rq(cpu);
74     } else
75         raw_spin_unlock_irq(&rq->lock);
76
77     post_schedule(rq);
78
79     sched_preempt_enable_no_resched();
80     if (need_resched())
81         goto need_resched;
82 }

static void __sched __schedule(void)

  第9行禁止内核抢占。第10行获取当前的cpu号。第11行获取当前cpu的进程运行队列。第13行将当前进程的描述符指针保存在prev变量中。第55行将下一个被调度的进程描述符指针存放在next变量中。第56行清除当前进程的内核抢占标记。第60行判断当前进程和下一个调度的是不是同一个进程,如果不是的话,就要进行调度。第65行,对当前进程和下一个进程的上下文进行切换(调度之前要先切换上下文)。下面看看该函数(kernel/sched/core.c):

 1 context_switch(struct rq *rq, struct task_struct *prev,
 2            struct task_struct *next)
 3 {
 4     struct mm_struct *mm, *oldmm;
 5
 6     prepare_task_switch(rq, prev, next);
 7
 8     mm = next->mm;
 9     oldmm = prev->active_mm;
10     /*
11      * For paravirt, this is coupled with an exit in switch_to to
12      * combine the page table reload and the switch backend into
13      * one hypercall.
14      */
15     arch_start_context_switch(prev);
16
17     if (!mm) {
18         next->active_mm = oldmm;
19         atomic_inc(&oldmm->mm_count);
20         enter_lazy_tlb(oldmm, next);
21     } else
22         switch_mm(oldmm, mm, next);
23
24     if (!prev->mm) {
25         prev->active_mm = NULL;
26         rq->prev_mm = oldmm;
27     }
28     /*
29      * Since the runqueue lock will be released by the next
30      * task (which is an invalid locking op but in the case
31      * of the scheduler it‘s an obvious special-case), so we
32      * do an early lockdep release here:
33      */
34 #ifndef __ARCH_WANT_UNLOCKED_CTXSW
35     spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
36 #endif
37
38     context_tracking_task_switch(prev, next);
39     /* Here we just switch the register state and the stack. */
40     switch_to(prev, next, prev);
41
42     barrier();
43     /*
44      * this_rq must be evaluated again because prev may have moved
45      * CPUs since it called schedule(), thus the ‘rq‘ on its stack
46      * frame will be invalid.
47      */
48     finish_task_switch(this_rq(), prev);
49 }

context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)

  上下文切换一般分为两个,一个是硬件上下文切换(指的是cpu寄存器,要把当前进程使用的寄存器内容保存下来,再把下一个程序的寄存器内容恢复),另一个是切换进程的地址空间(说白了就是程序代码)。进程的地址空间(程序代码)主要保存在进程描述符中的struct mm_struct结构体中,因此该函数主要是操作这个结构体。第17行如果被调度的下一个进程地址空间mm为空,说明下个进程是个线程,没有独立的地址空间,共用所属进程的地址空间,因此第18行将上个进程所使用的地址空间active_mm指针赋给下一个进程的该域,下一个进程也使用这个地址空间。第22行,如果下个进程地址空间不为空,说明下个进程有自己的地址空间,那么执行switch_mm切换进程页表。第40行切换进程的硬件上下文。 switch_to函数代码如下(arch/x86/include/asm/switch_to.h):

 1 __visible __notrace_funcgraph struct task_struct *
 2 __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
 3 {
 4     struct thread_struct *prev = &prev_p->thread,
 5                  *next = &next_p->thread;
 6     int cpu = smp_processor_id();
 7     struct tss_struct *tss = &per_cpu(init_tss, cpu);
 8     fpu_switch_t fpu;
 9
10     /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */
11
12     fpu = switch_fpu_prepare(prev_p, next_p, cpu);
13
14     /*
15      * Reload esp0.
16      */
17     load_sp0(tss, next);
18
19     /*
20      * Save away %gs. No need to save %fs, as it was saved on the
21      * stack on entry.  No need to save %es and %ds, as those are
22      * always kernel segments while inside the kernel.  Doing this
23      * before setting the new TLS descriptors avoids the situation
24      * where we temporarily have non-reloadable segments in %fs
25      * and %gs.  This could be an issue if the NMI handler ever
26      * used %fs or %gs (it does not today), or if the kernel is
27      * running inside of a hypervisor layer.
28      */
29     lazy_save_gs(prev->gs);
30
31     /*
32      * Load the per-thread Thread-Local Storage descriptor.
33      */
34     load_TLS(next, cpu);
35
36     /*
37      * Restore IOPL if needed.  In normal use, the flags restore
38      * in the switch assembly will handle this.  But if the kernel
39      * is running virtualized at a non-zero CPL, the popf will
40      * not restore flags, so it must be done in a separate step.
41      */
42     if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl))
43         set_iopl_mask(next->iopl);
44
45     /*
46      * If it were not for PREEMPT_ACTIVE we could guarantee that the
47      * preempt_count of all tasks was equal here and this would not be
48      * needed.
49      */
50     task_thread_info(prev_p)->saved_preempt_count = this_cpu_read(__preempt_count);
51     this_cpu_write(__preempt_count, task_thread_info(next_p)->saved_preempt_count);
52
53     /*
54      * Now maybe handle debug registers and/or IO bitmaps
55      */
56     if (unlikely(task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV ||
57              task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT))
58         __switch_to_xtra(prev_p, next_p, tss);
59
60     /*
61      * Leave lazy mode, flushing any hypercalls made here.
62      * This must be done before restoring TLS segments so
63      * the GDT and LDT are properly updated, and must be
64      * done before math_state_restore, so the TS bit is up
65      * to date.
66      */
67     arch_end_context_switch(next_p);
68
69     this_cpu_write(kernel_stack,
70           (unsigned long)task_stack_page(next_p) +
71           THREAD_SIZE - KERNEL_STACK_OFFSET);
72
73     /*
74      * Restore %gs if needed (which is common)
75      */
76     if (prev->gs | next->gs)
77         lazy_load_gs(next->gs);
78
79     switch_fpu_finish(next_p, fpu);
80
81     this_cpu_write(current_task, next_p);
82
83     return prev_p;
84 }

__visible __notrace_funcgraph struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct *next_p)

  该函数主要是对刚切换过来的新进程进一步做些初始化工作。比如第34将该进程使用的线程局部存储段(TLS)装入本地cpu的全局描述符表。第84行返回语句会被编译成两条汇编指令,一条是将返回值prev_p保存到eax寄存器,另外一个是ret指令,将内核栈顶的元素弹出eip寄存器,从这个eip指针处开始执行,也就是上个函数第17行所压入的那个指针。一般情况下,被压入的指针是上个函数第20行那个标号1所代表的地址,那么从__switch_to函数返回后,将从标号1处开始运行。

  需要注意的是,对于已经被调度过的进程而言,从__switch_to函数返回后,将从标号1处开始运行;但是对于用fork(),clone()等函数刚创建的新进程(未调度过),将进入ret_from_fork()函数,因为do_fork()函数在创建好进程之后,会给进程的thread_info.ip赋予ret_from_fork函数的地址,而不是标号1的地址,因此它会跳入ret_from_fork函数。后边我们在分析fork系统调用的时候,就会看到。

5.对于Linux操作系统进程模型的一些个人看法

  有一个形象的比喻:想象一位知识渊博、经验丰富的工程建筑设计师正在为一个公司设计总部。他有公司建筑的设计图,有所需的建筑材料和工具:水泥、钢筋、木板、挖掘机、吊升机、石钻头等。在这个比喻中,设计图就是程序(即用适当形式描述的算法),工程建筑师就是处理器(CPU),而建筑的各种材料就是输入数据。进程就是建筑工程设计师阅读设计图、取来各种材料和工具以及管理工人员工和分配资源、最后施工等一系列动作的总和,在过程中工程建筑师还需要遵循许多设计的规范和理念(模型),最后完成的公司总部就是软件或者可以实现某种功能的源代码。

  这里说明的是进程是某种类型的一个活动,它有程序、输入、输出以及状态。单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。那么Linux操作系统进程模型就是活动的规范,规范的出现创新让许多实现过程更加系统完整、安全可靠、速度效率等。

  就像人类基于理论实践伟大的工程设计智慧经验结晶,Linux操作系统是系统、效率、安全的,而且通过商业公司、庞大的社区群体、操作系统爱好者是在往前改善的,但如果有一天Linux操作系统闭源了,只有国内开放了源代码,还尚未掌握核心技术,卡住脖子怎么办?我们不能拥有完完全全拿来即用的心态,还需扎实掌握基础知识,提高自我创新意识。对于Linux操作系统进程模型,深入理解它,你会发现在Linux操作系统的应用实践上会愈加效率,同时通过它你可以实现更多好玩的操作。

6.参考资料

Contiki学习笔记:目录

源码地址(https://elixir.bootlin.com/linux/v4.6/source)

脚本之家(http://www.jb51.net/)

CSDN博客(https://blog.csdn.net/)

百度知道(https://zhidao.baidu.com/)

原文地址:https://www.cnblogs.com/maishistar/p/8974598.html

时间: 2024-10-13 05:28:33

第一次作业:基于Linux操作系统深入源码进程模型分析的相关文章

基于linux操作系统深入源码进程模型分析

1.进程 1.1进程的概念 计算机上所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程(sequential process),简称进程(process). 一个进程就是一个正在执行程序的实例,包括程序设计器.寄存器和变量的当前值.一个进程是某种类型的一种活动,它有程序.输入.输出以及状态.单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为了另一个进程服务功能. 1.2进程的特征 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动

第一次作业:基于Linux2.6内核源码进程模型分析

1.概括 进程的基本概念 操作系统是如何组织进程的 进程是如何调度的 对Linux操作系统进程模型的看法 2.什么是进程 一个进程就是一个正在运行的程序.一个进程应该包含以下内容:(1) 程序的代码,既然进程是一个正在运行的程序,自然需要程序的代码(2) 程序的数据(3) CPU寄存器的值,包括通用寄存器,程序计数器(4) 堆(heap)是用来保存进程运行时动态分配的内存空间(5) 栈(stack)有两个用途,1保存运行的上下文信息.2在函数调用时保存被调用函数的形参或者局部变量(6) 进程所占

第一次作业:Linux 2.6.32的进程模型与调度器分析

1.前言 本文分析的是Linux 2.6.32版的进程模型以及调度器分析.在线查看 源码下载 本文主要讨论以下几个问题: 什么是进程?进程是如何产生的?进程都有那些? 在操作系统中,进程是如何被管理以及它们是怎样被调用的? 2.进程模型 2.1进程的概念 在我的理解中,一个程序就相当于一个进程,程序的启动意味着产生了一个新的进程,程序的关闭也就意味着一个进程的消亡. 那么专业定义应该是: 在计算中,进程是正在执行的计算机程序的一个实例. 它包含程序代码及其当前活动. 根据操作系统(OS),一个进

第一次作业:基于Orange&#39;s OS系统的进程模型分析与心得体会

1一. 操作系统进程概念模型与进程控制块概念浅析 1. 什么是进程? 图 1 - 1 (WIN10系统任务管理器对进程管理的图形化界面) 计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. --百度百科 应用程序的实例.对正在运行的程序的抽象. --<现代操作系统> 2. 什么是进程控制块? 进程控制块(Processing Control Block),是操作系统核心中一种数据结构,主要表示进程状态.其作用是使一个在多道程序环境下不能独立

第一次作业:基于Linux系统深入源码分析进程模型

1.前言 本文主要基于Linux 2.6源代码分析进程模型.源代码下载地址:https://elixir.bootlin.com/linux/v2.6.39/source 2.进程 定义:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. 3.Linux系统进程的组织 进程是由进程控制块(PCB).程序段.数据段三部分组成. 3.1 进程控制块 进程控制块(Processing Control Block),是操作系统核心中一种数据结构

第一次作业 基于Linux 0.12的进程模型分析

作业内容 挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容: 操作系统是怎么组织进程的 进程状态如何转换(给出进程状态转换图) 进程是如何调度的 谈谈自己对该操作系统进程模型的看法 1.操作系统是怎么组织进程的 1.1什么是进程 程序是一个可执行的文件,而进程是一个执行中的程序实例.Linux操作系统上利用分时技术,可同时运行多个进程.利用分时技术,在Linux操作系统上同时可以运行多个进程.分时技术的基本原理是把CPU的运行时间划分成一个个规定长度的时间片,让每个进程在一个时间

Linux平台下源码安装mysql多实例数据库

Linux平台下源码安装mysql多实例数据库[[email protected] ~]# netstat -tlunp | grep 330tcp6 0 0 :::3306 :::* LISTEN 6191/mysqld [[email protected] ~]# ss -tlunp | grep 330tcp LISTEN 0 80 :::3306 :::* users:(("mysqld",pid=6191,fd=10)) [[email protected] ~]# syst

在 Linux 系统上源码安装 GTK+ 2.0

在 Linux 系统上源码安装 GTK+ 2.0==================================================Keywords: GTK+, Install, Linux, SourceAuthor:       whyglinux (whyglinux AT hotmail DOT com)Date:          2007-01-07==================================================目录0. 前言1.

数据库学习之--Linux下Mysql源码包安装

数据库学习之--Linux下Mysql源码包安装 系统环境: 操作系统:RedHat EL6 DB Soft:  Mysql 5.6.4-m7     Mysql 在linux下的安装方式有两种版本,一种为Binary(二进制),另外一种为Source(源码包),本文为Source Install方式. 1.安装前的准备 解压安装包 [[email protected] ~]$ ls mysql-5.6.4-m7  mysql-5.6.4-m7.tar.gz  mysql-5.6.4-m7.ta