📄 上传.txt
字号:
1.(若知道进程号,那么可以通过HASH 表很快地找到该进程)查找函数
static inline struct task_struct *find_task_by_pid(int pid)
struct task_struct*p,**htable=&[pid_hashfn(pid)];for(p=*htable;p&&p->pid!=;p=p->pidhash_next);return p
2.(调度程序的提速)
已被高度化了的schedul函数(26686行)又被作了进一步优化。大多数修改只是在System_call(171行)系列调用的基础上进行了重新组织,也就是说,通过允许普通情况直接通过以及把遍布在函数里的大部分if语句体都分散开来的方式提高代码地(运行)速度。举例来说,弟26706和26707行,如果要运行的代码它们就运行底下的下半部分,现在采用的这种形式:
if (bh_mask &bh_active)
goto handle_bh;
handle_bh_back;
do_bottom_half();
goto handle_bh_back;
这样一来,如果下半部分程序必须运行,控制流程就会跳转到新的handle_bh标号处,然后在运行完下半部分的正常情况下也要产生一个分支转移,因为在那种情况下所产生代码不得不跳过对handle_bottom_halves的调用。在新的版本里,正常情况只需直接通过不会产生任何分支
3.(用户空间和内核空间动态内存)
用户任务和内核本身都要快速的分配内存。C程序一般使用著名的malloc和free函数来完成这项工作;内核也有他自己的类似的而机制。当然,内核必须自少提供支持C语言的malloc函数和free函数的低级操作。
在LINUX平台上,就像其他的UNIX变种一样,一个进程的数据分区分为两个便于使用的部分,即“椎”和“栈”。为了避免这两个部分冲突,栈从(准确的是接近)可用地址空间的顶端开始并向下扩展,而椎从仅靠代码段上方开并向上扩展。虽然可以使用mmap在堆和栈之间分配内存,但是这部分空间通常是没有使用内存的空 白地带。即使不区域研究有关的内核代码(不过我们还要继续这项工作),读者也能这些地址区间处置有相当好的了解。下面的端程序显示出了几个挑选出来的对象地址,他们的分出于三种不同区域之内。
由于种种原因,不能保证他的任何版本下工作,而且应该可以一直到所长是的大部分平台上
#include<stdio.h> /*printf().*/
#include<stdio.h> /*malloc().*/
static void
one(void *p.const char *description)
{printf("%10p: %s\n",p,description);}
int
main(void)
{int i;
int j;
one(&i,"A stack variable,\"i\"");
one(%j,"Next stack variable,\"j\"")
one((void*) one , "The function \"one\"");
one((void*)main,"The function \"main\"");
one(malloc(1),"First heap allocation");
one(malloc(1),"Second heap allocation");
return 0
}
在有的系统上,得到如下的数字。你的结果可能稍有不同,这与编译器,内核,gcc版本。即使完全不相同,也应与下面的结果相当接近。
0xbffff824: A stack variable,"i"
0xbffff820: Next stack variable,"j"
0x80484a0: The function 'one"
0x80484bc: The function "main"
ox80496f8: "First heap allocation"
0x8049708: Second heap allocation");
从这里可以看出,如果使用大概的数字的话,栈从接近0xC000000处开始并向下生长,代码从0X8000000处开始,而堆则如前所述从临近代码上部的地方开始并行上扩展。
4.(错误变量)
linux中采用“错误变”这一用语(error variable idiom)来辅助说明资源获取用语,它使用一个临时变量,来记录函数的期望返回值。相当多的函数能实现这个功能。但是错误变量的不同点在于它通常用来处理由于速度的变化而变得非常复杂的流程空中的问题。错误的变量有两个典型值,0表示成功和负数表示有错误
这两个用于结合使用,我们就可十分自然的得到复合模式的代码如下:
int f(void)
{ int err;
resource *r1.r2;
err=-ERR1 /*Assume failure.*/ r1=acquire_resourcel();
if(!r1) /*Not acquired.*/
goto out; / Returns -ERR1.*/
/*Got resource r1;try for r2.*/
err=ERR2; /*Assume failure.*/
r1=acquire_resource2();
if(!r2) /*Not acquired.*/
goto out1; / Returns -ERR2.*/
/*Have both r1 and r2 .*/
err=0; /No error.*/
/*...Use r1 and r2...*/
out2;
release resource(r2);
out1;
release resource(r1);
out :
return err;
(注意变量err是 使用错误变量的一个明确实例,同样,诸如out之类的标号则指明了资源获取用语的使用。)
5.(关于exec函数)
如果我们能够进行的所有工作只是fork(或者_clone),那么我们就一次次建立同一个进程的拷贝就可以了——这样我们LINUX系统就只能运行在系统中第一个创建的用户进程init了, init是很有用的,但功能没有如此强大;我们还需要处理其他事情。
在我们创建新进程以后,它通过调用exec就能够变成独立于其进程的进程了(这实际上不只是名为exec的函数;exec通常用做一个引用一系列函数的通用术语,所以这些函数基本上都是处理相同的事情,但是使用的参数稍微有些不同)。
因此,创建一个“真正”的进程-预期祖先不同的运行镜像任务分为两步,一步是fork,另一步是exec,最后能够得出下面风格的C代码:
/*Ignore the possibility of error below.*/
if(fork()){
/*I'lm the parent ; continue normally.*/}else
/*I'm the child * /
/*Become/some/other/program./
execl("/some/other/programa".
"/some/other/programa".)
}
(execl是ecec家族若干函数中的一个。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -