LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 02-27-2014, 04:39 AM   #1
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Rep: Reputation: Disabled
how to solve this multiple linux timer with single signal handler ?


/*The handler checks that the value stored in sival_ptr matches a given timerID variable. The sival_ptr is the same as the one we set in makeTimer(), though here it lives in a different structure. Obviously, it got copied from there to here on the way to this signal handler. The point is that the timerID is what is used to determine which timer just went off and determine what to do next */

static void timerHandler( int sig, siginfo_t *si, void *uc )
{
timer_t *tidp;

tidp = si->si_value.sival_ptr;

if ( *tidp == firstTimerID )

TASK1(Task2ms_Raster);
else if ( *tidp == secondTimerID )
TASK2(Task10ms_Raster);
else if ( *tidp == thirdTimerID )
TASK3(Task100ms_Raster);
}

/* The function takes a pointer to a timer_t variable that will be filled with the timer ID created by timer_create(). This pointer is also saved in the sival_ptr variable right before calling timer_create(). In this function notice that we always use the SIGRTMIN signal, so expiration of any timer causes this signal to be raised. The signal handler I've written for that signal is timerHandler. */

static int makeTimer( char *name, timer_t *timerID, int expireMS, int intervalMS )
{
//sigset_t mask;
struct sigevent te;
struct itimerspec its;
struct sigaction sa;
int sigNo = SIGRTMIN;

/* Set up signal handler. */
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timerHandler;
sigemptyset(&sa.sa_mask);
if (sigaction(sigNo, &sa, NULL) == -1)
{
perror("sigaction");
}

/* Set and enable alarm */
te.sigev_notify = SIGEV_SIGNAL;
te.sigev_signo = sigNo;
te.sigev_value.sival_ptr = timerID;
timer_create(CLOCK_REALTIME, &te, timerID);

its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = intervalMS * 1000000;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = expireMS * 1000000;
timer_settime(*timerID, 0, &its, NULL);


return 1;
}

int main()
{

makeTimer("First Timer", &firstTimerID, 2, 2); //2ms

makeTimer("Second Timer", &secondTimerID, 10, 10); //10ms
makeTimer("Third Timer", &thirdTimerID, 100, 100); //100ms


}
I am using single handler to call the task for every 2ms, 10ms and 100ms. I am using multiple timers with single signal handler. How to identify which timer just went. In debug mode : the last debug control session is : static void timerHandler( int sig, siginfo_t *si, void *uc ){ . Could anyone help me how to solve this ?
You can find more info here : http://www.graphics-muse.org/wp/?p=868

Last edited by hemanth1989; 02-27-2014 at 04:55 AM.
 
Old 02-27-2014, 03:06 PM   #2
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,882
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
It works fine, you can't exit main() though, because that kills the timers. Try this copy instead, remember to compile with something like:
Code:
gcc -o <output-filename> -lrt <c-filename>
My point there being that you have to link with the real-time timer library, hence the -lrt (DASH-EL-ARE-TEE)

Note also that I changed make_timer() by way of taking out the char *name, because it wasn't being used.

Code:
#include <stdio.h>
#include <signal.h>
#include <time.h>

timer_t firstTimerID;
timer_t secondTimerID;
timer_t thirdTimerID;

static void timerHandler( int sig, siginfo_t *si, void *uc )
{
    timer_t *tidp;

    tidp = si->si_value.sival_ptr;

    if ( *tidp == firstTimerID )
        printf("2ms");
    else if ( *tidp == secondTimerID )
        printf("10ms\n");
    else if ( *tidp == thirdTimerID )
        printf("100ms\n\n");
}

static int makeTimer( timer_t *timerID, int expireMS, int intervalMS )
{
    struct sigevent te;
    struct itimerspec its;
    struct sigaction sa;
    int sigNo = SIGRTMIN;

    /* Set up signal handler. */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timerHandler;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sigNo, &sa, NULL) == -1) {
        perror("sigaction");
    }

    /* Set and enable alarm */
    te.sigev_notify = SIGEV_SIGNAL;
    te.sigev_signo = sigNo;
    te.sigev_value.sival_ptr = timerID;
    timer_create(CLOCK_REALTIME, &te, timerID);

    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = intervalMS * 1000000;
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = expireMS * 1000000;
    timer_settime(*timerID, 0, &its, NULL);

    return 1;
}

int main()
{
    makeTimer(&firstTimerID, 2, 2); //2ms
    makeTimer(&secondTimerID, 10, 10); //10ms
    makeTimer(&thirdTimerID, 100, 100); //100ms

    while(1)
        ;;
}
Sample Compile and output: (Note: I called my source timers.c)
Code:
~/testcode$ gcc -o timers -lrt timers.c
~/testcode$ ./timers 
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
100ms

2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
100ms

2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms2ms2ms10ms
2ms2ms2ms^C

Last edited by rtmistler; 02-27-2014 at 03:10 PM.
 
Old 02-27-2014, 03:28 PM   #3
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Original Poster
Rep: Reputation: Disabled
Thank you very much for the reply. But did you see the comments in the link :
http://www.graphics-muse.org/wp/?p=868

if we are printing some values or doing some calcuation then it is working fine but if you perform memory allocating then it is throqing error.
i am creating multiple timers, say 3 timers from a single process and all are supposed to land in single Timer handler.
But its giving bus error and "si->si_value.sival_ptr" ==> is of junk value and some how it is getting corrupted.

Please help me out.

Last edited by hemanth1989; 02-27-2014 at 03:37 PM.
 
Old 02-27-2014, 03:43 PM   #4
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,882
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
Quote:
Originally Posted by hemanth1989 View Post
Thank you very much for the reply. But did you see the comments in the link :
http://www.graphics-muse.org/wp/?p=868

i am creating multiple timers, say 3 timers from a single process (Ex: main ()). and all are supposed to land in single Timer handler.
But its giving bus error and "si->si_value.sival_ptr" ==> is of junk value and some how it is getting corrupted.

Please help me out.
That code which I placed there is literally your original code as posted with some modifications and it does exactly what you say, it creates 3 timers from a single process and it's not giving any bus error.

First, compile what I posted for you and see for yourself.

Second, if you're having trouble converting that example into your own specific case, then post exactly your code, the whole file, within [code][/code] tags, and also post representative output either from compiler/link errors you can't get past, or execution output which you can't get past, also within [code][/code] tags.

Note also that one does not need to have timer tasks as yet to prove that this works, I've done that with printf() statements. Therefore try that first, debug getting three timers working. This can also all be debugged using gdb, just put -ggdb within your compilation line and invoke your program using gdb.

However, consider that I've already tested, and then copied/pasted a working copy in my reply.
 
Old 02-28-2014, 12:54 AM   #5
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Original Poster
Rep: Reputation: Disabled
#include <features.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define million 1000000L

timer_t firstTimerID, secondTimerID, thirdTimerID;
double Task2ms_Raster, Task10ms_Raster, Task100ms_Raster;

struct sockaddr_in addr, client;
int acceptSocket;
char buf[256];
long rc, sentbytes;
int port = 18037;






void TASK1(Task2ms_Raster)
{

struct timespec start, stop;
double startTime, stopTime;

if( (startTime = clock_gettime( CLOCK_REALTIME, &start)) == -1 ) {
perror("clock gettime");

}

startTime =start.tv_sec + 0.0000001 * start.tv_nsec;
printf("start time is %lf", startTime);


// return EXIT_SUCCESS;

/* Trigger DAQ for the 2ms XCP raster. */
if( XCPEVENT_DAQ_OVERLOAD & Xcp_DoDaqForEvent_2msRstr( ))
{
++numDaqOverload2ms;
}

/* Update those variables which are modified every 2ms. */
counter32 += slope32;

/* Trigger STIM for the 2ms XCP raster. */
if( enableBypass2ms )
{
if( XCPEVENT_MISSING_DTO & Xcp_DoStimForEvent_2msRstr( ) )
{
++numMissingDto2ms;
}
}
if( (stopTime = clock_gettime( CLOCK_REALTIME, &stop)) == -1 ) {
perror( "clock gettime" );

}
stopTime = stop.tv_sec + 0.0000001 * stop.tv_nsec;
printf("stop time is %lf", stopTime);

duration2ms = (stopTime- startTime);
printf( "time difference for task1 is= %ld\n", duration2ms );



}

void TASK2(Task10ms_Raster)
{
struct timespec start, stop;

double startTime, stopTime;
if( (startTime = clock_gettime( CLOCK_REALTIME, &start)) == -1 ) {
perror( "clock gettime" );

}

startTime =start.tv_sec + 0.0000001 * start.tv_nsec;
printf("start time is %lf", startTime);


/* Trigger DAQ for the 10ms XCP raster. */
if( XCPEVENT_DAQ_OVERLOAD & Xcp_DoDaqForEvent_10msRstr( ))
{
++numDaqOverload10ms;
}

/* Update those variables which are modified every 10ms. */
counter16 += slope16;

/* Trigger STIM for the 10ms XCP raster. */
if( enableBypass10ms )
{
if( XCPEVENT_MISSING_DTO & Xcp_DoStimForEvent_10msRstr( ) )
{
++numMissingDto10ms;
}
}

if( (stopTime = clock_gettime( CLOCK_REALTIME, &stop)) == -1 ) {
perror( "clock gettime" );

}

stopTime = stop.tv_sec + 0.0000001 * stop.tv_nsec;
printf("stop time is %lf", stopTime);


duration10ms = ( stop.tv_sec - start.tv_sec )
+ (double)( stop.tv_nsec - start.tv_nsec )
/ (double)million;
printf( "time difference for task2 is= %ld\n", duration10ms );
}


void TASK3(Task100ms_Raster)
{
struct timespec start, stop;
double startTime, stopTime;



if( (startTime = clock_gettime( CLOCK_REALTIME, &start)) == -1 )
{
perror("clock gettime");

}
startTime =start.tv_sec + 0.0000001 * start.tv_nsec;
printf("start time is %lf", startTime);

/* Trigger DAQ for the 100ms XCP raster. */
if( XCPEVENT_DAQ_OVERLOAD & Xcp_DoDaqForEvent_100msRstr( ))
{
++numDaqOverload100ms;
}

/* Update those variables which are modified every 100ms. */
counter8 += slope8;


/* Trigger STIM for the 100ms XCP raster. */
if( enableBypass100ms )
{
if( XCPEVENT_MISSING_DTO & Xcp_DoStimForEvent_100msRstr( ) )
{
++numMissingDto100ms;
}
}



if((stopTime = clock_gettime( CLOCK_REALTIME, &stop)) == -1 ) {
perror( "clock gettime" );

}

stopTime = stop.tv_sec + 0.0000001 * stop.tv_nsec;
printf("stop time is %lf", stopTime);

Xcp_CmdProcessor();

duration100ms = ( stop.tv_sec - start.tv_sec )
+ (double)( stop.tv_nsec - start.tv_nsec )
/ (double)million;
printf( "time difference for task3 is= %ld\n", duration100ms );
}

/*The handler checks that the value stored in sival_ptr matches a given timerID
variable. The sival_ptr is the same as the one we set in makeTimer(),
though here it lives in a different structure.
Obviously, it got copied from there to here on the way to this signal handler.
The point is that the timerID is what is used to determine which timer just went off
and determine what to do next */


static void timerHandler( int sig, siginfo_t *si, void *uc )
{
timer_t *tidp;

tidp = si->si_value.sival_ptr;

if ( *tidp == firstTimerID )

TASK1(Task2ms_Raster);
else if ( *tidp == secondTimerID )
TASK2(Task10ms_Raster);
else if ( *tidp == thirdTimerID )
TASK3(Task100ms_Raster);
}

/*
The function takes a pointer to a timer_t variable that will be filled with the
timer ID created by timer_create(). This pointer is also saved in the sival_ptr
variable right before calling timer_create(). In this function notice that we
always use the SIGRTMIN signal, so expiration of any timer causes this signal to
be raised. The signal handler I've written for that signal is timerHandler.
*/

static int makeTimer( char *name, timer_t *timerID, int expireMS, int intervalMS )
{
//sigset_t mask;
struct sigevent te;
struct itimerspec its;
struct sigaction sa;
int sigNo = SIGRTMIN;

/* Set up signal handler. */
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timerHandler;
sigemptyset(&sa.sa_mask);
if (sigaction(sigNo, &sa, NULL) == -1)
{
perror("sigaction");
}

/* Set and enable alarm */
te.sigev_notify = SIGEV_SIGNAL;
te.sigev_signo = sigNo;
te.sigev_value.sival_ptr = timerID;
timer_create(CLOCK_REALTIME, &te, timerID);

its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = intervalMS * 1000000;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = expireMS * 1000000;
timer_settime(*timerID, 0, &its, NULL);


return 1;

}

int callBackTimers()
{
makeTimer("First Timer", &firstTimerID, 2, 2); //2ms
makeTimer("Second Timer", &secondTimerID, 10, 10); //10ms
makeTimer("Third Timer", &thirdTimerID, 100, 100); //100ms
while(1)
;;
}

int CreateSocket()
{


socklen_t len = sizeof(client);
// Socket creation for UDP

acceptSocket=socket(AF_INET,SOCK_DGRAM,0);

if(acceptSocket==-1)

{

printf("Failure: socket creation is failed, failure code\n");

return 1;

}

else

{

printf("Socket started!\n");

}

//non blocking mode
/* rc = ioctl(acceptSocket, FIONBIO, (char *)&flag);
if (rc < 0)
{
printf("\n ioctl() failed \n");
return 0;
}*/

//Bind the socket
memset(&addr, 0, sizeof(addr));

addr.sin_family=AF_INET;

addr.sin_port=htons(port);

addr.sin_addr.s_addr=htonl(INADDR_ANY);

rc=bind(acceptSocket,(struct sockaddr*)&addr,sizeof(addr));

if(rc== -1)

{

printf("Failure: listen, failure code:\n");

return 1;

}

else

{

printf("Socket an port %d \n",port);

}


if(acceptSocket == -1)
{
printf("Fehler: accept, fehler code:\n");

return 1;
}
else
{

while(rc!=-1)
{


rc=recvfrom(acceptSocket,buf, 256, 0, (struct sockaddr*) &client, &len);
if(rc==0)
{
printf("Server has no connection..\n");
break;
}
if(rc==-1)
{
printf("something went wrong with data %s", strerror(errno));
break;
}


XcpIp_RxCallback( (uint16) rc, (uint8*) buf, (uint16) port );
callBackTimers(); // whenever it recieves the data, I am calling this to start the timer . I think this is not the right way do. Could you suggest me some solution?

}


}

close(acceptSocket);



return 0;

}






int main()
{

Xcp_Initialize();
CreateSocket();
return 1;

}


// sending the data back to the client

void XcpApp_IpTransmit( uint16 XcpPort, Xcp_StatePtr8 pBytes, uint16 numBytes )
{


if ((long)XcpPort==port){
sentbytes = sendto(acceptSocket,(char*)pBytes,(long)numBytes,0, (struct sockaddr*)&client, sizeof(client));
}
XcpIp_TxCallback(port,(uint16)sentbytes);
}
I am working on a client and server architecture. Server code is shown above and I created a socket to recieve the request from the client via the ip address and port number. Server is waiting for a request from the client and send a response back to the client. when ever it recieves data from the client, it should call the timer task, For that I also created timer to call the task for every 2ms, 10ms and 100ms.
 
Old 02-28-2014, 06:19 AM   #6
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,882
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
The man page for timer_settime(2):

Quote:
The new_value->it_interval field specifies the period of the timer,
in seconds and nanoseconds. If this field is nonzero, then each time
that an armed timer expires, the timer is reloaded from the value
specified in new_value->it_interval. If new_value->it_interval
specifies a zero value then the timer expires just once, at the time
specified by it_value.
This same information applies to timer_create() however that man page does not discuss it_interval.
 
Old 02-28-2014, 06:36 AM   #7
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Original Poster
Rep: Reputation: Disabled
thank you very much.
I think, just have to modify itto :
its.it_interval.tv_sec = 1; in my code.

is it right ??
 
Old 02-28-2014, 06:38 AM   #8
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,882
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
I would say zero because you are always calling timer_create(), therefore you need no reloading of the timer, you always require the timers to be one-shot.
 
Old 02-28-2014, 06:40 AM   #9
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Original Poster
Rep: Reputation: Disabled
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = intervalMS * 1000000;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = expireMS * 1000000;
timer_settime(*timerID, 0, &its, NULL);


Is this right ??
 
Old 02-28-2014, 06:43 AM   #10
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,882
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
No. All of the it_interval structure should be zero.
 
Old 02-28-2014, 06:44 AM   #11
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Original Poster
Rep: Reputation: Disabled
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = expireMS * 1000000;
timer_settime(*timerID, 0, &its, NULL)

is this right ??
 
1 members found this post helpful.
Old 02-28-2014, 06:50 AM   #12
hemanth1989
Member
 
Registered: Dec 2013
Posts: 39

Original Poster
Rep: Reputation: Disabled
I have to call the task repeatedly for every 2ms, 10ms and 100ms. If i do like above , is it right for my case ??
 
Old 02-28-2014, 06:51 AM   #13
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,882
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
I believe so, and you should be trying this code versus repeatedly asking about syntax for something you were shown the reference too. Bear in mind that this may not be your sole problem, it was something I noticed. In the future, if you have problems, use the proper tags to show your code, those being [code][/code] tags. You can see the obvious result if you preview your post, and also in my earlier answers. The other thing to do is not show all your code, say "Help Me!", and then leave it at that, but instead to try to be as clear as possible exactly where you're stuck. Finally, if you're not showing the output, or a detailing of your steps to diagnose this yourself, eventually you're not going to get further help. Guess what? I'm at that point, you're repeatedly asking me about it_interval when it was explained in that post showing the manual page.
 
Old 06-03-2014, 03:51 AM   #14
abhi.ckd
LQ Newbie
 
Registered: Jun 2014
Posts: 1

Rep: Reputation: Disabled
How to have multiple timers as well as different handler functions as well..

For example..

i would like to have multiple timers like
makeTimer(timerid1,func1,intervalMs);
makeTimer(timerid2,func2,intervalMs);
makeTimer(timerid3,func3,intervalMs);


and func1,func2,func3 should be called respectively on their timer expiry time...
 
  


Reply

Tags
linux mint 9, timer, udp



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] catching multiple signals in a single handler in linux/C wch705 Programming 2 04-26-2011 06:32 AM
The terminal receives -1 after signal handler is invoked on timer expire. shylendra.kumar Linux - Software 1 09-11-2009 04:27 AM
user-define signal handler & coredump for signal 11(SIGSEGV) Alexlun Linux - Software 2 05-24-2009 06:37 AM
Custom Timer Interrupt Handler Linux 2.4x ratwings Linux - Software 2 01-13-2005 01:51 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie

All times are GMT -5. The time now is 03:48 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration