1.系统的进程运转方式
(jiffies 系统滴答)类似于系统的脉搏 可以想象成定时器 10ms
10ms一个系统滴答---->每隔10ms会引发一个定时器中断
然后执行中断函数,首先执行jiffies自增,然后执行与进程相关的函数(比如在最早的版本中使用优先级时间片轮转调度算法)
2.进程相关概念
- Linux系统 对线程和进程并不特别区分。线程仅仅被视为一个与其他线程共享某些资源的进程。每个线程都拥有唯一自己的task_struct。
-
内核调度的对象是根据task_struct结构体。可以说是线程,而不是进程。不仅仅要有资源,还需要有进程的描述,例如:pid(pid的数量是有限的)
在linux系统里,进程和线程都是通过task_struct结构体来描述。 进程之间不共享地址空间,而线程与创建它的进程是共享地址空间的。
3.进程的创建
3.1初始化
Linux在初始化的过程中会进行0号进程的创建,fork(
内核态是不可抢占的
用户态是可以抢占的
)
先把内核态切换到用户态,在内核初始化的过程中,会手动创建0号进程,0号进程是所有进程的父进程。
在0号进程中:
1.打开标准输入 输出 错误的控制台
2.创建1号进程,如果创建成功,则在一号进程中 首先打开"/etc/rc"文件,然后执行SHELL程序
"/bin/sh"
3.0号进程不可能结束,他会在没有其他进程调用的时候pause()
3.2进程创建
fork()
1.在task链变中找一个进程空位存放当前的进程
2.创建一个task_struct
3.设置task_struct
进程的创建就是对0号进程或者当前进程的复制
0号进程复制 结构体的复制 把task[0]对应的task_struct复制给新创建的task_struct
对于栈堆的拷贝 当进程做创建的时候要复制原有的栈堆
再次具体一定就是:
1.给当前要创建的进程分配一个进程号。
2.创建一个子进程的task_struct结构体
3.将当前的子进程放入到整体进程链表中
4.设置创建的task_struct结构体
进程存放图:
4.进程的销毁
4.1 exit是销毁函数---------一个系统调用------do_exit
首先该函数会释放进程的代码段和数据段占用的内存
4.2 关闭进程打开的所有文件,对当前的目录和i节点进行同步(文件操作)
4.3 如果当前要销毁的进程有子进程,那么就让1号进程作为新的父进程(init)
4.4 如果当前进程是一个会话头进程,则会终止会话中的所有进程
4.5 改变当前进程的运行状态,变成TASK_ZOMBIE僵死状态,并且向其父进程发送SIGCHLD信号。
--------------------
4.6 父进程在运行子进程的时候,一般会运行wait waitpid这两个函数(父进程等待某个子进程终止)当父进程收到SIGCHLD信号时父进程会终止僵死状态的子进程。
4.7 首先父进程会把子进程的运行时间累加到自己的进程变量中
4.8 把对于的子进程的进程描述结构体进行释放,置空任务数组中的空槽
总结 fork , vfork, clone
由于执行fork()引入了 写时拷贝并且明确了子进程先执行,所以 vfork()的好处就仅限于不拷贝父进程的页表项mm_struct。vfork()系统调用的实现是通过向clone()系统调用传递一个特殊标志来进行。
vfork场景下父进程会先休眠,等唤醒子进程后,再唤醒父进程。
这么做的好处是:由于子进程被创建出来,与父进程共享地址空间,且只读。只有在执行exec的创建新的内存映射时才会拷贝父进程的数据,来创建新的地址空间。如果此时,父进程还在执行,就有可能产生脏数据,或发生死锁。
线程在Linux中的实现
Linux实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux把所有线程都当做进程来实现。内核并没有准备特别的调度算法或者是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。
线程的创建:
创建和普通进程的创建类似,只不过在clone()的时候需要传递一些参数标志来指明需要共享的资源。
内核线程:
内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,他们只在内核空间运行,从来不切换到用户空间去。