【问题标题】:Segmentation Fault in multithreaded server implementation in CC中多线程服务器实现中的分段错误
【发布时间】:2015-10-23 03:52:35
【问题描述】:

所以,我已经有一段时间没有使用过多的 C 语言了,而且对 malloc 也不是很满意。我不知道这是否是问题的根源,但我遇到了分段错误,如果这里有人看到任何明显可能有问题的东西,它会为我节省很多查找它的麻烦,因为我'甚至不知道从哪里开始。

在启动时提供给 main 的信息:线程限制、帐户限制、输出文件。 可能的输入

TRANS [acct id 1] [amount] [acct id 2] [amount] 等等...

检查 [帐户 ID]

结束

输出不多,大部分信息都提供给输出文件。

如果这段代码特别难以理解或遵循,请告诉我,我会尽力解释。

编辑:所有来自 valgrind 的内存错误都是由调用 strtok() 引起的。我已经搞砸了,但似乎仍然无法解决它。如何从扫描的变量中使用 strtok() 而不会导致这些错误?

appserver.c

-主程序

#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include "Bank.c"
#include "appserver.h"




struct account *accounts=NULL;
pthread_t *threads=NULL;
char *threadstatus=NULL;
int done=0;
char busywait=0;
FILE * f;

int main(int argc, char **argv){
    int threadcount, banksize;
    char* outputpath;
    if(argc<4)
    {
        threadcount=1;
        banksize=100;
        outputpath="output.txt";
    }
    else
    {
        char *tstr=argv[1];
        char *sstr=argv[2];
        outputpath=argv[3];
        threadcount=(int) strtol(tstr, NULL, 10);
        banksize=(int) strtol(sstr, NULL, 10);
    }




    int reqID=0;
    struct request *req1, *req2;
    req1 = (struct request *) malloc( sizeof(struct request) );
    char * in;
    char *s;

    initialize_status(threadcount);
    threads = (pthread_t *) calloc( threadcount, sizeof(pthread_t) );
    initialize_accounts(banksize);
    initialize_mutex(banksize);
    f=fopen(outputpath,"w");
    int stringsize=1000;

    int threadindex=0;
    int i;
    while(1)
    {
        
        printf("> ");
        getline(&in, &stringsize, stdin);
        s=strtok(in," ");
        if( strcmp( (const char *) s, "CHECK") == 0 )
        {
            req2 = (struct request *) malloc( sizeof(struct request) );
            reqID++;
            req1->type = 'b';
            s=strtok(NULL," ");
            req1->balanceID = (int) strtol( s, NULL, 10);
            req1->next = req2;
            req1->requestID = reqID;
            threadindex=nextthread(threadcount);
            if(threadindex<0)
            {
                busywait=1;
                while(busywait==1)
                {
                    //do nothing, waiting for a thread to finish
                }
                threadindex=nextthread(threadcount);
            }
            req1->thread=threadindex;
            pthread_create(&threads[threadindex], NULL, (void *)&workthread, &req1);
            req1=req2;
        }
        else if( strcmp( (const char *) s, "TRANS") == 0 )
        {
            req2 = (struct request *) malloc( sizeof(struct request) );
            i=0;
            reqID++;
            req1->type = 't';
            while(s!=NULL&&i<10)
            {
                s = strtok(NULL," ");
                req1->transIDs[i] = (int) strtol( s, NULL, 10);
                if((s = strtok(NULL," "))==NULL)
                {
                     printf("Bad input: \n");
                     break;
                }
                req1->transvals[i] = (int) strtol( s, NULL, 10);
                i++;
            }
            req1->next = req2;
            req1->requestID = reqID;
            threadindex=nextthread(threadcount);
            if(threadindex<0)
            {
                busywait=1;
                while(busywait==1)
                {
                    //do nothing
                }
                threadindex=nextthread(threadcount);
            }
            req1->thread=threadindex;
            pthread_create(&threads[threadindex], NULL, (void *)&workthread, &req1);
            req1=req2;
        }
        else if( strcmp( (const char *) s, "END") == 0)
        {
            req1->type = 'e';
            threadindex=nextthread(threadcount);
            if(threadindex<0)
            {
                busywait=1;
                while(busywait==1)
                {
                    //do nothing
                }
                threadindex=nextthread(threadcount);
            }
            req1->thread=threadindex;
            pthread_create(&threads[threadindex], NULL, (void *)&workthread, &req1);
            for( i = 0; i < threadcount; i++)
                pthread_join( threads[i], NULL);
            free(accounts);
            free(threads);
            break;
        }
        else
        {
            printf("Try again\n");
        }
        free(in);
    }
    return 0;
}

void *workthread(struct request *data)
{
    threadstatus[data->thread]='b';
    int value;
    int i, balance;
    printf("< ID %d\n",data->requestID);
    while(1)
    {
        if(data->type == 't')
        {   
            int transamt[10]={0,0,0,0,0,0,0,0,0,0};
            int values[10]={0,0,0,0,0,0,0,0,0,0};
            for(i=0;i<10;i++)
            {
                if(!data->transIDs[i])
                    break;
                transamt[i]=data->transvals[i];
                value=read_account(data->transIDs[i]);
                if((values[i]=value+transamt[i])<0)
                {
                    busywait = 0;
                    threadstatus[data->thread]='a';
                    fprintf(f,"%d ISF %d\n",data->requestID,data->transIDs[i]);
                    return;
                }
            }
            if(translock(data->transIDs) == 1)
            {
                int ID;
                for(i=0;i<10;i++)
                {
                    ID=data->transIDs[i];
                    accounts[ID-1].value=values[i];
                    write_account(data->transIDs[i],values[i]);
                }
            }
            transunlock(data->transIDs);
            busywait=0;
            threadstatus[data->thread]='a';
            fprintf(f,"%d OK\n",data->requestID);
            return;
        }
        else if(data->type == 'b')
        {
            int balance=read_account(data->balanceID);
            fprintf(f,"%d BAL %d\n",data->requestID,balance);
            threadstatus[data->thread]='a';
            busywait=0;
            return;
        }


        else if(data->type == 'e')
        {
            done=1;
            return;
        }
    }
}

int transunlock(int ids[])
{
    struct account *current;
    int i=0;
    for(i=9;i>=0;i--)
    {
        current=&accounts[ids[i]-1];
        if(ids[i]<1)
            continue;
        pthread_mutex_unlock(&current->lock); //unlock previous account
    }
    return;
}

int translock(int ids[])
{
    struct account *current;
    int i=0;
    for(i=0;i<10;i++)
    {
        current=&accounts[ids[i]-1];
        if(ids[i]<1||pthread_mutex_trylock(&current->lock)!=0) //if lock attempt fails
        {
            while(--i>=0)
            {
                pthread_mutex_unlock(&current->lock); //unlock previous account
            }
            return 0;
        }
        current++;
    }
    return 1;
}

int initialize_mutex(int n)
{
    accounts=(struct account *) malloc(sizeof(struct account) * n);
    if(accounts==NULL)
        return 0;
    int i;
    for(i=0;i<n;i++)
    {
        accounts[i].value=0;
    }
    return 1;
}

int initialize_status(int n)
{
    threadstatus = (char *) malloc( sizeof(char) * n );
    int k;
    for(k=0;k<n;k++)
    {
        threadstatus[k]='a';
    }
}

int nextthread(int n)
{
    int i;
    for(i=0;i<n;i++)
    {
        if(threadstatus[i]=='a')
            return i;
    }
    return -1;
}

appserver.h

#include<pthread.h>

struct account{
    pthread_mutex_t lock;
    int value;
};

struct request{
    char type; //'b' for balance check, 't' for transaction, 'e' for exit
    int transIDs[10];
    int transvals[10];
    int balanceID;
    struct request *next;
    int requestID;
    int thread;
};


void *workthread(struct request *data);
int transunlock(int ids[]);
int translock(int ids[]);
int initialize_mutex(int n);
int initialize_status(int n);
int nextthread(int n);

银行.c

/**  Do not modify this file  **/

#include "Bank.h"
#include <stdlib.h>


int *BANK_accounts; //Array for storing account values

