Linux FAQ(很杂)
时间:2007-04-17 来源:Echo CHEN
内核介绍和基本配置
(lkml指Linux kernel Mailing List)
1. Q: 什么是试验版内核?
```A: (lkml)Linux内核版本分为两个开发树:试验树(版本号为奇数,如:1.3.xx 或 2.1.x )和稳定树(版本号为偶数,如1.2.xx,2.0.xx)。试验树更新得很快,通常用于测试新的特性,算法,设备驱动程序,等等。试验树的内核也许会产生奇怪的操作,这样就有可能导致数据丢失,或者机器死锁等等。
2. Q: 什么是稳定版内核?
```A: (lkml)稳定树具有定义良好的特性,更少的bug,可靠的驱动程序。虽然稳定树的更新比实验树慢很多,通常有一些比别的版本好的"最佳版本"。Linux的发行版通常是基于这些"最佳版本",而不一定是最新的版本。
3. Q: 内核版本号为f.g.hhprei表示什么?
```A: (lkml)这是Linux版本f.g.hh的中间版。通常i < 5,但是也有例外,如2.0.34prei就具有i = 1 to 16个版本。有时候"pre"会被的Linux版本维护者的名字的简写所取代。如2.1.105ac4表示Alan Cox发布的内核版本2.1.105的第四个中间版。
4. Q: 我在哪里能下载Linux的最新内核源码?
```A: (lkml)Linux内核源码(试验树和稳定树)的主要站点是由Transmeta公司(Linus Torvalds工作的公司)维护的http://www.kernel.org/。这个站点在全球很多国家都有镜像站点。你可以通过链接http://www.CODE.kernel.org/来访问本国的镜像站点。这里,"CODE"是指国家代码。如"cn"是中国的国家代码,这样,中国的Linux内核主要镜像站点就是http://www.cn.kernel.org/。
```你也可以通过ftp访问ftp://ftp.CODE.kernel.org/pub/linux/kernel/来得到Linux内核的patch,这是Linus发布他的Linux内核的地方。其他的著名的Linux内核hacker在"people"目录下有自己的目录,他们可以在那里存放自己的内核patch。"testing"是linus存放自己的pre-release patches的地方。pre-release patches主要是其他的开发者使用的,以便他们可以与Linus的源码树保持同步。加上这些patch的内核可能比试验树的内核更加危险,它可能会 crash,甚至破坏你的文件系统。使用这些patch时,你需要特别地注意到这种风险性。
5. Q: 我在哪里能下载额外的Linux的内核的patch?
```A: (lkml)有很多地方提供不同的额外的patch,这些patch可以为Linux内核增加新的功能。这里是一个不错的站点。
6. Q: 什么是patch?
```A: patch文件(这里指Linux内核patch)是ASCII文本文件,它包含在原始代码和新代码之间的不同部分,以及一些附加的信息,如文件名,行号。通过patch程序(man patch)可以把这个patch加到一个现有的内核源码树上。
7. Q: 怎样制作Linux内核的patch?
```A: (lkml)你可以用diff程序(info diff)来制作patch。最简单的方式是在/usr/src目录下建立两个源码树,建立一个链接"/usr/src/linux",链接到修改后的源码树。然后再运行diff程序比较这两个源码树。文件/usr/src/Documentation/CodingStyle包含了更多的详细的信息,读一读这个文件。要记住:
|
Q:什么是mm?
A:这可不是meimei哦!指的是内存管理(memory management)。是内核中的一套软件机制,用于有效管理内存的使用。
Q:内存管理的软件实现了什么功能?
A:1) 进程的保护,即进程向内存管理请求的物理页,由内存管理系统负责,不会将同一物理页(共享页面除外)映射到不同进程的虚地址空间。
2) 虚存的实现,允许所有进程使用的内存总量大于实际物理内存总量。
Q:MMU与TLB的功能?
A:TLB是MMU的核心部件,用于实现页表转换,将虚拟地址转换成实地址。
对i386而言,MMU会根据CR3中的地址(注意,这个地址是物理地址)指定的页表目录一级一级去查询,在下面情况会抛出缺页中断:
* 页目录为空(NULL)
* 页表项为空,或者对应物理页不再内存中。
* 权限不满足,比如写带只读属性的页;用户态的代码访问特权属性的页。
Q:什么是cache?
A:对于硬件,指的是一块SRAM,由于它的访问速度比较快,所以将经常访问的数据放在这块SRAM中,必要时才写回到主存储器中,这样可以提高
CPU的使用效率;对于软件,指的是主存储器中的一块空间(即缓冲区),当CPU与慢速IO进行交换数据时,先将数据放在这块内存空间中,等系统
空闲时,才真正与IO进行数据交换。比如说,当用户请求将一块数据写入磁盘时,内核会先将数据放在缓冲区中,等到系统空闲或真正有必要马上
就写数据时,内核才会执行磁盘操作,将数据写到磁盘上。在读内核源代码时,经常会看到"xx_cache_xx"之类的字眼,其实基本上都是内存缓冲区。
Q:什么是COW?
A:我第一次看它,还以为是“母牛”呢!它实际上指的是“写时拷贝(Copy On Write)”,其主要目的是提高内存管理的效率。当内核创建进程时,
为其分配的虚拟页面并没有被真正映射到物理内存上,而是被映射到一块不可写(页表项上的标志为“只读”)的内存上(对于子进程,映射的是其父
进程的物理空间),当该进程要往里面写东东时,就会产生一个页面异常。在异常处理函数中会将这块虚拟页面映射到它应该映射的物理页面上。
Q:什么是伙伴(buddy)系统?
A:用伙伴算法(一般的数据结构书中都有讲解)管理物理内存的系统。它分配的物理内存大小都是页(4K)的整数倍。
Q:什么是slab分配器?
A:指的是内存管理单元先向伙伴系统申请一块较大的物理内存(批发),然后再将其肢解成许多小碎片分配给需要的进程(零售)。当有的进程需要
分配一小块内存(一般都小于一个页面,或不是页的整数倍),用来存放某个数据结构(如vm_area_struct),都可以向slab申请零售。
Q:什么是足印(footprint)?
A:足印(footprint)是指要完成某个工作,需要访问的缓存(一般指L1 DATA CACHE 或TLB)项的个数。
例如,完成的工作是:访问B. 但要访问B对象首先必须从A对象中获得B的指针,如果A,B在一个页面中,那么只需要在TLB踏上一个"足印"就可以了,
否则需要踏上两个“足印”;进一步的,如果A,B对象在同一个16或者32个字节对齐的空间里,那么在L1 DATA CACHE里只需要踏上一个足印就可以了。
一般完成某项工作在各种缓存上的足印越少,引起缓存失去命中的可能性就越小,缓存和内存之间的交互就越小,因此访问速度就越快。
Q:什么是active_mm?
A:active_mm表示当前与CPU关联的分页结构,对于普通进程,它等于mm,对于内核线程,它等于上一次用户进程的mm。
Q:什么是e820?
A:INT15的0xe820(=AX)号BIOS调用获取系统物理内存的配置表(e820map)。
......待续!
由于小弟水平有限,有什么不足之处,敬请指正!
Q:源代码中的gfp_xxx,gfp是什么的缩写?
A:是Get Free Page。
-- zeuszj
Q:cache_cache里的slab的大小是不是一个page?
A:cache_cache是专用slab管理器队列的头,在 kmem_cache_sizes_init() 的过程中cache_cache->gfporder应该一直都是0,所以是一个page;而这个队列中的各个slab管理器(kmem_cache_t)的gfporder都不一定等于0,所以不一定是一个page。
Q:kmalloc(size)返回的是物理地址,还是虚拟地址,为什么?
A:是虚拟地址。因为该地址是经过映射后的地址。
-- ytang
Q:怎样得到kmalloc分配的内存地址的实际物理地址?
A:kmalloc分配了一段物理上连续的内存空间,可以通过virt_to_phys()将返回的虚地址转换为一个物理地址,或者通过virt_to_bus() 转换为一个IO总线地址。
--jkl
Q:malloc()在内核中对应的函数是什么?
A:用户程序中使用的malloc是Glibc包装过的函数,它通过brk系统调用分配虚存给用户。
--xshell
Q:分配虚存空间有了mmap和munmap为什么还需要sys_brk?
A:其实,brk调用的系统调用是sys_brk,他通过mmap进行实现(可以这样认为)。但是,对于用户调用的malloc,free等函数,是由glibc实现的。即:库首先通过brk向系统申请一块大的虚存块,然后,这块内存由库进行管理,就是堆的管理由库实现。实际上,调用brk的目的无非是告诉内核这块内存的引用是合法,只有这样,缺页中断才能进行下去。如果没有这种机制,则可能导致应用程序对应的虚存块太多,使系统性能不佳。如果堆的管理在应用层进行,则没有这种坏处。
-- xiongjs
Q:请教一个术语--“堆”。
A:堆和栈合起来才是堆栈,堆是从低到高扩展的,栈是从高到低扩展的。局部变量都在栈里,通过malloc分配的内存都是在堆里的。
Q:malloc在程序结束不free会怎样?
A:进程退出后,与进程相关的所有资源都被内核无条件释放,进程一般不会在内核中造成资源溢漏。
malloc()基于进程的虚存映射区域,进程退出后,随着虚存映射的撤消,它所占用的物理页面也随之释放。
A:page->age与__PAGE_OLD标志位有何关系?
Q:和交换有关。
PAGE_OLD实际上是cpu自己设定的,每次被访问的页面会被cpu设为~PAGE_OLD,age是linux设定的,初始根据PAGE_OLD,然后不断改动。
--lucian_yao
A:我想阅读mm部分的源代码,从哪个函数入手?
Q:page_alloc.c和slab.c是内核用于分配内存的,可以先看。
--lucian_yao
......待续!
1)
Q:内核启动相关的代码有哪些?
A:以X86为例,
arch/i386/boot/bootsect.S 引导部分;
arch/i386/boot/setup.S,video.S 根据BIOS初始化硬件数据,进入保护模式,并加载内核映象;
arch/i386/kernelhead.S 页表初始化;
2)
Q:内核引导时可以加参数麽?有哪些参数可以加载?
A:可以.一般而言,就是在lilo引导指定内核时加参数,如root=/dev/hda??,参见
BootPrompt-Howto.
3)
Q:内核引导过程中为什么不能使用0-64K直接的地址区域?
A:保留给BIOS数据区,在启动过程中,启动代码使用BIOS数据区和相关的BIOS调用获取初始化必
需的硬件信息.但是在启动后期该区域可以被覆盖使用,这也就意味着内核启动之后不能够进行BIO
S
调用.
4)
Q:大内核与普通内核的分界线是多少K?
A:大小在508K之内的属于普通内核,可以直接加载到0x10000,然后解压缩到0x100000,否则加载到
0x100000后再原地解压.
5)
Q:508K数值是如何得到的?
A:
内核引导代码将自己从当前所在的位置0x7c00:0000跳到0x9000:0000处(即576K地址处)
继续运行.所以如果是xx普通内核则必然要求大小满足
576K - 64K(BIOS自动保留区) - 参数区4K(到底存放了什么有待商榷) = 508K 之内.
参数区存放系统引导时接收的命令行参数和BIOS传给内核的必要的硬件信息
6)
Q:如何编译链接不同类型的内核映象?
A:make bzimage与make zimage分别对应了大内核与普通内核的生成方式.
7)
Q:内核启动时传入的命令行参数存放在甚么位置?
A: ?????
8)
Q:内核启动初始时的内存如何布局?
A:
0A0000 +------------------------------+
| Reserved for BIOS | Do not use. Reserved for BIOS EBDA.
09A000 +------------------------------+
| Stack/heap/cmdline | For use by the kernel real-mode code.
098000 +------------------------------+
| Kernel setup | The kernel real-mode code.
090200 +------------------------------+
| Kernel boot sector | The kernel legacy boot sector.
090000 +------------------------------+
| Protected-mode kernel | The bulk of the kernel image.
010000 +------------------------------+
| Boot loader | <- Boot sector entry point 0000:7C00
001000 +------------------------------+
| Reserved for MBR/BIOS |
000800 +------------------------------+
| Typically used by MBR |
000600 +------------------------------+
| BIOS use only |
000000 +------------------------------+
ps,我在版面上找了找,没有发现可以作为FAQ的好帖子,以上是自己看启动部分并
查阅一些网络资料根据自己的理解给出的几篇FAQ,敬请各位批正与补充.
网络上有英文的可以参考的关于启动部分的FAQ么?哪位知道的话,给个链接,我可以
翻译几篇充实一下这一部分的FAQ内容.
to garycao,我下一部分将整理7. 内核模块的FAQ
Linux 内核编程
1. Q: linux 内核原代码汇编中 .align 伪指令的意思是什么?
```A: (rush)gas文档
```For example `.align 3' advances the location counter until it a
multiple of 8. If the location counter is already a multiple of 8, no
change is needed.
```比如.align 3,2的三次方就是8,也就是要对齐在8边界,比如你现在所在的byte是5,那.align 3之后那个变量就会在8,中间自动插了2个内容为null的byte。如果是在程序代码段中则会插入nop操作码,如果我没理解错的话。
2. Q: start_kernel里面的prof_shift干什么用的 ?
if (prof_shift) {
prof_buffer = (unsigned int *) memory_start;
/* only text is profiled */
prof_len = (unsigned long) &_etext - (unsigned long) &_stext;
prof_len >>= prof_shift;
memory_start += prof_len * sizeof(unsigned int);
memset(prof_buffer, 0, prof_len * sizeof(unsigned int));
}
prof_buffer分配做什么用的,prof在原代码里什么意思
```A: (xshell)prof means profile,也许译成"概要"比较合适,这里是内核对自身代码使用情况的一个统计
这段代码的意思是你是否想通过在启动命令行中设置profile="整型参数n"将内核分成prof_shift=n个部分以便检查内核各部分代码使用的频繁程度
prof_buffer当然是内核存放那些统计记录的地址区域
init/main.c中该函数用于从命令行接受启动参数profile,赋值给prof_shift
135 static int __init profile_setup(char *str)
136 {
137 int par;
138 if (get_option(&str,&par)) prof_shift = par;
139 return 1;
140 }
与之相关的有/proc/profile and /usr/sbin/readprofile,当然必需在启动时加profile=integer,否则没有的啊
3. Q: System.map中的几个标志T,t,B,b,..的问题 ?
```A: (ytang)refer from binutils documents:
The symbol type. At least the following types are used; others are,
as well, depending on the object file format. If lowercase, the symbol
is local; if uppercase, the symbol is global (external).
A
The symbol's value is absolute, and will not be changed by further linking.
B
The symbol is in the uninitialized data section (known as BSS).
C
The symbol is common. Common symbols are uninitialized data. When
linking, multiple common symbols may appear with the same name. If the
symbol is defined anywhere, the common symbols are treated as undefined
references. For more details on common symbols, see the discussion of
-warn-common in Linker options.
D
The symbol is in the initialized data section.
G
The symbol is in an initialized data section for small objects.
Some object file formats permit more efficient access to small data
objects, such as a global int variable as opposed to a large global
array.
I
The symbol is an indirect reference to another symbol. This is a
GNU extension to the a.out object file format which is rarely used.
N
The symbol is a debugging symbol.
R
The symbol is in a read only data section.
S
The symbol is in an uninitialized data section for small objects.
T
The symbol is in the text (code) section.
U
The symbol is undefined.
V
The symbol is a weak object. When a weak defined symbol is linked
with a normal defined symbol, the normal defined symbol is used with no
error. When a weak undefined symbol is linked and the symbol is not
defined, the value of the weak symbol becomes zero with no error.
W
The symbol is a weak symbol that has not been specifically tagged
as a weak object symbol. When a weak defined symbol is linked with a
normal defined symbol, the normal defined symbol is used with no error.
When a weak undefined symbol is linked and the symbol is not defined,
the value of the weak symbol becomes zero with no error.
4. Q: 为什么需要copy_from_user ?
```A: (xshell)
```1.
copy_from_user中的fixup的作用是为了修补当缺页异常在中断上下文中发生时保证copy_from_user的正常返回,其返回值为尚
未成功copy的字节数,如果在非中断上下文的情况下,发生用户空间或内核空间地址缺页异常仍然按照一般的缺页异常的处理方式调页.所以
copy_from_user在使用上具有通用性
```2. 内核地址空间中可以发生缺页异常,但是并不真正地调页,这仅仅是为了遵循MMU的缺页机制而已,同时也做一些缺页检查,这一点可以在fault.c中的do_page_fault()的vmalloc_fault:中找到答案
5. Q: wmb是干吗的函数 ?
```A: (xshell)在指令序列中放一个wmb的效果是使得指令执行到该处时,把所有缓存的数据写到该写的地方,同时使得wmb前面的写指令一定会在wmb的写指令之前执行
6. Q: 宏#与##有什么区别 ?
```A: (jkl)宏定义中的"#"前缀将参数替换成字符串,例如
#define test(x) #x
test(123) 被替换成字符串"123";
"##"用于连接参数,例如
#define test(x,y) ##x##y
test(123,456) 替换成123456;
因此BI(0x0,0)替换成BUILD_IRQ(0x00),"pushl $"#nr"-256\n\t"替换成"pushl $""0x00""-256\n\t"
cpp.info手册对此有详细说明。
7. Q: __attribute__是什么意思?
```A: (jkl)__attribute__是gcc的关键字,用以描述变量属性,如:
__attribute__((regparm(0))) int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1,
内核调试
1. Q: 有人熟悉linux内核调试技术吗?
```A: (garycao)有以下几种方法:
|
2)));禁止printk使用寄存器传递调用参数,并将printk的参数1作为printf格式串,从参数2开始检查其类型;详细信息
8. Q: 请问_end在那儿定义的 ?
(2.0.34里的arch\i386\kernel\setup.c中的_end变量line number 162)
```A: (jkl)_end是连接器ld定义的,每个ELF格式的应用程序都可以使用此符号。参见vmlinux.lds文件.
```ld的脚本里面可以定义许多东西。info ld可以了解ld脚本的编写。
9. Q: .prvious是什么意思 ?
```A: (jkl).previous伪指令恢复当前段的前一个段作为当前段,由于ELF中允许用.section自定义段,这里的.previous作用就是恢复.text作为当前段。
```或许应该说恢复到当前.section定义之前的段作为当前段。
10. Q: volatile是什么意思 ?
````A: (onfirelinux)volatile指一个变量可能随时由于外界地变化而变化。详细信息
11. Q: 什么是信号 ?
````A: (sirx)信号是UNIX进程间通信的一种标准方式,在最早期的UNIX系统中已经存在。信号的出现允许内核和其它进程通知进程特定事件的发生。现代UNIX中也存在其它的进程间通信方式,但由于信号相对简单和有效,它们仍然被广泛使用。 详细信息
硬件相关
1. Q: 磁盘扇区与磁盘块如何定义以及如何区分使用?
```A: (m.ouyang)Basically I think, hard sector is a physical parameter got from device, block size is just block size, when fs layer transfers requests to ide drivers , all info are in sector expression, anyway, block size setting will affect io requests. For example, if you doubled block size, you may cut io request times(ide interrupts) to nearly a half. So in your own ide/atapi driver, you can define the block size as you want, but not sector size, only keep it as multiples of sector size.
网络
1. Q: 怎样才能快速找到connect,socket函数实现的代码?
```A: (nxin)下载并解开glibc的源码,socket的实现在sysdeps/unix/sysv/linux/i386/socket.S中, linux的大部分系统调用都在sysdeps/unix/sysv/linux/ 或sysdeps/unix/sysv/linux/i386/ 中。系统调用最后都调用了int $0x80,有些共用同一个入口,比如socket,connect都调sys_socketcall,但select调sys_select。
2. Q: 请教高手:Lan网卡收到包后,应该比较mac地址?
```A: (nxin)网卡可以工作在几种模式之下,比如是否接收广播,是否接收指定MAC地址,是否接收指定多播地址,是否全部接收,一般情况不设为全部接收状态,因为这样会加重系统负担,如果你需要可以设为混杂模式就可以全部接收了。
进程管理及线程相关
1. Q: 如何在内核中唤醒和睡眠用户进程?
```A: (xshell)你可以参考interruptible_sleep_on和wake_up_interruptible的代码实现对指定进程的睡眠与唤醒,
其中,使用interruptible_sleep_on将当前进程置入睡眠态和一睡眠进程管理队列中,该队列中的进程可被中断唤醒,wake_up_interruptible则唤醒睡眠进程管理队列中的进程。 详细信息
2. Q: 如read, 用户没输入时候,系统调用阻塞。此时候进程(进程1)是否退出了核心执行态,进入suspend,由内核重新调度其他进程(进程2)运行;那先前的进程1在用户输入时如何又再次获得cpu呢??是等进程2 的时间片用完,重新调度吗 ?
```A: (linux_tao)进程在核心态执行系统调用,系统调用阻塞时进程转入内存睡眠状态,内核调度其他进程运行。当睡眠进程等待的事件发生时,该进程被唤醒,转为内存就绪。之后它被调度,进入“核心态运行”状态。在此状态下,继续完成read调用。read调用完成后,返回用户态运行。
```(sirx)不需要等到进程2的时间片用完。如果进程1等待的中断发生的时候进程2正在执行系统调用,那么需要__等到进程2的系统调用执行完__再重新调度。如果中断发生的时候进程2正在用户态运行,马上重新调度。 详见Linux 内核笔记2 – 进程调度
3. Q: linux中线程是不是属于内核实现的?或者是创建线程在用户级,管理在内核?
```A: (sirx)linux下的线程有用户态和内核态两种,但内核只创建和管理内核线程,用它们来完成一些需要经常重复执行的工作。用户线程,就是应用程序内部的线程啦,由用户态的线程库来生成和调度,内核从来就不知道用户线程的存在。
4. Q: 在linux下当一个进程创建了若干个线程的时候
a、这时在主进程(非线程中)内调用fork,那么子进程是如何继承父进程的线程的?是全部还是不继承?
b、若在主进程的某个线程内fork,这时这个子进程是继承父进程的全部线程还是只继承fork它的线程或是不继承?
```A: (待答)
5. Q.在当前系统下,调度时间片的长度是多少?
```A: (sirx) 与2.2.x版的内核相比,kernel2.4.x的时间片长度缩短了,对于最高优先级的进程来说,时间片的长度为100ms,默认优先级进程的时间片长度为60ms,而最低优先级进程的时间片长度为10ms。
6. Q. Linux如何保证对I/O事件相对比较快的响应速度,这个响应速度是否与调度时间片的长短有关?
```A: (sirx)当I/O事件发生的是时候,对应的中断处理程序被激活,当它发现有进程在等待这个I/O事件的时候,它会激活等待进程,并且设置当前正在执行进程的need_resched标志,这样在中断处理程序返回的时候,调度程序被激活,原来在等待I/O事件的进程(很可能)获得执行权,从而保证了对 I/O事件的相对快速响应(毫秒级)。
从上面的说明可以看出,在I/O事件发生的时候,I/O事件的处理进程会抢占当前进程,响应速度与调度时间片的长度无关。
7. Q.高优先级(nice)进程和低优先级进程在执行上有何区别?例如一个优先级为-19(最高优先级)的进程和优先级为20(最低)的进程有何区别
```A: (sirx)进程获得的CPU时间的绝对数目取决于它的初始counter值,初始的counter的计算公式(sched.c in kernel 2.4.14)如下:
p->counter = (p->counter >> 1) + ((20 - p->nice) >> 2) +1)
由公式可以计算出,对于标准进程(p->nice 为0), 得到的初始counter为6,即进程获得的时间片为60ms。
最高优先级进程(nice为-19)的初始counter值为10,进程的时间片为100ms。
最低优先级进程(nice为20)的初始counter值为1,进程时间片为10ms。
结论是最高优先级进程会获得最低优先级进程10倍的执行时间,普通优先级进程接近两倍的执行时间。当然,这是在进程不进行任何IO操作的时候的数据,在有IO操作的时候,进程会经常被迫睡眠来等待IO操作的完成,真正所占用的CPU时间是很难比较的。
我们可以看到每次重新计算counter的时候,新的counter值都要加上它本身剩余值的一半,这种奖励只适用于通过SCHED_YIELD 主动放弃CPU的进程,只有它在重新计算的时候counter值没有用完,所以在计算后counter值会增大,但永远不可能超过20。
8. Q: Linux提供的同步机制有那些?
```A: (garycao)(see /usr/include/目录下semaphore.h,pthread.h)
一般linux信号量分为以下几类
1. semaphore:
int sem_init (sem_t *sem, int pshared,
unsigned int value);
int sem_wait (sem_t * sem);
int sem_post (sem_t * sem);
int sem_destroy (sem_t * sem);
2. mutex
pthread_mutex_init
pthread_mutex_lock
pthread_mutex_unlock
pthread_mutex_destroy
3.Condition Variables
pthread_cond_init
pthread_cond_wait
pthread_cond_signal
pthread_cond_broadcast
pthread_cond_destroy
内核同步
1. Q: 对于单cpu的系统,是不是不应该用spin_lock。如果用了,会不会降低系统的性能呢?
```A: (minihorse)
> I have a question related to spin locking on UP systems.Before that I would
> like to point out my understanding of the background stuff
> 1. spinlocks shud be used in intr handlers
It should be used in the interrupt handler, if you need to prevent any race
conditions with other interrupt/non-interrupt context code that may be
executing on some other CPU on an SMP system. Thus spinlocks need to be held
for as short a duration as possible. You would need to use the
spin_lock_irqsave/spin_unlock_irqrestore variant pair to prevent your
interrupt handler from running on the same processor while holding the lock.
This may be needed if the interrupt handler may try to acquire the same lock
thus causing a deadlock.
> 2. interrupts can preempt kernel code
> 3. spinlocks are turned to empty when kernel is compiled without SMP
> support.
>
> If a particular driver is running( not the intr handler part) and at this
> time an interrupt occurs. The handler has to be invoked now. Won't the
> preemption cause race conditions/inconsistencies? Is any other mechanism
> used? Pl correct me if I have not understood any part of this correctly
On a UP kernel the spin_lock_irqsave/spin_unlock_irqrestore pair expand to
save_flags(flag); cli()/restore_flags(flag).
The masking of interrupts on the processor between spin_lock_irqsave and
spin_unlock_irqrestore pair prevent the user context code from being
preempted by the interrupt handler.
进程管理及线程相关
>>>2. Q: 如read, 用户没输入时候,系统调用阻塞。此时候进程(进程1)是否退出了核心执行态,进入suspend,由内核重新调度其他进程(进程2)运行;那先前的进程1 在用户输入时如何又再次获得cpu呢??是等进程2 的时间片用完,重新调度吗 ?
>>>```A: (linux_tao)进程在核心态执行系统调用,系统调用阻塞时进程转入内存睡眠状态,内核调度其他进程运行。当睡眠进程等待的事件发生时,该进程被唤醒,转为内存就绪。之后它被调度,进入“核心态运行”状态。在此状态下,继续完成read调用。read调用完成后,返回用户态运行。
补充一点:
>>>是等进程2 的时间片用完,重新调度吗 ?
不需要等到进程2的时间片用完。如果进程1等待的中断发生的时候进程2正在执行系统调用,那么需要__等到进程2的系统调用执行完__再重新调度。如果中断发生的时候进程2正在用户态运行,马上重新调度。
---老王开始卖瓜:
俺前几天写的一篇文章有更详细的相关内容:
<< Linux 内核笔记2 – 进程调度 >>
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=294463&page=1&view=collapsed&sb=5&o=7&fpart=
>>>3. Q: linux中线程是不是属于内核实现的?或者是创建线程在用户级,管理在内核?
>>>```A: (待答)
A. linux下的线程有用户态和内核态两种,但内核只创建和管理内核线程,用它们来完成一些需要经常重复执行的工作。用户线程,就是应用程序内部的线程啦,由用户态的线程库来生成和调度,内核从来就不知道用户线程的存在。