Chinaunix

标题: 如果全局变量是由寄存器指向的,那么CPU怎么会有那么多寄存器呢? [打印本页]

作者: escapedog    时间: 2007-08-14 09:27
标题: 如果全局变量是由寄存器指向的,那么CPU怎么会有那么多寄存器呢?
我一直以为全局变量是被禁止优化的,可是忽然听到有全局变量参与线程现场切换的说法。迷惑ing......

如果说全局变量是由寄存器指向的,那么如果全局变量的数目超过 CPU 的寄存器数目怎么办呢?

高手能解答一下么?

谢谢了!
作者: flw    时间: 2007-08-14 09:39
全局变量是在内存中的,
而在段页式管理的 OS 中,
段描述符和页表基地址都是储存在寄存器中的,
任务切换的时候,
寄存器是现场的一部分,
也要切换(事前保护和事后恢复),
因此全局变量自然也是现场的一部分。
作者: 思一克    时间: 2007-08-14 09:40
TASK切换pgd不就是切换到自己私有的虚拟内存里面有自己的全局变量吗。
全局变量是TASK私有的。
一个PROCESS的各THREAD因为共享才使得各THREAD可见。但是CPU不是在各个THREAD间轮换,而是将每一个THREAD当TASK切换(从其他进程也可以切换过来)。
作者: flw    时间: 2007-08-14 09:44
原帖由 思一克 于 2007-8-14 09:40 发表
TASK切换pgd不就是切换到自己私有的虚拟内存里面有自己的全局变量吗。
全局变量是TASK私有的。
一个PROCESS的各THREAD因为共享才使得各THREAD可见。但是CPU不是在各个THREAD间轮换,而是将每一个THREAD当TASK ...

你我对 windows 都不太熟悉,也许 windows 是这样?把进程调度和线程调度分开?
呵呵。
作者: escapedog    时间: 2007-08-14 09:48
原帖由 思一克 于 2007-8-14 09:40 发表
TASK切换pgd不就是切换到自己私有的虚拟内存里面有自己的全局变量吗。
全局变量是TASK私有的。
一个PROCESS的各THREAD因为共享才使得各THREAD可见。但是CPU不是在各个THREAD间轮换,而是将每一个THREAD当TASK ...


现代的操作系统的任务切换大多是线程级的,除非发现这个线程属于另外一个进程,才切换进程空间。和是不是windows 关系不大。
作者: 思一克    时间: 2007-08-14 09:48
也有可能。但必须说清楚。你我也不能仅仅凭想象

原帖由 flw 于 2007-8-14 09:44 发表

你我对 windows 都不太熟悉,也许 windows 是这样?把进程调度和线程调度分开?
呵呵。

作者: 思一克    时间: 2007-08-14 10:09
LINUX上内河将PROCESS和THREAD几乎一样对待。

THREAD不是在一个进程中自己换来换去。即使象你说的不用,也有从其他TASK切换过来的问题,也要换PGD

原帖由 escapedog 于 2007-8-14 10:07 发表



进程切换要切换地址空间,线程不用,根本就不是一回事儿。不是我心目中,是在若干 kernel 程序员的心目中!

作者: flw    时间: 2007-08-14 10:09
原帖由 escapedog 于 2007-8-14 10:07 发表

不是我心目中,是在若干 kernel 程序员的心目中!

请问是何 kernel?
作者: flw    时间: 2007-08-14 10:10
原帖由 flw 于 2007-8-14 10:09 发表

请问是何 kernel?

哦,不好意思,我不问了。是你们公司的商业机密,你一定不肯说的。抱歉。
作者: escapedog    时间: 2007-08-14 10:12
原帖由 思一克 于 2007-8-14 10:09 发表
LINUX上内河将PROCESS和THREAD几乎一样对待。

THREAD不是在一个进程中自己换来换去。即使象你说的不用,也有从其他TASK切换过来的问题,也要换PGD



天下的操作系统是不是只有 linux 一种?
作者: escapedog    时间: 2007-08-14 10:12
原帖由 flw 于 2007-8-14 10:10 发表

哦,不好意思,我不问了。是你们公司的商业机密,你一定不肯说的。抱歉。


说了你也理解不了,别给自己找台阶下了!
作者: 思一克    时间: 2007-08-14 10:15
没有一种,是多中。但必须有实例来说明。我拿LINUX说明,你可以用其他的来说明。
如果什么也不用,自己想象不可以。对吗

原帖由 escapedog 于 2007-8-14 10:12 发表