/*
 *  Intialize back accounts
 *  Input:  int n - Number of bank accounts
 *  Return:  1 if succeeded, 0 if error
 */
int initialize_accounts( int n )
{
    BANK_accounts = (int *) malloc(sizeof(int) * n);
    if(BANK_accounts == NULL) return 0;

    int i;
    for( i = 0; i < n; i++)
    {
        BANK_accounts[i] = 0;
    }
    return 1;
}

/*
 *  Read a bank account
 *  Input:  int ID - Id of bank account to read
 *  Return:  Value of bank account ID
 */
int read_account( int ID )
{
    usleep( 100000 );
    return BANK_accounts[ID - 1];
}

/*
 *  Write value to bank account
 *  Input:  int ID - Id of bank account to write to
 *  Input:  int value - value to write to account
 */
void write_account( int ID, int value)
{
    usleep( 100000 );
    BANK_accounts[ID - 1] = value;
}

银行.h

/**  Do not modify this file  **/

/*
 *  These functions do not provide any error checking.
 *  If an invalid input is supplied the behavior is
 *  undefined.
 */

/*
 *  Intialize n bank accounts with IDs from 1 to n and values of 0.
 *  Input:  int n - Number of bank accounts, must be larger than 0
 *  Return:  1 if succeeded, 0 if error
 */
int initialize_accounts( int n );

/*
 *  Read a bank account
 *  Input:  int ID - Id of bank account to read
 *  Return:  Value of bank account ID
 */
int read_account( int ID );

/*
 *  Write value to bank account
 *  Input:  int ID - Id of bank account to write to
 *  Input:  int value - value to write to account
 */
void write_account( int ID, int value);

就像我之前说的,valgrind 上的所有错误都与 strtok() 调用有关,如下:

条件跳转或移动取决于未初始化的值

使用大小为 8 的未初始化值

大小为 1 的读取无效

进程以信号 11 (SIGSEGV) 的默认操作终止

【问题讨论】:

  • valgrind 下运行您的代码。这会告诉你你是否在记忆方面做任何可疑的事情。
  • 这只是……一个问题。所有忙等待线程微管理的东西。这是自找麻烦:(
  • 我应该怎么做呢?如果服务器中的所有线程都在使用,我希望能够以某种方式知道其中一个线程何时完成。我知道我可以加入所有线程,但这只会迫使我等待每个线程完成,有没有办法解决这个问题?
  • 我认为你的结构初始化可能有问题。您已经创建了一个排序的链接列表,并且在使用它们之前没有初始化节点。 (指struct request与成员struct request *next;
  • 还值得一提的是,我刚刚对 valgrind 做了一些处理,它返回的几乎每一个错误都与对 strtok() 的调用有关---------- 条件跳转或移动取决于未初始化的值----- 使用大小为 8 的未初始化值----- 大小为 1 的无效读取------ 进程以信号 11 的默认操作终止(SIGSEGV) ------ 所有这些错误都响应输入“TRANS 1 10”。我搞砸了对 strtok 的调用,但没有运气。

标签: c multithreading


【解决方案1】:

您在 main() 中写道:

char * in;
...
while(1)
{
    printf("> ");
    scanf("%s",&in);

我认为 scanf() 不会为您调用 malloc() 。

【讨论】:

  • 如果我不知道字符串会有多长,我该如何使用 malloc?
  • 如果您在具有虚拟内存的重要平台上,malloc 64k 字节。那会足够长吗?是的,我是认真的。猜测你最长的字符串可能有多长。加倍并添加 8k。使用它。
  • @Martin James 尝试为 *in 和 *s 分配内存,然后在每次循环后释放内存。仍然是同样的错误,仍然是 strtok() 的错误。
  • 或者,使用getline(3)。它读取整行,并使用mallocrealloc(留给您free 的责任)
  • @Basile Starynkevitch 虽然这确实教会了我一些我不知道的东西,而且我现在已经实现了它,但遗憾的是它并没有解决问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 2016-10-26
  • 2021-11-22
  • 1970-01-01
  • 2012-05-10
  • 2015-09-10
相关资源
最近更新 更多