beagley
|
posted on 15/12/11 at 02:46 PM |
|
|
OT - Any Linux Programmers in the house?!
Working on an "opportunity" at work where I need a multi-threaded application to listen for N connections coming in. I am getting a
"Segmentation Fault" error when I try to run the program from the command line, but when debugging it executes exactly like it should be.
I'm basically stuck at lik 98% completion on this project and frankly I'm tired of beating my head against a wall to get it to work.
Since this request is a REAL reach I won't post the code up unless anyone is stup.... ahem.... brave enough to offer help.
Thanks,
Beags
I'm not scared!!! I'm just marking my territory.
|
|
|
pewe
|
posted on 15/12/11 at 03:10 PM |
|
|
Beags, my in-house techie (aka No2 son) has done some Linux development and asks for more info such as:-
First what programming language?
When does it segment fault - straight away?
What distribution are you using?
Are you calling any external progs. or libraries?
Anything else you think can help as he's saying it could be numerous causes?
Maybe best if the two of you correspond by email or skype (he's around today) - if you want to talk to him let me have yours by U2U.
Cheers, Pewe
|
|
beagley
|
posted on 15/12/11 at 03:15 PM |
|
|
quote: Originally posted by pewe
Beags, my in-house techie (aka No2 son) has done some Linux development and asks for more info such as:-
First what programming language?
When does it segment fault - straight away?
What distribution are you using?
Are you calling any external progs. or libraries?
Anything else you think can help as he's saying it could be numerous causes?
Maybe best if the two of you correspond by email or skype (he's around today) - if you want to talk to him let me have yours by U2U.
Cheers, Pewe
U2U sent.....
For any other that are wondering the same information....
First what programming language?
C++
When does it segment fault - straight away?
Yep, program compiles fine, but bails out immediately
What distribution are you using?
SUSE Linux Enterprise 11.4
Are you calling any external progs. or libraries?
Not calling any external programs, and have included all of the necessary libraries as well as linked them when using g++ to compile the program into
a binary.
Thanks again for the help.
Beags
I'm not scared!!! I'm just marking my territory.
|
|
pewe
|
posted on 15/12/11 at 03:32 PM |
|
|
Beags, he's just gone off to Post Office but says he'll contact you on his return.
Cheers, Pewe.
|
|
andy350z
|
posted on 15/12/11 at 04:02 PM |
|
|
Try running a dtrace on it :
dtrace -Ff yourprogram 2> trace.log
You can then look in trace.log to try and find where it seg faulted and work your way back up the trace looking for errors.
Andy
|
|
MikeRJ
|
posted on 15/12/11 at 04:36 PM |
|
|
Have you tried the "back to basics" debugging; lots of printf statements so you can find out how far execution progresses before the
segfault?
Are you dynamically allocating any memory early on in in your code? I had something similar years back that took ages to find, but was obvious in
retrospect:
something like:
buffer = malloc( length + 1 * sizeof(int) )
When I really meant
buffer = malloc( ( length + 1 ) * sizeof(int) )
A Linux guru at work says it's worth trying Valgrind
|
|
beagley
|
posted on 16/12/11 at 05:26 AM |
|
|
quote: Originally posted by MikeRJ
Have you tried the "back to basics" debugging; lots of printf statements so you can find out how far execution progresses before the
segfault?
Are you dynamically allocating any memory early on in in your code? I had something similar years back that took ages to find, but was obvious in
retrospect:
something like:
buffer = malloc( length + 1 * sizeof(int) )
When I really meant
buffer = malloc( ( length + 1 ) * sizeof(int) )
A Linux guru at work says it's worth trying Valgrind
Ugh.... I remember that from uni.... and yes I did try it. I actually did make some progress on it this afternoon. I was instantiating an array
of thread pointers, BUT, I was not releasing that memory upon exiting the program. I corrected that and made a couple of other small adjustments and
got the program off of life support (debug only mode) and into intensive care (can run from a prompt, but still doesn't behave right).
Now I just need to figure out why some ports are coming back as being blocked even though they aren't. This is so much easier when done in
Windows..... Here is a good analogy for all you locosters:
Developing programs for Windows using Visual Studio = buying/building a kit car
Developing programs for Linux = building a car from scratch while blind folded with your hands tied behind your back and being sprayed from a fire
hydrant.
Thanks for the help all
Beags
I'm not scared!!! I'm just marking my territory.
|
|
Xtreme Kermit
|
posted on 16/12/11 at 09:07 AM |
|
|
Try lsof to look for processes hanging on to ports.
|
|
beagley
|
posted on 18/12/11 at 04:26 PM |
|
|
Thanks for the help all, I *think* that I finally got it figured out. It runs without any problems, I'm just still not sure if its done the
"correct" way or if I just brute forced it. Anyway, I'll post the code up I guess in case anyone is remotely interested.
Thanks again!!! Here is the updated code:
[CODE]
//============================================================================
// Name : ThreadedListener.cpp
// Author : beagley
// Version : 1.0
// Copyright : Your copyright notice
// Description : This program takes in two arguments to establish
// a port range of which a thread will be created
// dedicated to each port listening for any incoming
// TCP/IP data.
//============================================================================
#include <errno.h>
#include <iostream>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace std;
// Declare a global variable that we can use to let us know we need
// to exit this program.
volatile bool KeepRunning = true;
void error(const char *msg)
{
perror(msg);
exit(0);
}
void *ListenForConnection(void *ptr);
// Declare a structure that we can use to pass in any information
// into the thread we are going to kick off. If any new parameters
// are needed simply add them to this "struct" object and they will
// be available within the method that is called via the thread.
struct tcp_connection_params
{
// This is the port number to use
int tcp_port;
};
int main(int argc, char *argv[])
{
int from_port;
int to_port;
// First step is to make sure that the user entered what they
// were supposed to. There should be a total of 3 arguments
// provided.
// 1. Program Name
// 2. Beginning Port Number
// 3. Ending Port Number
if (argc < 3)
{
// It is allowed to have only supply two arguments, which
// would be the command name and a single port from which
// we will use to populate the "to port". In laymans terms
// if the argument count is fewer than 3 AND the count is
// not equal to 2 we should error.
if (argc != 2)
{
error("Usage is <program_name> <starting_port> <ending_port>"
}
}
// At this point we know we at least have the minimum amount
// of arguments needed. Now we will see if there are too many.
if (argc > 3)
{
error("Too many arguments provided"
}
// Once code execution has reached this point we know that we have the
// correct number of arguments. Now we need to validate the port numbers
// to make sure that they won't blow anything up.
from_port = atoi(argv[1]);
if (argc == 2)
{
to_port = atoi(argv[1]);
}
else
{
to_port = atoi(argv[2]);
}
// Congrats, the user entered the correct number of
// arguments, BUT, did they enter ones that we can
// actually use?
if (to_port > from_port)
{
error("Starting port number must be less than or equal to Ending port number"
}
// Well, well, throw a party. The user entered the correct number
// of arguments AND they put them in the correct order.
//
// Let's proceed shall we.....
//
// This variable will be used to help us identify how many
// threads we will be needing to create
int ThreadCount = to_port - from_port;
// Create an array of threads, but we can't specify
// how many threads we will need just yet, so we
// will just declare a pointer of the correct data type
pthread_t *Thread_Array;
// Declare a tcp_connection_params object array pointer
// that we will use to pass in our parameters when we
//create a new thread.
struct tcp_connection_params *Params;
// We need to base our number of threads upon how many
// ports we were told to open
Thread_Array = new pthread_t[ThreadCount + 1];
Params = new tcp_connection_params[ThreadCount + 1];
// Now that we have our array of threads and parameters
// identified we can run through a loop of that amount
// creating new threads with their associated parameters
// dedicated to listening on the specified port number.
for (int i=0; i <= ThreadCount; i++)
{
Params.tcp_port = from_port + i;
pthread_create(&(Thread_Array), NULL, &ListenForConnection, &Params);
pthread_detach(Thread_Array);
}
// Make sure we wait for all of the threads to finish
// working before we exit the program
pthread_exit(NULL);
// Make sure that we free up any memory that was allocated
// for our array objects
free(Thread_Array);
free(Params);
return 0;
}
/* Here is the method that is invoked each time we
create a new thread. It simple will create a
couple of sockets and listen on the passed in
port number for any incoming connections. */
void *ListenForConnection(void* parameters)
{
// This will be used to open our socket on the
// designated port.
int socket_file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
// Once that connection is established we will move it off
// to another socket to free the original up for any other
// connections that may try to access it. For this particular
// case that I am designing this program we will only be having
// a single machine accessing this program on a single port.
int new_socket_file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
// This variable will be used to count the number of characters
// that are sent from the remote system.
int characters_read;
// This is an easier way for us to identify which port we will
// be using to bind to the socket.
int port_number;
// We need to create a boolean value stored as an integer so
// that we can reuse a socket once communication has finished.
int yes = 1;
// Declare a couple of sockaddr_in objects to define
// the server and client when they connect
struct sockaddr_in server_address;
struct sockaddr_in client_address;
socklen_t client_length = sizeof(client_address);
// Here is where we will store our message
char buffer[256];
// create a pointer to a tcp_connection_params object so that
// we can reference any parameters that we passed into this
// method. I chose to pass in a struct so that if any new parameters
// need to be added at a later date I just need to define them in the
// struct which would prevent me from having to come downstream and
// define them at other points in the program.
struct tcp_connection_params* pp = (struct tcp_connection_params*) parameters;
// Set our port number that we are going to open
port_number = pp->tcp_port;
if (socket_file_descriptor < 0)
{
error("Error Opening Socket"
}
else
{
// Make sure that we can reuse the sockets once the remote
// machine has closed the connection.
setsockopt(socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
// Clear the buffer used for the server's address
bzero((char *) &server_address, sizeof(server_address));
// Now that we have cleared that buffer we can populate
// it with our new data
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(port_number);
if (bind(socket_file_descriptor, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
{
cout << "Did not open port: " << port_number << endl;
}
else
{
listen(socket_file_descriptor, 5);
client_length = sizeof(client_address);
while(KeepRunning)
{
setsockopt(new_socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
new_socket_file_descriptor = accept(socket_file_descriptor, (struct sockaddr *) &client_address, &client_length);
bzero(buffer, 256);
n = read(new_socket_file_descriptor, buffer, 255);
if (n > 0)
{
if (buffer[0] == 'A'
{
KeepRunning = false;
close(new_socket_file_descriptor);
close(socket_file_descriptor);
}
cout << buffer << endl;
}
// Make sure that we close the "new" socket that
// we dumped the original connection on so that
// we can repeat those same steps for each
// successive connection.
close(new_socket_file_descriptor);
}
pthread_exit(NULL);
}
}
close(socket_file_descriptor);
return NULL;
}
[/CODE]
Again, a million thanks!!
Beags
I'm not scared!!! I'm just marking my territory.
|
|
scudderfish
|
posted on 18/12/11 at 04:51 PM |
|
|
Going back to basics, do you have to implement it in C++?
|
|
beagley
|
posted on 18/12/11 at 06:58 PM |
|
|
quote: Originally posted by scudderfish
Going back to basics, do you have to implement it in C++?
Well, this is ultimately going to reside on a Linux server, and C++ was close enough to C# which is what I used to protoype the application in. I had
done that prototype on Windows using Visual Studio 2010 and had it done in about two days, it was MUCH easier to do. Once I was able to prove the
concept would work for our system I had to "downgrade" it so to speak so that it would run from a command line natively on Linux which is
why I chose C++. That is where the trouble came in. Windows has all those nice wrappers that go around the classes which makes them so much easier
to program with.
I actually got it solved late Friday afternoon and have been trying to break it since. I started a test about 16:30 sending 7 batches of data from
different sources to my new program every 5-7 seconds and it is still running. Has close to 200,000 records so far without missing a beat, so thats a
bit of a relief.
Thanks again for the help all!
Beags
I'm not scared!!! I'm just marking my territory.
|
|
MikeRJ
|
posted on 19/12/11 at 07:49 PM |
|
|
quote: Originally posted by beagley
[CODE]
...
...
// Make sure we wait for all of the threads to finish
// working before we exit the program
pthread_exit(NULL);
// Make sure that we free up any memory that was allocated
// for our array objects
free(Thread_Array);
free(Params);
return 0;
}
[/CODE]
I'm probably missing something, but surely pthread_exit() in main will cause main to exit before the Thread_Array and Params structures are
freed?
[Edited on 19/12/11 by MikeRJ]
|
|
beagley
|
posted on 19/12/11 at 08:46 PM |
|
|
From the documentation I found Here
"To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3)."
I haven't encountered ay issues with it so far. I ran a test all weekend sending 250,000 records and i didn't lose a single one and the
program was able to exit normally this morning when I got into work.
Anyway.....
Thanks again!
I'm not scared!!! I'm just marking my territory.
|
|
scudderfish
|
posted on 20/12/11 at 09:53 AM |
|
|
quote: Originally posted by MikeRJ
quote: Originally posted by beagley
[CODE]
...
...
// Make sure we wait for all of the threads to finish
// working before we exit the program
pthread_exit(NULL);
// Make sure that we free up any memory that was allocated
// for our array objects
free(Thread_Array);
free(Params);
return 0;
}
[/CODE]
I'm probably missing something, but surely pthread_exit() in main will cause main to exit before the Thread_Array and Params structures are
freed?
[Edited on 19/12/11 by MikeRJ]
The way I read that and the docs, the process will terminate at the call to pthread_exit, the following code will never be run. However in the
process clean up the memory allocated to Thread_Array and Params will be released.
|
|
beagley
|
posted on 20/12/11 at 09:58 AM |
|
|
After going over the code some more I think you guys are correct. Pthread_exit() is the wrong method to be calling. Thankfully due to poor design
it's not being called at the moment. This was a quick and dirty fix for some communication problems we've been having at my facility.
Thanks for providing all of the extra eye on this code, really helps to have that.
Beags
I'm not scared!!! I'm just marking my territory.
|
|
scudderfish
|
posted on 20/12/11 at 04:14 PM |
|
|
Incidentally, did you have a look at Mono (http://www.mono-project.com/Main_Page), and implementation of C# on things other than Windows?
|
|