参考博客: fork()函数的实现过程
http://blog.tonychow.me/blog/2013/06/27/linuxzhong-forkxi-tong-diao-yong-fen-xi/
参考书籍
《Linux内核设计与实现》
关于likely()与unlikely()函数:
转载博客
likely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢?
首先明确:
if (likely(value))等价于if (value)
if (unlikely(value))等价于if (value)
也就是说likely()和unlikely()从阅读和理解的角度是一样的。
这两个宏在内核中定义如下:
define likely(x) __builtin_expect(!!(x), 1)
define unlikely(x) __builtin_expect(!!(x), 0)
这里的__built_expect()函数是gcc(version >= 2.96)的内建函数,提供给程序员使用的,目的是将”分支转移”的信息提供给编译器,这样编译器对代码进行优化,以减少指令跳转带来的性能下降。
__buildin_expect((x), 1)表示x的值为真的可能性更大.
__buildin_expect((x), 0)表示x的值为假的可能性更大.
也就是说,使用likely(),执行if后面的语句的机会更大,使用unlikely(),执行else后面的语句机会更大一些。通过这种方式,编译器在编译过程中,会将可能性更大的代
码紧跟着后面的代码,从而减少指令跳转带来的性能上的下降。
比如 :
if (likely(a>b)) {
fun1();
}
if (unlikely(a fun2();
}
这里就是程序员可以确定 a>b 在程序执行流程中出现的可能相比较大,因此运用了likely()告诉编译器将fun1()函数的二进制代码紧跟在前面程序的后面,这样就
cache在预取数据时就可以将fun1()函数的二进制代码拿到cache中。这样,也就添加了cache的命中率。
个人觉得重要的概念
进程:是处于执行期的程序及相关的资源的总称,是对资源调用分配的单位。程序可以有多个进程。
线程:是在进程中活动的对象!是内核调度的对象。对Linux而言,线程与进程并不特别区分,只是在调用fork()函数时,传入的参数不同,即要复制的东西不同。
Linux进程描述:Linux 中的进程描述符是一个 task_struct 类型的结构体。在 Linux 中,一个进程的进程描述符结构如下图所示:
task_struct 是一个相当大的数据结构,同时里面也指向了其他类型的数据结构,比如 thread_info,指向的是这个进程的线程信息; mm_struct 指向了这个进程的内存结构; file_struct 指向了这个进程打开的进程描述符结构,等等。task_struct 是一个复杂的数据结构,我们将会在下面对其进行更详细的分析。
不同架构的task_struct对应的thread_info并不同,因为有些架构的处理器中,并没有多余的寄存器用于存储这些信息,故只能在内核栈的尾端创建该结构,通过计算偏移量间接的查找.!
进程的状态:五种,进程的状态:
TASK_RUNNING: 进程是可执行的进程要么在执行,要么在运行队列中等待执行
TASK_INTERRUPTIBLE:可中断。进程正在睡眠,等待某些条件的达成。条件达成,内核就会把进程状态设置为运行。也可以接收到信号而提前被唤醒。
TASK_UNINTERUPTIBLE:不可中断。接收到信号也不会投入运行。其他和可中断相同。
__TASK_TRACED:被其他进程跟踪
__TASK_STOPPED:进程停止。进程没有投入运行,也不能投入运行。在接受到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信号时调试期间收到这种信号,都会进入这种状态。
TASK__ZOMBIE僵尸,呆傻状态,已结束,但其父进程未接到通知,描述符未释放
TASK_SWAPPING进程页面被兑换出内存
task_struct的exit_state域中有EXIT_ZOMBIE
进程家族:所有进程都是init进程的后代,每个进程必有一个父进程。
进程的创建:调用fork()与exec()函数。
Linux的fork()采用写时拷贝页实现,写时拷贝是一种可以被推辞到甚至被免除拷贝数据的技术,内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。
fork()具体实现看参考博客
创建线程与创建进程的区别
线程的创建和普通进程创建类似,只是在调用clone()的时候,需要传递一些参数标志知名需要共享的资源。
进程的终结
进程的折构是自身引起的,该任务的大部分需要靠do_exit()执行,他需要完成的操作如下:
- 将标志设置为PF_EXITING
- 调用del_timer_sync()删除任意内核定时器。
- 如果BSD进程几张功能开启,do_Exit()会调用相应函数,输出记账信息。
- 释放mm__struct即分配的栈空间,如果资源是单独享有的话。
- 调用sem__exit()函数。如果进程排队等候IPC信号,它则离开队列。
- 退出各种资源使用
为子进程寻找养父。
尽管调用了do_exit()后进程死了,但是进程仍旧保留了他的进程描述符,