linux 下 c 语言编程,使用多线程是方便的。有时候,我们需要函数创建线程后立刻返回,线程继续在后台运行。可是,却很容易出现段错误(Segmentation fault),请看下面这段代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct __PARAM
{
char* str;
int cnt;
}PARAM;
void* thread(void* pdata)
{
PARAM* p = (PARAM*)pdata;
printf("str's ptr in %s: %p - %d\n", __FUNCTION__, p->str, p->cnt);
sleep(1);
printf("\n in thread:\n");
printf("str(%p): %s", p->str, p->str);
printf("cnt: %d\n\n", p->cnt);
return NULL;
}
int ifun(char* str, int cnt)
{
pthread_t pid;
PARAM param = {0};
printf("str's ptr in %s: %p\n", __FUNCTION__, str);
param.str = str;
param.cnt = cnt;
if(0>pthread_create(&pid, NULL, thread, ¶m)){
printf("%s create thread failed\n", __FUNCTION__);
return -1;
}
return 0;
}
int main()
{
char str[] = "hello world\n";
int cnt = 88;
printf("str's ptr in %s: %p\n", __FUNCTION__, str);
ifun(str, cnt);
sleep(3);
printf("\nfinished\n\n");
return 0;
}
我们编译执行,发现出现了段错误:
$ gcc thread_resource.c -lpthread -g
$ ./a.out
str's ptr in main: 0x7ffec58dbcd0
str's ptr in ifun: 0x7ffec58dbcd0
str's ptr in thread: 0x7ffec58dbcd0 - 88
in thread:
Segmentation fault (core dumped)
这是为什么呢?
创建线程后立刻返回,出现段错误的原因
linux 下程序每调用一个函数,就会为该函数创建一个栈帧
,被调用函数的局部变量等信息都会保存在栈帧
里。被调用函数返回后,它的栈帧
就会被销毁了。如果之前传递了栈帧
里的参数给线程函数,此时线程函数再引用这里的参数,操作系统就会判定引用非法,就会出现Segmentation fault(段错误)。
我们来看看上述代码的对应的汇编代码:
$ objdump -dS a.out
a.out: file format elf64-x86-64
...
int ifun(char* str, int cnt)
{
400820: 55 push %rbp
400821: 48 89 e5 mov %rsp,%rbp
400824: 48 83 ec 40 sub $0x40,%rsp
400828: 48 89 7d c8 mov %rdi,-0x38(%rbp)
40082c: 89 75 c4 mov %esi,-0x3c(%rbp)
40082f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400836: 00 00
400838: 48 89 45 f8 mov %rax,-0x8(%rbp)
40083c: 31 c0 xor %eax,%eax
pthread_t pid;
PARAM param = {0};
40083e: 48 c7 45 e0 00 00 00 movq $0x0,-0x20(%rbp) # 传递给线程的 param 在 ifun 的栈帧里,栈帧销毁后无法再使用
400845: 00
400846: 48 c7 45 e8 00 00 00 movq $0x0,-0x18(%rbp)
40084d: 00
...
解决办法
问题的关键就是线程函数引用的参数被销毁了,那么只要不让其销毁,就不会出现段错误了。方法有两个,一是创建全局变量作为传递给线程函数的参数,全局变量不会随着函数的返回被销毁。一是动态分配一个变量作为线程函数的参数,动态分配的变量存在堆中,也不会在函数返回时被销毁。因为全局变量对程序的独立封装性有破坏,所以下面的例子使用了动态分配变量来解决问题:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct __PARAM
{
char* str;
int cnt;
}PARAM;
void* thread(void* pdata)
{
PARAM* p = (PARAM*)pdata;
printf("str's ptr in %s: %p - %d\n", __FUNCTION__, p->str, p->cnt);
sleep(1);
printf("\n in thread:\n");
printf("str(%p): %s", p->str, p->str);
printf("cnt: %d\n\n", p->cnt);
free(p);
return NULL;
}
int ifun(char* str, int cnt)
{
PARAM* param = (PARAM*)malloc(sizeof(PARAM));
pthread_t pid;
printf("str's ptr in %s: %p\n", __FUNCTION__, str);
param->str = str;
param->cnt = cnt;
if(0>pthread_create(&pid, NULL, thread, param)){
printf("%s create thread failed\n", __FUNCTION__);
return;
}
return 0;
}
int main()
{
char str[] = "hello world\n";
int cnt = 88;
printf("str's ptr in %s: %p\n", __FUNCTION__, str);
ifun(str, cnt);
sleep(3);
printf("\nfinished\n\n");
return 0;
}
编译执行结果如下:
$ gcc thread_resource.c -lpthread -g
$ ./a.out
str's ptr in main: 0x7fffe5e35fc0
str's ptr in ifun: 0x7fffe5e35fc0
str's ptr in thread: 0x7fffe5e35fc0 - 88
in thread:
str(0x7fffe5e35fc0): hello world
cnt: 88
finished
成功了,程序没有再出现段错误。这样,我们就解决了函数创建线程后立刻返回,线程出现段错误(Segmentation fault)
的问题。