2012-03-28 3 views
0

현재 Linux의 C++에서 웹 크롤러/스파이더를 쓰고 있는데 데이터베이스를 업데이트하는 데 문제가 있습니다. 나는 공정하게 새로운 C/C++, 다만 FYI이다.libmysqlcppconn 준비 문을 사용하여 glibc 메모리 손상

데이터베이스 업데이트는 pthreads를 사용하는 별도의 스레드에 의해 실행되지만, main()에서 실행될 경우 동일한 문제가 존재하므로 나는 순진하게도 스레딩 요소를 원인의 하나로 버립니다.

데이터베이스 API에 libmysqlcppconn을 사용하고 있습니다.

gcc 버전 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1)에서 -O2 -Wall -pedantic으로 컴파일 중이며 정상적으로 컴파일됩니다.

아래의 commitChangesToDatabase() 함수를 호출하면 기본적으로 std :: map (url_queue)에서 항목을 선택하고 std :: vector (업데이트)에 던져서 원래 항목에서 해당 항목을 지 웁니다. :: map을 실행 한 후 std :: vector를 반복하여 벡터의 각 항목에 대한 MySQL 준비 문을 실행합니다. 여기가 어려운 곳입니다.

은 임의로 하나 : glibc가 메모리 부패

  • 충돌 검출 에러 출력 (NO 세그먼트 폴트없이 스택 트레이스없고 아무것도)없이

    나는 간단한 exe cuteUpdate()하지만 아무 소용이 없습니다. url_queue의 첫 번째 루프에서 업데이트 할 항목을 찾을 때마다 항목을 선택하는 단계를 생략하고 업데이트를 실행하려고했습니다.

    이 응용 프로그램의 다른 기능은 준비된 명령문 (다른 UPDATE)을 사용하며 정상적으로 작동합니다. 이러한 함수는 별도의 스레드에 의해 실행됩니다.

    나는 valgrind를 통해 응용 프로그램을 실행할 것입니다. 그러나 솔직히 말하면 출력의 대부분을 이해하지 못해서 많은 도움이되지 않습니다. 그러나 누군가 출력을 원한다면 실행 옵션을 알려주십시오 그것과 나는 그것을 제공 할 것이다.

    여기부터 진행하는 데는 단서가 없습니다. 누구라도 잘못된 점을 알고 있습니까?

    struct queue_item_t { 
        int id; 
        int sites_id; 
        int priority; 
        int depth; 
        int handler; 
        int state; // 0 = Pending, 1 = Working, 2 = Completed, 3 = Checked 
        double time_allowed_crawl; 
    
        bool status; 
        bool was_redirected; 
    
        double time; 
        double time_end; 
        double time_curl; 
        double size; 
    
        std::string hash; 
        std::string url; 
        std::string file; 
        std::string host; 
    }; 
    
    void commitChangesToDatabase() 
    { 
        map< string, queue_item_t >::iterator it, end; 
        sql::PreparedStatement *pstmt; 
        int i = 0; 
    
        if (!url_queue.size()) { 
         return; 
        } 
    
        pthread_mutex_lock(&dbCommitMutex); 
        pthread_mutex_lock(&itemMutex); 
    
        cout << "commitChangesToDatabase()" << endl; 
        pstmt = dbPrepareStatement("UPDATE crawler_queue SET process_hash = NULL, date_crawled = NOW(), url = ?, hash = ? WHERE id = ?"); 
    
        for (it = url_queue.begin(); it != url_queue.end();) 
        { 
         if (it->second.state == 2) 
         { 
          pstmt->setString(1, it->second.url); 
          pstmt->setString(2, it->second.hash); 
          pstmt->setInt(3, it->second.id); 
    
          try { 
           pstmt->executeUpdate(); 
           ++i; 
    
          } catch (sql::SQLException &e) { 
           cerr << "# ERR: SQLException in " << __FILE__; 
           cerr << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl; 
           cerr << "# ERR: " << e.what(); 
           cerr << " (MySQL error code: " << e.getErrorCode(); 
           cerr << ", SQLState: " << e.getSQLState() << ")" << endl; 
          } 
    
          url_queue.erase(it++); 
         } 
         else { 
          ++it; 
         } 
        } 
    
        delete pstmt; 
    
        cout << "~commitChangesToDatabase()" << endl; 
    
        pthread_mutex_unlock(&itemMutex); 
        pthread_mutex_unlock(&dbCommitMutex); 
    } 
    
    // this function is defined in another file but is written here just to show the contents of it 
    sql::PreparedStatement *dbPrepareStatement(const std::string &query) 
    { 
        return con->prepareStatement(query); 
    } 
    

    편집 :

    일부 그러나 나는 그것을 배제하지만 반복을 데이터베이스에서 작동하지만 모든 주석 한 문제가 url_queue 수집을 통해 반복으로 믿는 것 같다. 또한 여기에서 반복은 단순화 된 (그러나 작동하는) 원본 버전을 사용하여지도에서 항목을 선택하고 벡터를 던져지도에서 지우며 프로그램의 해당 부분이 정상적으로 작동합니다. - 은 데이터베이스를 사용할 때마다 충돌합니다.

    for (it = url_queue.begin(); it != url_queue.end();) 
    { 
        if (it->second.state == 2) 
        { 
         update_item.type = (!it->second.was_redirected ? 1 : 2); 
         update_item.item = it->second; 
    
         updates.push_back(update_item); 
    
         url_queue.erase(it++); 
        } 
        else { 
         ++it; 
        } 
    } 
    

    편집 2 :

    valgrind --leak-check=yes에서

    출력 : http://pastie.org/private/2ypk0bmawwsqva3ikfazw

  • +0

    'queue_item_t'의 선언을 질문에 추가 할 수 있습니까? –

    +0

    또한 반복하는 컬렉션에서 항목을 지우지 않아야합니다. iterator를 무효화 할 수 있습니다. 또한 반복자를 두 번 늘리면 의도적으로 두 번째 항목을 건너 뛰는 것입니까? –

    +0

    컬렉션에서 지우기 방법을 발견했습니다. 반복 할 때마다 컬렉션의 .begin() 및 .end()를 평가할 것을 제안하는 StackOverflow에서 반복합니다. 죄송합니다. 이중 증가는 문제에 영향을 미치지 않은 실수였습니다. for (~;)의 ++는 거기에 있지 않아야합니다. –

    답변

    0

    반복자가 불필요하게 증가 보인다; 루프 본문에서 처음으로, for 문에서. 이 코드에서는 end 반복자를 증가시킬 수 있습니다. 이는 문제가되는 작업이며 문제의 원인 일 수 있습니다.

    다음 루프 구조는이 경우에 더 적합 :
    it = url_queue.begin();
    while(it != url_queue.end()){ //loop body }

    +0

    나는이 문제가 아니라는 것을 보여주기 위해 내 질문을 편집했다. 나는 이미 프로그램의이 부분을 범인으로 배제했다. –

    +0

    그런 다음 확인하십시오. 나는 그들이 더 적용 가능한지 확실하지 않은 두 개의 메모가 더 있습니다. 먼저; 'conn-> prepareStatement()'의 출력을 검사하는 것이 좋은 방법이지만, 여전히 실패 할 수도 있습니다. 둘째 :'url_queue.size()'는 뮤텍스 보호 블록 안에 있어야합니다. – xaero99

    0

    가 나는 반복자와 혼란에 좋은 아이디어라고 생각하지 않습니다.

    else { 
         ++it; 
        } 
    

    에 의해 :

    else continue; 
    

    를하거나 제거 교체합니다.

    +0

    내 질문에 대한 첫 번째 편집에서 쓴 것처럼 반복기 논리가 작동합니다. valgrind는 데이터베이스 API의 오류 만 표시하지만 valgrind는 응용 프로그램의 다른 영역에서 작동합니다. 왜이 기능에서 작동하지 않습니다. 필자가 이해할 수있는 것처럼 iterator를 증가시키는 것이 다음 요소로 넘어갈 수있는 유일한 방법이다. –

    관련 문제