问题
I am new to the world of Linux C Programming so request your patience. Found several threads about inter-process synchronization (same process but different instance) using mutex and semaphores but not exactly matching my case. I tried to follow them and tried to create few samples but none of them is working for me.
Finally posting here to get some help.
I am working on to create a utility which will executed over Ethernet telnet session. As noted below in USAGE comment, 1st call will pass command line argument -init which will start a thread which will be running always. Following -init all call will have -c: argument specified with different hex code.
The problem is when one instance is still working on another call comes with different -c: hex code value. Sometimes this is creating a problem that the response returned by second call gets returned to the 1st call. (This is because the end device is sending response to 2nd command quickly while 1st command is still in-progress)
Looking for some psudo code or reference which help me synchronize the part of code which can prevent sending 2nd command before receiving response of 1st command.
Hope I explained it properly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
static volatile sig_atomic_t isRunning = 1;
/* Signal handler */
void signal_handler(int signal) {
switch (signal) {
case SIGINT:
case SIGTERM:
case SIGQUIT:
/* Graceful shutdown */
isRunning = 0;
break;
default:
break;
}
}
void* random_generator(void* data) {
fd_set readfd;
struct timeval timeout;
char buff[20];
struct tm *sTm;
do {
time_t now = time(0);
sTm = gmtime(&now);
strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", sTm);
printf("%s\n", buff);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int ret = select(0, NULL, NULL, NULL, &timeout);
printf("Select returned: %d\n", ret);
} while (isRunning);
printf("Exiting thread...");
pthread_exit((void*) 0);
}
int main(int argc, char** argv) {
/*
* USAGE:
* 1st command -> ./util -init
* 2nd command -> ./util -c:<hexcode>
* 3rd command -> ./util -c:<hexcode>
* .......
*/
pthread_t mythread;
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
if (argc == 2)
return 0;
if (strcmp(argv[1], "-c:") == 0) {
// TODO: Only one process should be executing this part at any point of time
...lock();
int count = 0;
do{
printf("Processing...%d\n", count);
}while(count++ < 30);
...unlock();
return 0;
} else if (strcmp(argv[1], "-init") == 0) {
// always running thread
printf("Starting thread...\n");
int ret = pthread_create(&mythread, NULL, random_generator, (void*) NULL);
if (ret)
printf("Failed starting thread\n");
else
printf("Thread started\n");
pthread_join(mythread, NULL);
}
return 0;
}
回答1:
No.
You create a worker thread (so named because it does the heavy lifting), but have the original thread just wait for it to exist. This makes no sense; you could just call the random_generator()
function directly (replacing the pthread_exit()
with a return NULL
), and not bother with threads at all. The comment you have there indicates you desire the "thread" to always run, but that's not true: it'll only run for as long as the original process runs.
Each process is a separate entity. Just because two processes happen to execute the same binary, does not "join" them together in any way. You cannot "share" threads between processes; each thread belongs to one particular process, and that's it.
What you should do instead, is to use a socket, typically an Unix domain stream socket. If there was a single privileged service on each machine, that socket would usually be bound to a /var/run/servicename
address, but because this looks like a per-user service, I'd use /tmp/username-servicename
instead.
When a user invokes your program, it first tries to bind() the socket (belonging to the effective user; you can obtain the user name using e.g. an getpwuid(geteuid())). If the bind fails, there is an existing service for the user, and the program connect()s to it instead.
If the bind succeeds, the program knows there is no service yet. So, it calls listen() on the socket to tell the kernel it expects incoming connections, then fork()s a child process, with the child process detaching from the controlling terminal, creating a new session, and therefore daamonizing itself. (That way it will not be killed if you started it in a terminal when you close that particular window or SSH connection -- except possibly if you use systemd is configured to break things.) After that, the child process starts servicing the requests.
The parent process needs to close that socket (because another instance of that socket description is used by the child process), create a new one, and connect()
to the service, to perform whatever action the user specified for this particular command.
An often overlooked wrinkle is that when the child process is forked to service the requests, the parent process needs to wait until the child process is ready to accept() new connections, or the connect will fail. Above, that is avoided by having the original process call listen()
on the bound socket before the child is forked, so that the kernel knows and will buffer incoming connection requests before the child process is forked, avoiding the possibility of a race condition.
Poor programmers "fix" this instead by adding a variant of "sleep()" in the parent process instead, assuming that pausing the parent for a second or so is surely enough time for the child to start accepting connections. (It is not a fix, because it simply pauses the parent process in the hopes that the race window caused by poor design is shorter than that. The correct fix is always to avoid the race condition, or to work around it.)
In this scheme, the need to start the background/service daemon is detected by the presense of the listening socket.
If jobs need to be sequential (and not handled in parallel), then the service program only needs to accept()
one connection at a time, service it, and close()
it, before accepting a new one -- essentially, accept()
splits of a connection-specific socket off the listening socket.
Each job is serviced in the same process, so if jobs are to be served in parallel, at the same time, then the service process is usually implemented using threads (each thread servicing one connection) or processes (a child process forked to serve each connection).
回答2:
This is how I got it working by implementing interprocess semaphore. Hopefully it will help somebody save their time and learn something. Below is the updated code. Perfectly working on Ubuntu 14.04 LTS.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
static volatile sig_atomic_t isRunning = 1;
#define SEM_NAME "mutex2"
static sem_t *mutex;
/* Signal handler */
void signal_handler(int signal) {
switch (signal) {
case SIGINT:
case SIGTERM:
case SIGQUIT:
/* Graceful shutdown */
isRunning = 0;
break;
default:
break;
}
}
void* random_generator(void* data) {
fd_set readfd;
struct timeval timeout;
char buff[20];
struct tm *sTm;
int rc;
do {
time_t now = time(0);
sTm = gmtime(&now);
strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", sTm);
printf("%s\n", buff);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int ret = select(0, NULL, NULL, NULL, &timeout);
printf("Select returned: %d\n", ret);
} while (isRunning);
printf("Exiting thread...\n");
pthread_exit((void*) 0);
}
int main(int argc, char** argv) {
/*
* USAGE:
* 1st command -> ./util -init
* 2nd command -> ./util -c:<hexcode>
* 3rd command -> ./util -c:<hexcode>
* .......
*/
pthread_t mythread;
int rc;
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
if (argc != 2)
return 1;
if (strcmp(argv[1], "-c:") == 0) {
// TODO: Only once process should be executing this part at any point of time
mutex = sem_open(SEM_NAME, O_CREAT);
if (mutex == SEM_FAILED) {
printf("sem_open error - %d (%m)\n", errno);
return 1;
}
printf("sem_open success\n");
printf("calling sem_wait\n");
rc = sem_wait(mutex);
if(rc < 0){
printf("sem_wait error - %d (%m)\n", errno);
return 1;
}
int i;
for (i = 0; i < 10; i++){
printf("process %d %d\n", getpid(), i);
sleep(1);
}
printf("sem_post calling\n");
rc = sem_post(mutex);
if(rc < 0){
printf("sem_post error - %d (%m)\n", errno);
return 1;
}
return 0;
} else if (strcmp(argv[1], "-init") == 0) {
// always running thread
printf("Starting thread...\n");
int ret = pthread_create(&mythread, NULL, random_generator, (void*) NULL);
if (ret)
printf("Failed starting thread\n");
else
printf("Thread started\n");
// open semaphore
mutex = sem_open(SEM_NAME, O_CREAT);
if (mutex == SEM_FAILED) {
printf("sem_open error - %d (%m)\n", errno);
sem_close(mutex);
sem_unlink(SEM_NAME);
return 1;
}
printf("sem_open success\n");
rc = sem_init(mutex, 1, 1);
if(rc < 0){
printf("sem_init error - %d (%m)\n", errno);
sem_close(mutex);
sem_unlink(SEM_NAME);
return 1;
}
printf("sem_init success\n");
// join thread
pthread_join(mythread, NULL);
printf("Unlink semaphore...\n");
rc = sem_close(mutex);
if(rc < 0){
fprintf(stdout, "sem_close error - %d (%m)\n", errno);
}
rc = sem_unlink(SEM_NAME);
if(rc < 0){
printf("sem_unlink error - %d (%m)\n", errno);
}
}
return 0;
}
Compile and execute commands are below,
$ gcc util.c -o util -pthread
- On Terminal 1 run -
$ ./util -init
- On Terminal 2,3,4 run -
$ ./util -c:
回答3:
The solution already exists and it's called: mutex
. You can tons of information online to educate yourself about it.
Here is a trivial example on how mutex
(i.e. lock) works:
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void increment_count()
{
pthread_mutex_lock(&count_mutex);
count = count + 1;
pthread_mutex_unlock(&count_mutex);
}
long long get_count()
{
long long c;
pthread_mutex_lock(&count_mutex);
c = count;
pthread_mutex_unlock(&count_mutex);
return (c);
}
The two functions in example use the mutex
lock for different purposes. The increment_count()
function uses the mutex
lock simply to ensure an atomic update of the shared variable. The get_count()
function uses the mutex
lock to guarantee that the 64-bit quantity count is read atomically. On a 32-bit architecture, a long long
is really two 32-bit quantities.
来源:https://stackoverflow.com/questions/37980466/wait-until-the-previous-instance-of-process-finish