为什么多个 pthread_create() 调用同一个函数会导致分段错误?
Why multiple pthread_create() calling same function end up in segmentation fault?
我正在尝试 运行 一段具有多个线程的代码。我的理解是,即使线程共享进程内存,从线程调用的每个函数都有自己的堆栈框架,即寄存器。因此,从多个线程调用同一个函数应该没有问题(如果我错了请指正)。
我有以下代码。此代码通常与单个线程一起工作。我的目标是 运行 同时执行此代码的某些部分。
#include "rsa/config.h"
#include "rsa/aes.h"
#include "rsa/bignum.h"
#include "rsa/rsa.h"
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define NUM_OF_THREAD 8
void thread_function(void * input){
printf("Test thread\n");
unsigned char private_encrypt[KEY_BUFFER_SIZE];
int total_dec=1000;
unsigned char * buffer = 0;
long length;
unsigned char msg_decrypted[KEY_LEN];
FILE * fp2 = fopen ("msg.enc", "rb");
int size1=KEY_BUFFER_SIZE;
if(fp2){
while(size1>0){
fread(private_encrypt,1,sizeof (private_encrypt),fp2);
size1=size1-1;
}
}
fclose(fp2);
FILE * fp = fopen ("rsa_priv.txt", "rb");
if (fp){
fseek (fp, 0, SEEK_END);
length = ftell (fp);
fseek (fp, 0, SEEK_SET);
buffer = calloc (1,length+1);
if (buffer){
fread (buffer, 1, length, fp);
}
fclose (fp);
}
// initialize rsaContext
rsa_context rsaContext;
rsa_init(&rsaContext,RSA_PKCS_V15, 0);
rsaContext.len=KEY_LEN;
// spliting keys and load into rsa context
const char s[3] = "= ";
char *token;
int k=0, size;
// get the first token
token = strtok(buffer, s);
// walk through other tokens
while( token != NULL ) {
size = strlen(token);
switch (k) {
case 1:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.N, 16, token);
break;
case 3:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.E, 16, token);
break;
case 5:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.D, 16, token);
break;
case 7:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.P, 16, token);
break;
case 9:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.Q, 16, token);
break;
case 11:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.DP, 16, token);
break;
case 13:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.DQ, 16, token);
break;
case 15:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.QP, 16, token);
break;
}
k=k+1;
token = strtok(NULL, "= \n");
}
printf("Here\n");
while (total_dec>=0) {
if( rsa_private(&rsaContext,private_encrypt, msg_decrypted) != 0 ) {
printf( "Decryption failed! %d\n", rsa_private(&rsaContext,private_encrypt, msg_decrypted));
}else{
printf("%d Decrypted plaintext-----> %s\n",total_dec, msg_decrypted );
}
total_dec--;
}
}
int main(){
int i;
clock_t t;
// total number of thread
pthread_t ths[NUM_OF_THREAD];
// start time count
t = clock();
for (i = 0; i < NUM_OF_THREAD; i++) {
pthread_create(&ths[i], NULL, thread_function, NULL);
}
for (i = 0; i < NUM_OF_THREAD; i++) {
void* res;
pthread_join(ths[i], &res);
}
// end time count
t = clock() - t;
double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds
printf("Took %f seconds to execute \n", time_taken);
printf("Total Cycle %f \n", t);
return 0;
}
当我 运行 使用单线程的代码时,代码运行良好并产生了理想的结果。如果我将 NUM_OF_THREAD
设置为不同的值,我的程序就会开始表现得很奇怪。有时所有线程都已创建并完美运行,有时线程已创建但之后出现分段错误。有时几个线程工作,几个线程不工作。
错误类型:1(设置NUM_OF_THREAD 8
),输出:
因此,我尝试 运行 具有 8 个线程的代码。不同的尝试给我不同的结果
$./multiThread
Test thread
Test thread
Test thread
Test thread
Test thread
Test thread
Test thread
Here
Here
Here
Here
Segmentation fault (core dumped)
错误类型:2(设置NUM_OF_THREAD 8
),输出:
$./multiThread
Test thread
Test thread
Test thread
Test thread
Test thread
Here
Here
Test thread
Here
Here
Here
Decryption failed! -17156
Decryption failed! -17156
Segmentation fault (core dumped)
错误类型:3(设置NUM_OF_THREAD 8
),输出:
在这个试验中,有几个线程失败了,但其中一些线程产生了预期的结果。
$./multiThread
Decryption failed! -17156
Decryption failed! -17156
.
.
.
.
3 Decrypted plaintext-----> xxxxxxxxxxx
2 Decrypted plaintext-----> xxxxxxxxxxx
2 Decrypted plaintext-----> xxxxxxxxxxx
1 Decrypted plaintext-----> xxxxxxxxxxx
1 Decrypted plaintext-----> xxxxxxxxxxx
0 Decrypted plaintext-----> xxxxxxxxxxx
0 Decrypted plaintext-----> xxxxxxxxxxx
Took 174.413690 seconds to execute
Total Cycle 174.413690
我的预感,问题出在rsa_private()
。这是在 #include "rsa/rsa.h"
中声明的。这被认为是共享资源吗?如果我不正确,有人可以告诉我我在这里做错了什么吗?我该怎么做才能解决这个问题?我不想使用锁,因为它会导致我的代码变慢。
我的 CPU:Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz,四核,每核 2 个线程。
strtok()
不是线程安全的,因为它在内部使用静态缓冲区。请改用 strtok_r()
。
来自
the man page
┌────────────────────────┬───────────────┬───────────────────────┐
│Interface │ Attribute │ Value │
├────────────────────────┼───────────────┼───────────────────────┤
│strtok() │ Thread safety │ MT-Unsafe race:strtok │
├────────────────────────┼───────────────┼───────────────────────┤
│strtok_r() │ Thread safety │ MT-Safe │
└────────────────────────┴───────────────┴───────────────────────┘
您的理论是正确的,但是 strtok()
不符合您的假设,strtok_r()
是这样做的函数。
strtok()
函数不是 MT-safe
,这意味着您不应该在多线程环境中使用它。 strtok
的可重入(因此 MT-safe
)版本是 strtok_r
,这就是您应该使用的版本。
strtok_r
here.
手册
strtok
使用 static
(永远不会是 MT-safe
)存储其状态并更新该状态不同步。这使得它在多线程应用程序中使用时严重暴露于竞争条件,如果这样做会导致不可预测的行为。
strtok_r
在本地保存它的状态(因此额外的 saveptr
参数),使其可重入并且 MT-safe
隐式。
如果您想亲眼看看,请检查 strtok
是如何实现的 here:
char *
strtok (char *s, const char *delim)
{
static char *olds;
return __strtok_r (s, delim, &olds);
}
我正在尝试 运行 一段具有多个线程的代码。我的理解是,即使线程共享进程内存,从线程调用的每个函数都有自己的堆栈框架,即寄存器。因此,从多个线程调用同一个函数应该没有问题(如果我错了请指正)。
我有以下代码。此代码通常与单个线程一起工作。我的目标是 运行 同时执行此代码的某些部分。
#include "rsa/config.h"
#include "rsa/aes.h"
#include "rsa/bignum.h"
#include "rsa/rsa.h"
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define NUM_OF_THREAD 8
void thread_function(void * input){
printf("Test thread\n");
unsigned char private_encrypt[KEY_BUFFER_SIZE];
int total_dec=1000;
unsigned char * buffer = 0;
long length;
unsigned char msg_decrypted[KEY_LEN];
FILE * fp2 = fopen ("msg.enc", "rb");
int size1=KEY_BUFFER_SIZE;
if(fp2){
while(size1>0){
fread(private_encrypt,1,sizeof (private_encrypt),fp2);
size1=size1-1;
}
}
fclose(fp2);
FILE * fp = fopen ("rsa_priv.txt", "rb");
if (fp){
fseek (fp, 0, SEEK_END);
length = ftell (fp);
fseek (fp, 0, SEEK_SET);
buffer = calloc (1,length+1);
if (buffer){
fread (buffer, 1, length, fp);
}
fclose (fp);
}
// initialize rsaContext
rsa_context rsaContext;
rsa_init(&rsaContext,RSA_PKCS_V15, 0);
rsaContext.len=KEY_LEN;
// spliting keys and load into rsa context
const char s[3] = "= ";
char *token;
int k=0, size;
// get the first token
token = strtok(buffer, s);
// walk through other tokens
while( token != NULL ) {
size = strlen(token);
switch (k) {
case 1:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.N, 16, token);
break;
case 3:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.E, 16, token);
break;
case 5:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.D, 16, token);
break;
case 7:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.P, 16, token);
break;
case 9:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.Q, 16, token);
break;
case 11:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.DP, 16, token);
break;
case 13:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.DQ, 16, token);
break;
case 15:
token[size-1]='[=11=]';
mpi_read_string(&rsaContext.QP, 16, token);
break;
}
k=k+1;
token = strtok(NULL, "= \n");
}
printf("Here\n");
while (total_dec>=0) {
if( rsa_private(&rsaContext,private_encrypt, msg_decrypted) != 0 ) {
printf( "Decryption failed! %d\n", rsa_private(&rsaContext,private_encrypt, msg_decrypted));
}else{
printf("%d Decrypted plaintext-----> %s\n",total_dec, msg_decrypted );
}
total_dec--;
}
}
int main(){
int i;
clock_t t;
// total number of thread
pthread_t ths[NUM_OF_THREAD];
// start time count
t = clock();
for (i = 0; i < NUM_OF_THREAD; i++) {
pthread_create(&ths[i], NULL, thread_function, NULL);
}
for (i = 0; i < NUM_OF_THREAD; i++) {
void* res;
pthread_join(ths[i], &res);
}
// end time count
t = clock() - t;
double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds
printf("Took %f seconds to execute \n", time_taken);
printf("Total Cycle %f \n", t);
return 0;
}
当我 运行 使用单线程的代码时,代码运行良好并产生了理想的结果。如果我将 NUM_OF_THREAD
设置为不同的值,我的程序就会开始表现得很奇怪。有时所有线程都已创建并完美运行,有时线程已创建但之后出现分段错误。有时几个线程工作,几个线程不工作。
错误类型:1(设置NUM_OF_THREAD 8
),输出:
因此,我尝试 运行 具有 8 个线程的代码。不同的尝试给我不同的结果
$./multiThread
Test thread
Test thread
Test thread
Test thread
Test thread
Test thread
Test thread
Here
Here
Here
Here
Segmentation fault (core dumped)
错误类型:2(设置NUM_OF_THREAD 8
),输出:
$./multiThread
Test thread
Test thread
Test thread
Test thread
Test thread
Here
Here
Test thread
Here
Here
Here
Decryption failed! -17156
Decryption failed! -17156
Segmentation fault (core dumped)
错误类型:3(设置NUM_OF_THREAD 8
),输出:
在这个试验中,有几个线程失败了,但其中一些线程产生了预期的结果。
$./multiThread
Decryption failed! -17156
Decryption failed! -17156
.
.
.
.
3 Decrypted plaintext-----> xxxxxxxxxxx
2 Decrypted plaintext-----> xxxxxxxxxxx
2 Decrypted plaintext-----> xxxxxxxxxxx
1 Decrypted plaintext-----> xxxxxxxxxxx
1 Decrypted plaintext-----> xxxxxxxxxxx
0 Decrypted plaintext-----> xxxxxxxxxxx
0 Decrypted plaintext-----> xxxxxxxxxxx
Took 174.413690 seconds to execute
Total Cycle 174.413690
我的预感,问题出在rsa_private()
。这是在 #include "rsa/rsa.h"
中声明的。这被认为是共享资源吗?如果我不正确,有人可以告诉我我在这里做错了什么吗?我该怎么做才能解决这个问题?我不想使用锁,因为它会导致我的代码变慢。
我的 CPU:Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz,四核,每核 2 个线程。
strtok()
不是线程安全的,因为它在内部使用静态缓冲区。请改用 strtok_r()
。
来自 the man page
┌────────────────────────┬───────────────┬───────────────────────┐
│Interface │ Attribute │ Value │
├────────────────────────┼───────────────┼───────────────────────┤
│strtok() │ Thread safety │ MT-Unsafe race:strtok │
├────────────────────────┼───────────────┼───────────────────────┤
│strtok_r() │ Thread safety │ MT-Safe │
└────────────────────────┴───────────────┴───────────────────────┘
您的理论是正确的,但是 strtok()
不符合您的假设,strtok_r()
是这样做的函数。
strtok()
函数不是 MT-safe
,这意味着您不应该在多线程环境中使用它。 strtok
的可重入(因此 MT-safe
)版本是 strtok_r
,这就是您应该使用的版本。
strtok_r
here.
strtok
使用 static
(永远不会是 MT-safe
)存储其状态并更新该状态不同步。这使得它在多线程应用程序中使用时严重暴露于竞争条件,如果这样做会导致不可预测的行为。
strtok_r
在本地保存它的状态(因此额外的 saveptr
参数),使其可重入并且 MT-safe
隐式。
如果您想亲眼看看,请检查 strtok
是如何实现的 here:
char *
strtok (char *s, const char *delim)
{
static char *olds;
return __strtok_r (s, delim, &olds);
}