AIO를 사용한 서버 구성시 메모리 에러

psw9255439의 이미지

리눅스의 Posix AIO를 사용하여 간단한 서버를 만들어보았습니다.
클라이언트가 접속을 요청하면 aiocb 구조체를 동적할당하며 aio_read()함수를 호출하는 방식입니다.
그런데 동적 할당 후 aio_read()함수 실행 시 invalid argument에러를 출력합니다.
원인을 찾아보던 중 동적할당한 aiocb 구조체의 메모리를 해제한 것이 문제였습니다.
delete를 통해 메모리를 해제하는 부분을 주석처리하고 해제하는 메모리를 출력하도록 하였더니
정상동작하는 것을 확인했습니다.
delete를 통해 수동으로 메모리 해제 시 왜 문제가 발생하는지 궁금합니다.

서버 소스입니다.
/* Header */
#include
#include
#include
#include
#include
// AIO Header
#include
#include
// Custom Header
#include "header/Decl.h"

using namespace std;

/* Global variable */
SOCKET SERVERSOCKET;

/* Function declare */
void ErrorExit( const char* );
// Function related in socket
const int CreateTCPSocket( void );
void InitTCPSocket( const SOCKET, const int );
// Function related in AIO
void SetAiocb( struct aiocb*, const SOCKET );
void AioHandler( union sigval );

/* Main function */
int main( int argc, char *argv[] )
{
/*
if ( argc != 2 )
{
cout << "Usage: " << argv[0] << " " << endl;
exit(1);
}*/

// Socket
SOCKET sock = CreateTCPSocket();
// InitTCPSocket( sock, atoi( argv[1] ) );
InitTCPSocket( sock, 8888 );
SERVERSOCKET = sock;

struct aiocb cb;
SetAiocb( &cb, sock );
if ( aio_read( &cb ) < 0 )

ErrorExit( "aio_read() failed in main()" );

while ( true )
{
cout << "I'm sleeping" << endl;
sleep(100);
}

delete[] reinterpret_cast< char* >( const_cast< void* >( cb.aio_buf ) );
return 0;
}

/* Function definition */
void ErrorExit( const char *str )
{
cout << "[Error] " << str << endl;
exit(1);
}

// Function related in socket
const int CreateTCPSocket( void )
{
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock < 0 )
ErrorExit("[Error] socket() fail");

cout << "socket() success" << endl;
return sock;
}
void InitTCPSocket( const SOCKET sock, const int port )
{
struct sockaddr_in ServerAddr; memset( &ServerAddr, 0, sizeof( ServerAddr ) );
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons( port );
ServerAddr.sin_addr.s_addr = htonl( INADDR_ANY );

if ( bind( sock, reinterpret_cast< sockaddr* >( &ServerAddr ), sizeof( ServerAddr ) ) < 0 )
ErrorExit( "bind() fail" );
cout << "bind() success" << endl;

if ( listen( sock, CONCURRENT) < 0 )
ErrorExit( "listen() fail" );
cout << "listen() success" << endl;
}

// Function related in AIO
void SetAiocb( struct aiocb *cb, const SOCKET fd )
{
/*
* Type of cb->aiobuff is volatile void*.
* but return value of new char[ BUFFSIZE ] is char*.
* thus reinterpret_cast is used
*/
cb->aio_buf = reinterpret_cast< void* >( new char[ BUFFSIZE ] );
cb->aio_nbytes = BUFFSIZE;
cb->aio_fildes = fd;
cb->aio_offset = 0;
cb->aio_sigevent.sigev_notify = SIGEV_THREAD;
cb->aio_sigevent.sigev_notify_function = AioHandler;
cb->aio_sigevent.sigev_notify_attributes = NULL;
cb->aio_sigevent.sigev_value.sival_ptr = cb;
}
void AioHandler( union sigval sig)
{
struct aiocb *cb = reinterpret_cast< aiocb* >( sig.sival_ptr );
SOCKET sock = cb->aio_fildes;

if ( sock == SERVERSOCKET )
{
struct sockaddr_in ClientAddr;
socklen_t ClientAddrLen = sizeof( ClientAddr );
memset( &ClientAddr, 0, ClientAddrLen );
SOCKET ClientSock = accept( sock, reinterpret_cast< sockaddr* >( &ClientAddr ), &ClientAddrLen );
if ( ClientSock < 0 )
cout << "The client attempted to connect but the accept() failed";
else
cout << "Connected" << endl;

struct aiocb *ClientAio = new aiocb;
SetAiocb( ClientAio, ClientSock );

if ( aio_read( cb ) < 0 )
ErrorExit("Error occurred! aio_read( cb ) failed in ServerSocket AioHandler");

if ( aio_read( ClientAio ) < 0 )
{
cout << "==========Failed CB Info==========" << endl;
cout << "ClientAio->aio_buf " << sizeof( ClientAio->aio_buf ) << endl;
cout << "ClientAio->aio_nbytes " << ClientAio->aio_nbytes << endl;
cout << "ClientAio->aio_fildes " << ClientAio->aio_fildes << endl;
cout << "ClientAio->aio_offset " << ClientAio->aio_offset << endl;
cout << "ClientAio->aio_sigevent.sigev_notify " << ClientAio->aio_sigevent.sigev_notify << endl;
cout << "CientAio->aio_sigevent.sigev_notify_function " << ClientAio->aio_sigevent.sigev_notify_function << endl;
cout << "ClientAio->aio_sigevent.sigev_notify_attributes " << ClientAio->aio_sigevent.sigev_notify_attributes << endl;
cout << "ClientAio->aio_sigevent.sigev_value.sival_ptr " << ClientAio->aio_sigevent.sigev_value.sival_ptr << endl;

close( ClientSock );
// delete ClientAio;
cout << "Error occurred! aio_read( ClientAio ) failed in ServerSocket AioHandler" << endl;
}
else
{
cout << "==========Success CB Info==========" << endl;
cout << "ClientAio->aio_buf " << sizeof( ClientAio->aio_buf ) << endl;
cout << "ClientAio->aio_nbytes " << ClientAio->aio_nbytes << endl;
cout << "ClientAio->aio_fildes " << ClientAio->aio_fildes << endl;
cout << "ClientAio->aio_offset " << ClientAio->aio_offset << endl;
cout << "ClientAio->aio_sigevent.sigev_notify " << ClientAio->aio_sigevent.sigev_notify << endl;
cout << "ClientAio->aio_sigevent.sigev_notify_function " << ClientAio->aio_sigevent.sigev_notify_function << endl;
cout << "ClientAio->aio_sigevent.sigev_notify_attributes " << ClientAio->aio_sigevent.sigev_notify_attributes << endl;
cout << "ClientAio->aio_sigevent.sigev_value.sival_ptr " << ClientAio->aio_sigevent.sigev_value.sival_ptr << endl;
}
}
else
{
cout << "Client process routing start " << sock << endl;
/*
* Type of cb->aiobuff is volatile void*.
* but i do not need volatile. i just need socket buffer.
* thus cast volatile void* to void*
* and cast void* to char*
*/
char *buff = reinterpret_cast< char* >( const_cast< void* >( cb->aio_buf ) );
size_t bufflen = aio_return(cb);
cout << "Read data length is " << bufflen << endl;

struct Packet RecvPacket;
RecvPacket.type = buff[0];
strcpy( RecvPacket.username, &buff[1] );
strcpy( RecvPacket.password, &buff[65] );

if ( RecvPacket.type == Request::Login )
{
cout << "Client requests login" << endl;
cout << "Username: " << RecvPacket.username << endl;
cout << "Password: " << RecvPacket.password << endl;
write( sock, "Login Success", sizeof( "Login Success" ) );
}
else if ( RecvPacket.type == Request::SignUP )
{
cout << "Client requests signup" << endl;
cout << "Username: " << RecvPacket.username << endl;
cout << "Password: " << RecvPacket.password << endl;
write( sock, "Sign Up Success", sizeof( "Sign Up Success" ) );
}
else
cout << "Client sent unknown type " << RecvPacket.type << endl;

close( sock );

/* #################
* # Problem Point #
* #################
*/
// delete cb;
cout << "delete cb " << cb << endl;
cout << "Connection Close" << endl;
}
}

peecky의 이미지

.

bushi의 이미지

callback 에서 자기 자신을 없애버리는게 타당한지 모르겠습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.