天下的操作系统是不是只有 linux 一种?

作者: escapedog    时间: 2007-08-14 10:16
原帖由 思一克 于 2007-8-14 10:15 发表
没有一种,是多中。但必须有实例来说明。我拿LINUX说明,你可以用其他的来说明。
如果什么也不用,自己想象不可以。对吗






QUOTE:
原帖由 flw 于 2007-8-14 10:09 发表

请问是何 kernel?
哦,不好意思,我不问了。是你们公司的商业机密,你一定不肯说的。抱歉。




斑竹我可以投诉么?

这不是讨论问题的态度,对不?

[ 本帖最后由 escapedog 于 2007-8-14 10:25 编辑 ]
作者: coldwarm    时间: 2007-08-14 10:25
倾向于escapedog 的说法,任务切换时保存的应该是描述任务的数据结构,而不是任务本身。对于全局变量,只要进程没有退出,它就应当一直在那儿存在着,当然也有可能通过换页操作换出物理内存,但无论如何它与任务的切换没什么直接关系。
作者: escapedog    时间: 2007-08-14 10:27
原帖由 coldwarm 于 2007-8-14 10:25 发表
倾向于escapedog 的说法,任务切换时保存的应该是描述任务的数据结构,而不是任务本身。对于全局变量,只要进程没有退出,它就应当一直在那儿存在着,当然也有可能通过换页操作换出物理内存,但无论如何它与任务 ...


谢谢。

进程目前通常被认为是 resource holder, 而不是切换的基本单位。
作者: coldwarm    时间: 2007-08-14 10:33
原帖由 escapedog 于 2007-8-14 10:27 发表

进程目前通常被认为是 resource holder, 而不是切换的基本单位。


我是针对linux来说的,有的书里将进程定义为资源申请的基本单位,线程定义为任务切换的基本单位,但对linux而言,它的线程就是通过task_struct这个进程描述符结构来实现的。
作者: escapedog    时间: 2007-08-14 10:35
原帖由 coldwarm 于 2007-8-14 10:33 发表


我是针对linux来说的,有的书里将进程定义为资源申请的基本单位,线程定义为任务切换的基本单位,但对linux而言,它的线程就是通过task_struct这个进程描述符结构来实现的。


linux 的我大概听人讲过一些,确实有些与众不同的地方。
作者: flw    时间: 2007-08-14 10:35
to coldwarm:
你的观点也是错的。
作者: coldwarm    时间: 2007-08-14 10:54
原帖由 flw 于 2007-8-14 10:35 发表
to coldwarm:
你的观点也是错的。


麻烦老大指出来哪里有问题。


  1. kernel/sched.c
  2. 1048 /*
  3. 1049 * context_switch - switch to the new MM and the new
  4. 1050 * thread's register state.
  5. 1051 */
  6. 1052 static inline
  7. 1053 task_t * context_switch(runqueue_t *rq, task_t *prev, task_t *next)
  8. 1054 {
  9. 1055   struct mm_struct *mm = next->mm;
  10. 1056   struct mm_struct *oldmm = prev->active_mm;
  11. ...
  12. 1063     switch_mm(oldmm, mm, next);
  13. ...
  14. 1072   switch_to(prev, next, prev);
  15. 1073
  16. 1074   return prev;
  17. 1075 }

  18. /include/asm-i386/mmu_context.h
  19. 026  static inline void switch_mm(struct mm_struct *prev,
  20. 027     struct mm_struct *next,
  21. 028     struct task_struct *tsk)
  22. 029  {
  23. 030   int cpu = smp_processor_id();
  24. 031
  25. 032   if (likely(prev != next)) {
  26. 033    /* stop flush ipis for the previous mm */
  27. 034    cpu_clear(cpu, prev->cpu_vm_mask);
  28. 035  #ifdef CONFIG_SMP
  29. 036    cpu_tlbstate[cpu].state = TLBSTATE_OK;
  30. 037    cpu_tlbstate[cpu].active_mm = next;
  31. 038  #endif
  32. 039    cpu_set(cpu, next->cpu_vm_mask);
  33. 040
  34. 041    /* Re-load page tables */
  35. 042    load_cr3(next->pgd);
  36. 043
  37. 044    /*
  38. 045    * load the LDT, if the LDT is different:
  39. 046    */
  40. 047   if (unlikely(prev->context.ldt != next->context.ldt))
  41. 048     load_LDT_nolock(&next->context, cpu);
  42. 049   }
  43. 050  #ifdef CONFIG_SMP
  44. 051   else {

  45. /include/asm-i386/system.h
  46. 012  extern struct task_struct * FASTCALL(__switch_to(struct task_struct *prev, struct
  47. task_struct *next));

  48. 015  #define switch_to(prev,next,last) do {     \
  49. 016   unsigned long esi,edi;       \
  50. 017   asm volatile("pushfl\n\t"       \
  51. 018   "pushl %%ebp\n\t"        \
  52. 019   "movl %%esp,%0\n\t"  /* save ESP */    \
  53. 020   "movl %5,%%esp\n\t"  /* restore ESP */    \
  54. 021   "movl $1f,%1\n\t"   /* save EIP */   \
  55. 022   "pushl %6\n\t"   /* restore EIP */   \
  56. 023   "jmp __switch_to\n"        \
  57. 023   "1:\t"          \
  58. 024   "popl %%ebp\n\t"        \
  59. 025   "popfl"         \
  60. 026   :"=m" (prev->thread.esp),"=m" (prev->thread.eip),  \
  61. 027   "=a" (last),"=S" (esi),"=D" (edi)     \
  62. 028   :"m" (next->thread.esp),"m" (next->thread.eip),   \
  63. 029   "2" (prev), "d" (next));       \
  64. 030  } while (0)

复制代码

这段是上下文切换的代码。它保存的只是对任务的当前状态的描述。从操作系统的角度来看,在这个层次上,它所认知的单位仅仅是页面,至于页面上到底存放的是什么,它根本就不知道,那只能由应用程序来解释。
作者: 0521    时间: 2007-08-14 11:03
原帖由 coldwarm 于 2007-8-14 10:54 发表


麻烦老大指出来哪里有问题。


kernel/sched.c
1048 /*
1049 * context_switch - switch to the new MM and the new
1050 * thread's register state.
1051 */
1052 static inline
1053 task_t *  ...


兄台的 linux 似乎很熟,能不能找机会交流一下?
站内短信联系行不?
作者: flw    时间: 2007-08-14 11:06
原帖由 coldwarm 于 2007-8-14 10:54 发表

这段是上下文切换的代码。它保存的只是对任务的当前状态的描述。从操作系统的角度来看,在这个层次上,它所认知的单位仅仅是页面,至于页面上到底存放的是什么,它根本就不知道,那只能由应用程序来解释。

对。你说的没错。
作者: bluster    时间: 2007-08-14 11:19
原帖由 escapedog 于 2007-8-14 09:27 发表
我一直以为全局变量是被禁止优化的,可是忽然听到有全局变量参与线程现场切换的说法。迷惑ing......

如果说全局变量是由寄存器指向的,那么如果全局变量的数目超过 CPU 的寄存器数目怎么办呢?

高手能解答 ...

据我所知,在一些risc上面可以用一个寄存器来对全局变量寻址,不知道这是不是所谓寄存器指向。
另外,全局变量不是volatile的,也是有存取优化的。只是相比局部变量,优化的要少。
作者: jamesr    时间: 2007-08-14 11:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: mik    时间: 2007-08-14 12:08
原帖由 flw 于 2007-8-14 09:39 发表
全局变量是在内存中的,
而在段页式管理的 OS 中,
段描述符和页表基地址都是储存在寄存器中的,
任务切换的时候,
寄存器是现场的一部分,
也要切换(事前保护和事后恢复),
因此全局变量自然也是现场的 ...


正确来说:应该是描述符表。
作者: flw    时间: 2007-08-14 12:19
原帖由 mik 于 2007-8-14 12:08 发表

正确来说:应该是描述符表。

对。应该是描述符表。

另外还有段选择子。
作者: mik    时间: 2007-08-14 12:28
分页是建立在段式之上的。
所有的变量都须参考到段描述符,只有 GDT 和 IDT 是全局可用的,不需切换!
就连保存执行环境的 TSS 块也需要进行切换
作者: la.lune    时间: 2007-08-14 15:10
原帖由 flw 于 2007-8-14 10:35 发表
to coldwarm:
你的观点也是错的。


我也倾向于coldwarm的观点 全局变量 与 进程切换没有必然联系
一直没等到下文
版主老大能不能指出错处 举一两个具体例子说明一下
学习了 谢谢
作者: flw2    时间: 2007-08-14 15:28
原帖由 coldwarm 于 2007-8-14 10:33 发表


我是针对linux来说的,有的书里将进程定义为资源申请的基本单位,线程定义为任务切换的基本单位,但对linux而言,它的线程就是通过task_struct这个进程描述符结构来实现的。


linux好像也是这样的呀,调度是基于task_struct,而这个结构说白了也就是个线程,是调度的单位。线程之间共享文件,地址空间等资源。
作者: 圆点坐标    时间: 2007-08-14 22:46
这个全局变量不分kernel和应用层?还有说的kernel的线程调度还是线程库自己的线程调度?
作者: 圆点坐标    时间: 2007-08-14 22:49
一般来说进程就是资源的container,调度是以线程为单位,包括linux,只是linux的线程的描述结构和进程的描述结构一样。
作者: 蚊见蚊爱    时间: 2007-08-14 23:27
进程在Windows中不是调度单位,线程才是调度单位。

全局变量不是寄存器指向的。
作者: woshiwo    时间: 2007-08-14 23:43
进程线程模型对于不同操作系统差别很大。
对于Linux来说,进程和内核线程是一对一,或者说是同一个粒度的,是内核调度的基本单位。用户线程是基于lib库实现的,并不直接接受内核调度。
solaris的LWP和用户线程是一对一,但与Linux不同,可以接受内核调度。
作者: mantiser    时间: 2007-08-15 01:06
这种问题也拿出来问。。

任务,线程,进程。。

在嵌入式系统中,大部分只有任务的概念,一个任务就是一个调度单元。
作者: bebeowulf2006    时间: 2007-08-15 10:14
进程线程模型对于不同操作系统差别很大。
对于Linux来说,进程和内核线程是一对一,或者说是同一个粒度的,是内核调度的基本单位。用户线程是基于lib库实现的,并不直接接受内核调度。


同意,是1:1 还是M:N还是有关系的。
作者: Cocal    时间: 2007-08-15 10:15
原帖由 escapedog 于 2007-8-14 09:27 发表
我一直以为全局变量是被禁止优化的,可是忽然听到有全局变量参与线程现场切换的说法。迷惑ing......

如果说全局变量是由寄存器指向的,那么如果全局变量的数目超过 CPU 的寄存器数目怎么办呢?


你说的优化是全局变量地址优化在通用寄存器里吗? 那么编译器当然知道CPU有多少个可用寄存器,不够用了就不优化呗,还能怎样?
作者: 莫闲云    时间: 2007-08-15 11:02
这个问题是这样的. 为了提高效率(寄存器的访问速度比内存的访问速度要高). 所以在优化后, 有可能某一线程在某一时刻对全局变量修改时不会立即将这个值写入内存, 而是暂放在寄存器中. 之后一些操作如果对全局变量 进行计算时, 是直接对这个寄存器进行操作. 这样运算速度就会被优化. 当时一段操作结束后, 不再对这个全局变量进行操作(比如函数返回时), 代码才会把寄存器真正写入内存当中.

这样会有这么一个问题, 当一个线程修改某一个全局变量时, 可能会把值暂放在某一个寄存器中. 这样在另一个线程 会观察不到这个全局变量发生变化. 可能带个同步上的问题.

另外这种优化并不是说 在同一时刻 对所有的全局变量 暂存的. 不会有那么多寄存器. 只是暂存其中 在当时使用最频繁的几个全局变量.
作者: flw2    时间: 2007-08-15 12:32
原帖由 woshiwo 于 2007-8-14 23:43 发表
进程线程模型对于不同操作系统差别很大。
对于Linux来说,进程和内核线程是一对一,或者说是同一个粒度的,是内核调度的基本单位。用户线程是基于lib库实现的,并不直接接受内核调度。
solaris的LWP和用户线程 ...


http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html


K. Internals of LinuxThreads
K.1: What is the implementation model for LinuxThreads?
LinuxThreads follows the so-called "one-to-one" model: each thread is actually a separate process in the kernel. The kernel scheduler takes care of scheduling the threads, just like it schedules regular processes. The threads are created with the Linux clone() system call, which is a generalization of fork() allowing the new process to share the memory space, file descriptors, and signal handlers of the parent.

Advantages of the "one-to-one" model include:

    * minimal overhead on CPU-intensive multiprocessing (with about one thread per processor);
    * minimal overhead on I/O operations;
    * a simple and robust implementation (the kernel scheduler does most of the hard work for us).

The main disadvantage is more expensive context switches on mutex and condition operations, which must go through the kernel. This is mitigated by the fact that context switches in the Linux kernel are pretty efficient.




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2