Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1711554
  • 博文数量: 143
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1462
  • 用 户 组: 普通用户
  • 注册时间: 2016-08-23 11:14
文章分类

全部博文(143)

文章存档

2022年(3)

2021年(13)

2020年(21)

2019年(8)

2018年(28)

2017年(7)

2016年(63)

我的朋友

分类: LINUX

2018-12-09 17:18:07

一、介绍:

选自:认识cpu、核与线程

1、物理核:物理核数量=cpu(机子上装的cpu的数量)*每个cpu的核心数

2、逻辑核:所谓的48线程,4核指的是物理核心。通过超线程技术,用一个物理核模拟两个虚拟核,每个核两个线程,总数为8线程。在操作系统看来是8个核,但是实际上是4个物理核。

通过超线程技术可以实现单个物理核实现线程级并行计算,但是比不上性能两个物理核。---超线程技术实现了单个物理核心同时两个线程,也就是别人常说的虚拟内核数。比如单物理核心实现的双线程,它同时可以处理两个线程,它的物理核心数其实是是1个,通过Hyperthreading技术实现的线程级并行(Thread Lever Parallelism)


点击(此处)折叠或打开

  1. 并发(Concurreny)与并行(Parallelism)
  2. 小明可以一边玩手机一边看电视,但是事实上,他的眼睛在看电视的时候不能看手机,他在看手机没法盯着屏幕,他的眼睛飞快在两个屏幕上切换。这不是真正意义上的同时进行,但又是客观存在同时进行两件事,这叫并发。
  3. 但是小明可以一天坐公交一边听音乐,这两件事同时进行互不干扰,做到真正意义的同步同时进行,这叫并行。


3、进程是操作系统进行资源(包括cpu、内存、磁盘IO等)分配的最小单位。

4、线程是cpu调度和分配的基本单位。即,多进程中每个进程有自己的地址空间,而同一进程的多线程则共享地址空间。


二、绑定

对于普通的应用,操作系统的默认调度机制是没有问题的。但是,当某个进程需要较高的运行效率时,就有必要考虑将其绑定到单独的核上运行,以减小由于在不同的核上调度造成的开销。把某个进程/线程绑定到特定的cpu核上后,该进程就会一直在此核上运行,不会再被操作系统调度到其他核上。但绑定的这个核上还是可能会被调度运行其他应用程序的。

可以使用多种方法把进程/线程指定到特定的cpu核上运行。见网络。


三、查看:

参看:LinuxCPU亲和性(affinity)

1、  Linux下查看CPU相关信息

2、  Linux查看某个进程运行在哪个逻辑核上

...

3、  Linux查看某个线程运行在哪个逻辑核上



四、有趣的问题

单核单线程的CPU中,当一个空的死循环使CPU达到100%时,CPU还能做其它事嘛?如:输入或者run另一个进程。于是,我在一个单物理核单逻辑核的CPU上,测试各种死循环:

0、环境信息:

点击(此处)折叠或打开

  1. /tmp # cat /proc/cpuinfo
  2. system type : QCA955x
  3. cpu MHz : 720
  4. processor : 0
  5. cpu model : MIPS 74Kc V5.0
  6. BogoMIPS : 359.42
  7. wait instruction : yes
  8. microsecond timers : yes
  9. tlb_entries : 32
  10. extra interrupt vector : yes
  11. hardware watchpoint : yes, count: 4, address/irw mask: [0x0000, 0x0ff8, 0x0ff8, 0x0ff8]
  12. ASEs implemented : mips16 dsp
  13. shadow register sets : 1
  14. core : 0
  15. VCED exceptions : not available
  16. VCEI exceptions : not available

  17. /tmp #
1、空的死循环

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int main(char argc, char *argv[])
  3. {
  4.     while(1)
  5.     {
  6.     }
  7.     return 0;
  8. }
[CPU: 96.0%左右 usr  0.5%左右 sys  0.0% idle][R 90%左右 ./while1.out](即CPU占用率100%)。

---空的死循环(即在循环中没有调用sleep()或者阻塞函数[阻塞函数]),并未占尽所有时间片,Linux操作系统还是会按时间片轮转执行其他进程的线程,只不过它线程得到操作系统切换过去执行的可能性极低。相反需要知道,进程在sleep()或阻塞的时候是不占用CPU的时间的。

2、调用函数

2.1、printf()

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int main(char argc, char *argv[])
  3. {
  4.     while(1)
  5.     {
  6.         printf("while1 %s\n", argv[1]);
  7.     }
  8.     return 0;
  9. }
[CPU: 10.0%左右 usr  9.0%左右 sys  76.0%左右 idle][R或S 18%左右 ./while1.out 111]
串口很卡顿,但进程很容易被Ctrl+c掉。

---由于串口不停的在打印,所以导致串口很难响应任何串口输入,但疯狂敲击串口也能输入,因而看起来很卡。相反,当改为./while.out > /dev/null执行时,串口正常。

2.2、system()

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main(char argc, char *argv[])
  4. {
  5.     char cmd[128] = {0};
  6.     
  7.     while(1)
  8.     {
  9.         sprintf(cmd, "echo while1 > %s.txt", argv[1]);
  10.         system(cmd);
  11.     }
  12.     return 0;
  13. }

[CPU: 18.0%左右 usr  75.5%左右 sys  0.0% idle][ 高概率R(偶尔会S) 18%左右 ./while1.out 111] (CPU占用率100%)
串口流畅,但进程很难被Ctrl+c掉

---虽然用户态进程CPU占用率低,但是由于此处调用了写文件导致sys内核态执行占用了大量CPU时间,而导致整体CPU占用率100%。正因为此,系统虽能及时响应串口,却很难响应串口输入Ctrl+c

3、调用sleep()

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <unistd.h>

  3. int main(char argc, char *argv[])
  4. {
  5.     while(1)
  6.     {
  7.         sleep(atoi(argv[1]));
  8.     }
  9.     return 0;
  10. }

3.1Sleep(1)

[CPU: 0.0%左右 usr  0.0%左右sys  90.%左右 idle][S 0.0% ./while1.out 1]

3.2Sleep(0)

[CPU: 0.3%左右 usr  0.7%左右 sys  95 %左右idle][高概率R(偶尔会S) 0.0% ./while1.out 0]


注意,以上程序是前台执行:./while1.out,而改为后台执行:./while1.out &结果也是一样的。

综上,即使是在单核单线程CPU中,当进程中一个空的死循环使CPU达到100%时,也不会导致别的进程完全得不到时间运行,如此时串口也是可以正常输入的,这是因为Linux操作系统按时间片轮转执行。


  1.     关于[阻塞函数]理解,我是通过反向理解的,理解不深,如下仅代表个人观点:
  2.     首先,进程有如下的状态:
  3. Linux进程状态:R (TASK_RUNNING),可执行状态。
  4. 只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的task_struct结构(进程控制块)被放入对应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中)。进程调度器的任务就是从各个CPU的可执行队列中分别选择一个进程在该CPU上运行。
  5. 很多操作系统教科书将正在CPU上执行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在linux下统一为 TASK_RUNNING状态。
  6. Linux进程状态:S (TASK_INTERRUPTIBLE),可中断的睡眠状态。
  7. 处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。
  8.     然后,什么是阻塞函数:
  9. 当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。


阅读(8225) | 评论(0) | 转发(0) |
0

上一篇:seq_file

下一篇:单链表

给主人留下些什么吧!~~