免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 15894 | 回复: 4
打印 上一主题 下一主题

malloc是线程安全的吗?结果很出人意料 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-03-09 09:11 |只看该作者 |倒序浏览
本帖最后由 wp00000008 于 2010-03-09 09:12 编辑

先介绍一下系统
Linux tech.tianya.cn 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 i686 i386 GNU/Linux
cc (GCC) 4.1.1 20070105 (Red Hat 4.1.1-52)

关于malloc的线程安全性,主流说法是线程安全的。这个说法很容易让人引起共鸣:如果malloc不是线程安全的,那多线程编程不是太可怕了吗?
我相信malloc是线程安全的,结果这回出事了。
[root@tech iotest]# ./a.out /root/chengsj/disk0 50 256
dir=/root/chengsj/disk0, threads=50, cache=256k
段错误 (core dumped)
core dump的那一行是一个free
由此引起对c标准库的严重怀疑。c标准库中,没有什么东西是线程安全的,兄弟们悠着点,谨防黑手。
以下是代码。这个代码是用于多线程环境下IO读取性能测试,利用不同的线程数量和cache大小进行比较,以期得到比较好的优化结果。
由于是测试代码,就没有很注意细节。
现有代码在运行时会有core dump,但是对malloc加锁之后(程序中被注释的部分),就不会core dump了

/*
* iotest.c
*
*  Created on: 2010-3-8
*      Author: cheng
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>

#include "iothreads.h"

int main(int argc, char *argv[])
{
        if (argc != 4) {
                printf("Usage: iotest src-dir threadnum cache-k  \n");
                return 1;
        }
        char *dir;
        char buf[1024];
        int threads, cachelen, result;
        DIR *fdir;
        struct dirent * pdir;
        time_t t1, t2;

        double total;

        dir = argv[1];
        threads = atoi(argv[2]);
        cachelen = atoi(argv[3]);

        printf("dir=%s, threads=%d, cache=%dk\n", dir, threads, cachelen);

        if (threads_init() != 0) {
                printf("error threads_init. \n");
                return 1;
        }

        fdir = opendir(dir);
        if (fdir == NULL) {
                printf("error open dir %s: %s\n", dir, strerror(errno));
                return 1;
        }
        while((pdir = readdir(fdir))!=NULL) {
                if (pdir->d_type == 8) {
                        threads_add(dir, pdir->d_name);
                }
        }
        closedir(fdir);

        t1 = time(NULL);

        threads_create(threads, cachelen);

        while (threads_count()) {
                sleep(1);
        }

        t2 = time(NULL);

        total = threads_total();
        printf("time(s): %d, ", t2-t1);
        printf("bytes(M): %f, rate(M/s):%f \n", total, total/(t2-t1));

        return 0;
}



/*
* iothreads.h
*
*  Created on: 2010-3-8
*      Author: cheng
*/

#ifndef IOTHREADS_H_
#define IOTHREADS_H_

#define MAX_FILE_COUNT         10*1024
#define MAX_FILE_LEN        1024

int init_pthread_lock(pthread_mutex_t *pthread_lock);
double threads_total();
int threads_init();
int threads_add(char *path, char *name);
int threads_create(int num, int cache);
int threads_count();

#endif /* IOTHREADS_H_ */



/*
* iothreads.c
*
*  Created on: 2010-3-8
*      Author: cheng
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/poll.h>
#include <sys/select.h>
#include <netinet/tcp.h>
#include <sys/sendfile.h>

#include "iothreads.h"

static char st_files[MAX_FILE_COUNT][MAX_FILE_LEN];
static pthread_mutex_t st_lock;
static int st_count;
static int st_idx;
static int st_cache;

volatile static int st_threads;
volatile static double st_total;

int init_pthread_lock(pthread_mutex_t *pthread_lock)
{
        pthread_mutexattr_t mat;
        int result;

        if ((result=pthread_mutexattr_init(&mat)) != 0)
        {
                return result;
        }
        if ((result=pthread_mutexattr_settype(&mat, \
                        PTHREAD_MUTEX_ERRORCHECK_NP)) != 0)
        {
                return result;
        }
        if ((result=pthread_mutex_init(pthread_lock, &mat)) != 0)
        {
                return result;
        }
        if ((result=pthread_mutexattr_destroy(&mat)) != 0)
        {
                return result;
        }

        return 0;
}
double threads_total()
{
        return st_total;
}
int total_add(int len)
{
        int result;

        if ((result=pthread_mutex_lock(&st_lock)) != 0) {
                fprintf(stderr, "\t Error pthread_mutex_lock[%d / %s]: %s \n", __LINE__, __FILE__, strerror(result));
                return (result);
        }

        st_total = st_total+len*1.0/(1024*1024);

        pthread_mutex_unlock(&st_lock);

        return 0;
}

int threads_init()
{
        int result;

        st_count = 0;
        st_idx = -1;
        st_threads = 0;
        st_total = 0;

        if ((result=init_pthread_lock(&st_lock)) != 0) {
                fprintf(stderr, "\t Error init_pthread_lock[%d / %s]: %s \n", __LINE__, __FILE__, strerror(result));
                return (result);
        }

        return 0;
}

int threads_add(char *path, char *name)
{
        int result;

        if ((result=pthread_mutex_lock(&st_lock)) != 0) {
                fprintf(stderr, "\t Error pthread_mutex_lock[%d / %s]: %s \n", __LINE__, __FILE__, strerror(result));
                return (result);
        }
        if (st_count < MAX_FILE_COUNT-1) {
                st_count ++;
                sprintf(st_files[st_count-1], "%s/%s\0", path, name);
        } else {
                fprintf(stderr, "\t Error threads_add[%d / %s]: %d exceed. \n", __LINE__, __FILE__, MAX_FILE_COUNT);
        }

        pthread_mutex_unlock(&st_lock);

        return 0;
}

void* threads_run(void *p)
{
        char file[MAX_FILE_LEN];
        char *buf;
        int result, filefd, len;

        /*
        if ((result=pthread_mutex_lock(&st_lock)) != 0) {
                fprintf(stderr, "\t Error pthread_mutex_lock[%d / %s]: %s \n", __LINE__, __FILE__, strerror(result));
                st_threads --;
                return;
        }
        */
        buf = (char *)malloc(st_cache*1024);
        //pthread_mutex_unlock(&st_lock);

        if (buf != NULL) {
                while (1) {
                        if ((result=pthread_mutex_lock(&st_lock)) != 0) {
                                fprintf(stderr, "\t Error pthread_mutex_lock[%d / %s]: %s \n", __LINE__, __FILE__, strerror(result));
                                continue;
                        }
                        st_idx ++;
                        if (st_idx >= st_count) {
                                pthread_mutex_unlock(&st_lock);
                                break;
                        }
                        memset(file, 0, sizeof(file));
                        memcpy(file, st_files[st_idx], MAX_FILE_LEN);
                        pthread_mutex_unlock(&st_lock);

                        filefd = open(file, O_RDONLY);
                        if (filefd < 0) {
                                fprintf(stderr, "Error[%d / %s]: %s while open [%s].\n", __LINE__, __FILE__, strerror(errno), file);
                                continue;
                        }
                        while ((len=read(filefd, buf, st_cache*1024)) > 0) {
                                total_add(len);
                        }
                        close(filefd);
                }
                free(buf);
                buf = NULL;
        } else fprintf(stderr, "Error[%d / %s]: %s while malloc.\n", __LINE__, __FILE__, strerror(errno));

        st_threads --;
}

int threads_create(int num, int cache)
{
        int result, i;
        pthread_t tid;

        if ((result=pthread_mutex_lock(&st_lock)) != 0) {
                fprintf(stderr, "\t Error pthread_mutex_lock[%d / %s]: %s \n", __LINE__, __FILE__, strerror(result));
                return (result);
        }

        for (i=0; i<num; i++) {
                if ((result=pthread_create(&tid, NULL, threads_run, NULL)) != 0) {
                        fprintf(stderr, "threads_create:pthread_create error: %s\n", strerror(result));
                        return (result);
                }
        }

        st_threads = num;
        st_cache = cache;

        pthread_mutex_unlock(&st_lock);

        return 0;
}

int threads_count()
{
        return st_threads;
}

论坛徽章:
0
2 [报告]
发表于 2010-03-09 14:17 |只看该作者
换成是我,出了错误第一时间怀疑的是自己的代码,而不是其他已经很成熟的东西,比如库函数、比如编译器……

为什么free会崩溃? 在 threads_create 函数中,先 pthread_create,子线程已经开跑了; 后设置 st_cache 变量。
而子线程malloc的空间大小取的是 st_cache,很有可能malloc的时候 st_cache 还是初始值 0,但是 read 的时候值变了,结果读越界了,然后把 libc 内存管理的数据给写坏了……

malloc 加了锁为什么又不会崩溃,你加的都是 st_lock 这个锁,加锁之后不就 malloc 不就一定在 st_cache 被设置值之后才会执行了吗?

论坛徽章:
0
3 [报告]
发表于 2010-03-10 10:34 |只看该作者
多谢精灵王的帮助。
我测试了一下,精灵王的说法是OK的。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2010-03-11 11:32 |只看该作者
linux 下是线程安全的,而windows下默认可不是线程安全的,一般要改编译选项。

论坛徽章:
0
5 [报告]
发表于 2010-03-11 15:22 |只看该作者
果然是学习了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP