首页 > BGA rework station > Using signals in Unix/C environment, prototype

Using signals in Unix/C environment, prototype

发表于:2009-06-27 20:04:26   点击: 320

SMT产品,回流焊,BGA返修台——威力泰网上商城

I wrote a prototype and would love some code review (on the technique not on my style... it is a prototype so style is not primary concern).

I have a use case where I need to synchronize time among many processes, gateways and devices (I am going to be real vague about gateway and device just assume some random hardware that does some random things).

There are two modes to this as follows:

  • Every two minutes we should synchronize clocks (actually we just store the current offset of time)
  • If the user (this user would be an admin) goes to the GUI to set the system time of this embedded device then we need to fire off an signal to synchronize now instead of waiting two minutes

My first inclination was to start up a thread and sleep for 2 minutes, then process the time sync, then sleep, then process the time sync. Then since we are using OS message queues throughout the system, to just have another message queue that the application fires a message to this daemon that then propagates the time sync (the timer lives in the daemon along with a lot of other stuff).

Then, I thought, why not just use signal handling. I can ask the OS for a signal every 2 minutes and then process the time sync there. But, with signal handling you need to return quickly because it suspends all other threads in your application so since this process could take a second or two (or longer), I have to fire off a thread anyway so the signal handler returns near immediately. Then there is the matter of how does the GUI signify that it has updated the system time.

Easy enough you say, just raise a signal from the GUI to the daemon that houses the timer. Then the next question is how do I get the PID of the daemon to raise the signal. There is no standard Unix/C/Posix way to get a list of process ids (PIDS). It can't be done. In order to get the PIDS, you need to run "ps -ae". (Please prove me wrong because I really want a standard way to get running PIDS or at least a way that will work on all flavors of Linux.... ah hell... how about a way to do this on all Ubuntu boxes.) Here is the code to find the PID of the daemon process:

typedef struct pidInfo {
char name[80];
int id;
} PidInfo;

int find_pid(char* processName, PidInfo* foundPid) {


FILE *fp; //file pointer to open "ps -ae" process
int status; //generic return status holder
char line[LINE_SIZE]; //current line that we are reading

errno = 0;
fp = popen("ps -ae", "r");
if (fp == NULL) {
perror("Unable to open command");
return -1;
}

while (fgets(line, LINE_SIZE, fp) != NULL) {
PidInfo pidInfo = parse_pid(line); //parse current line into struct
if (strcmp(pidInfo.name, processName)==0) {
foundPid->id = pidInfo.id;
strcpy(foundPid->name, pidInfo.name);
}
}


status = pclose(fp);
if (status == -1) {
perror("Unable to close command");
return -1;
}

return 0;

}

PidInfo parse_pid(char* line) {
char *spid = strtok (line, " ");
strtok(NULL, " "); //tty
strtok(NULL, " "); //time
char *name = strtok(NULL, " \n");
PidInfo pidInfo;
strcpy(pidInfo.name, name);
pidInfo.id = atoi(spid);
return pidInfo;
}


So I implemented the prototype, but then I noticed that there is a possible contention if both the GUI initiates the time sync and the OS initiates the time sync (both by rasing a OS signal). I tried adding a signal mask, but it seems you can't add a mask to a signal that you are currently handling (this is not mention anywhere in the documentation that I could find). Now I could be wrong about this, but this is the behavior I observed while trying to add the signal mask in the signal handler for the signal alarm as follows (read comment):

/*
* This does not work. It could be an API usage problem, or
* it could just be that you cannot mask an alarm when
* you are in the alarm handler. No error code or message is
* produced. It just silently fails.
*/
void turn_off_signal_alarms_while_in_handler() {
/* Turn off the alarm signal while we are handling the alarm. */
sigset_t mask_set; /* used to set a signal masking set. */
sigset_t old_set; /* used to store the old mask set. */
sigemptyset(&mask_set);
sigaddset(&mask_set, SIGALRM);

errno = 0;
if (sigprocmask(SIG_SETMASK, &mask_set, &old_set) != 0) {
perror("Unable to set the mask!");
}

}

So I would add the signal mask, but the code representing the GUI would still raise a signal and get into the handler the same time as the timer, and if I raise two then we would fire off many threads which would execute time sync subroutines out of order (something we are trying to avoid). Did I do something wrong?

This scenario is unlikely to happen in the real world, but I added mutex support and was able to get the operations to only happen in sequence no matter how many signals the GUI (GUI simulator sent).

Here is the code for the prototype daemon:

/*
============================================================================
Name : Prototypes.c
Author : Rick Hightower
Version : prototype for daemon that periodically syncs times between
devices, gateways and system time.
Copyright : Your copyright notice
Description : Sends the alarm signal sooner rather than later.
============================================================================
*/
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>

#define INTERVAL 30 //(60 * 2) //Two minutes in real world... 30 seconds in proto

pthread_mutex_t a_mutex = PTHREAD_MUTEX_INITIALIZER; //mutex I added to ensure time sync happens in sequence


void alarm_wakeup (int i);
void* time_sync(void* data);


/* Set the signal timer. */
void set_timer() {
struct itimerval tout_val;
tout_val.it_interval.tv_sec = 0;
tout_val.it_interval.tv_usec = 0;
tout_val.it_value.tv_sec = INTERVAL; /* set timer for "INTERVAL seconds */
tout_val.it_value.tv_usec = 0;

signal(SIGALRM,alarm_wakeup); /* set the Alarm signal capture */

setitimer(ITIMER_REAL, &tout_val,0);

}

/*
* This does not work. It could be an API usage problem, or
* it could just be that you cannot mask an alarm when
* you are in the alarm handler. No error code or message is
* produced. It just silently fails.
*/
void turn_off_signal_alarms_while_in_handler() {
/* Turn off the alarm signal while we are handling the alarm. */
sigset_t mask_set; /* used to set a signal masking set. */
sigset_t old_set; /* used to store the old mask set. */
sigemptyset(&mask_set);
sigaddset(&mask_set, SIGALRM);

errno = 0;
if (sigprocmask(SIG_SETMASK, &mask_set, &old_set) != 0) {
perror("Unable to set the mask!");
}

}
/* Wake up we got a SIGALARM signal from the OS.
* Reset the timer so we another alarm in
* */
void alarm_wakeup (int i)
{
turn_off_signal_alarms_while_in_handler();
static int times = 0;
printf("------------- %d %d\n",INTERVAL, times++);
start_time_sync_thread();
set_timer();

}

/* Create a new Thread to handle time sync. */
void start_time_sync_thread() {
pthread_t p_thread; /* thread's structure */
pthread_create(&p_thread, NULL, time_sync, NULL);
}

void lock_mutex() {
int status = pthread_mutex_lock(&a_mutex);
if (status) { /* an error has occurred */
perror("lock mutex");
pthread_exit(NULL);
}
}

void unlock_mutex() {
int status = pthread_mutex_unlock(&a_mutex);
if (status) { /* an error has occurred */
perror("lock mutex");
pthread_exit(NULL);
}
}

/* Simulate doing a time sync. */
void* time_sync(void* data)
{
lock_mutex();
//Look up system time once and only once
sleep(3); //Simulate getting list of devices
printf("Got list\n");
sleep(3); //Simulate querying gateway
printf("Query gateway\n");
sleep(3); //Update time offset in db
printf("Update db\n");
unlock_mutex();
/* terminate the thread */
pthread_exit(NULL);

}




int main ()
{

set_timer();

while (1)
{
sleep(2000); //Simulate doing other important stuff
}

return 0;

}

Here is the code for the prototype GUI simulator:

/*
============================================================================
Name : Wakeup.c
Author : Rick Hightower
Version : prototype for GUI function that changes time
Copyright : Your copyright notice
Description : Sends the alarm signal now...
Daemon should process as it normally would.
============================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


typedef struct pidInfo {
char name[80];
int id;
} PidInfo;



PidInfo parse_pid(char* line);
int find_pid(char* processName, PidInfo* foundPid);

const int LINE_SIZE = 80;

int main(void) {
PidInfo foundPid;

if (find_pid("Prototypes", &foundPid)==-1) {
printf("Unable to find process");
exit(1);
}

errno=0;
if (kill(foundPid.id, SIGALRM)==-1) {
perror("Unable to raise alarm for the process");
}
exit(0);



}


int find_pid(char* processName, PidInfo* foundPid) {


FILE *fp; //file pointer to open "ps -ae" process
int status; //generic return status holder
char line[LINE_SIZE]; //current line that we are reading

errno = 0;
fp = popen("ps -ae", "r");
if (fp == NULL) {
perror("Unable to open command");
return -1;
}

while (fgets(line, LINE_SIZE, fp) != NULL) {
PidInfo pidInfo = parse_pid(line); //parse current line into struct
if (strcmp(pidInfo.name, processName)==0) {
foundPid->id = pidInfo.id;
strcpy(foundPid->name, pidInfo.name);
}
}


status = pclose(fp);
if (status == -1) {
perror("Unable to close command");
return -1;
}

return 0;

}

PidInfo parse_pid(char* line) {
char *spid = strtok (line, " ");
strtok(NULL, " "); //tty
strtok(NULL, " "); //time
char *name = strtok(NULL, " \n");
PidInfo pidInfo;
strcpy(pidInfo.name, name);
pidInfo.id = atoi(spid);
return pidInfo;
}



So after this, I wonder if it is wise to use singnal handling after all. I mean if am using threads, mutexes and have to write my own custom process utilites to raise a signal, why not just use a thread that implements the timer in the first place and use the same OS IPC queue mechanism that the rest of the application uses extensively. The application uses message queues, shared memory, and sockets (if it just added named pipes and OS semaphores it could complete the IPC world tour). After this prototype, signals which looked like an good fit is not that much (if any) less complex than implementing the timer in pthreads and GUI notification in OS IPC queue.


0 投票
标签: Bga Sockets


发表评论
称呼: 主页: