一 Pthread 并发编程——深入剖析线程基本元素和状态( 五 )

#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int times = 1;// 先打印再申请内存空间void* func(void* arg) {printf("times = %d\n", times);times++;char s[1 << 20]; // 申请 1MB 内存空间(分配在栈空间上)func(NULL);return NULL;}int main() {pthread_t t;pthread_create(&t, NULL, func, NULL);pthread_join(t, NULL);return 0;}由于上面两个程序的输出结果是一样的,所以我就只放出一个程序的输出结果了:

一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
但是不对呀!如果是后申请内存空间的话,程序的输出应该能够打印 times = 8 ?。?因为之前只申请了 7MB 的空间,我们打印 times = 8 的时候还没有执行到语句 char s[1 << 20]; ,那为什么也只打印到 7 呢?
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
出现上面问题的主要原因就需要看编译器给我们编译后的程序是如何申请内存空间的 。我们将上面的函数 func 的汇编代码展示出来:
00000000004005e0 <func>:4005e0:55push%rbp4005e1:48 89 e5mov%rsp,%rbp4005e4:48 81 ec 20 00 10 00sub$0x100020,%rsp4005eb:48 8d 04 25 3c 07 40lea0x40073c,%rax4005f2:004005f3:48 89 7d f8mov%rdi,-0x8(%rbp)4005f7:8b 34 25 40 10 60 00mov0x601040,%esi4005fe:48 89 c7mov%rax,%rdi400601:b0 00mov$0x0,%al400603:e8 c8 fe ff ffcallq4004d0 <printf@plt>400608:48 bf 00 00 00 00 00movabs $0x0,%rdi40060f:00 00 00400612:8b 34 25 40 10 60 00mov0x601040,%esi400619:81 c6 01 00 00 00add$0x1,%esi40061f:89 34 25 40 10 60 00mov%esi,0x601040400626:89 85 ec ff ef ffmov%eax,-0x100014(%rbp)40062c:e8 af ff ff ffcallq4005e0 <func>400631:48 bf 00 00 00 00 00movabs $0x0,%rdi400638:00 00 0040063b:48 89 85 e0 ff ef ffmov%rax,-0x100020(%rbp)400642:48 89 f8mov%rdi,%rax400645:48 81 c4 20 00 10 00add$0x100020,%rsp40064c:5dpop%rbp40064d:c3retq上面的汇编代码是上面的程序在 x86_64 平台下得到的 , 我们需要注意一行汇编指令 sub$0x100020,%rsp ,这条指令的主要作用就是将栈顶往下扩展(栈是从上往下生长的)1 MB 字节(实际上稍微比 1MB 大一点 , 因为还有其他操作需要一些栈空间),事实上就是给变量 s 申请 1MB 的栈空间 。
好了,看到这里就破案了,原来编译器申请栈空间的方式是将栈顶寄存器 rsp ,往虚拟地址空间往下移动 , 而编译器在函数执行刚开始的时候就申请了这么大的空间,因此不管是先申请空间再打印 , 还是先打印再申请空间,在程序被编译成汇编指令之后,函数 func 在函数刚开始就申请了对应的空间,因此才出现了都只打印到 times = 7
总结在本篇文章当中主要给大家介绍了线程的基本元素和一些状态 , 还重点介绍了各种与线程相关属性的函数 , 主要使用的各种函数如下:
  • pthread_create , 用与创建线程
  • pthread_attr_init,初始话线程的基本属性 。
  • pthread_attr_destroy,释放属性相关资源 。
  • pthread_join,用于等待线程执行完成 。
  • pthread_attr_setstacksize,用于设置线程执行栈的大小 。
  • pthread_attr_setstack,设置线程执行栈的栈顶和栈的大小 。
  • pthread_testcancel,用于检测线程是否被取消了,是一个取消点 。
  • pthread_cancel,取消一个线程的执行 。
希望大家有所收获!
更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore
关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识 。

推荐阅读