我是Linux C编程的新手,所以请耐心等待。使用互斥锁和信号量找到了几个关于进程间同步(相同进程但不同实例)的线程但不完全匹配我的情况。我试着跟着他们,试着创建几个样本,但没有一个对我有效。
最后在这里发帖以获得一些帮助。
我正在创建一个将通过以太网telnet会话执行的实用程序。如下面USAGE注释中所述,第一次调用将传递命令行参数-init,它将启动一个将始终运行的线程。以下 - 所有调用都将使用不同的十六进制代码指定-c:参数。
问题是,当一个实例仍在处理另一个调用时,会带有不同的-c:十六进制代码值。有时这会产生一个问题,即第二个调用返回的响应会返回到第一个调用。 (这是因为当第一个命令仍在进行中时,终端设备正在快速向第二个命令发送响应)
寻找一些psudo代码或引用,帮助我同步代码部分,这可以防止在收到第一个命令的响应之前发送第二个命令。
希望我能正确解释。
#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;
}
答案 0 :(得分:1)
没有
你创建了一个工作线程(因为它完成了繁重的工作而命名),但原始线程只是等待它存在。这毫无意义;您可以直接调用random_generator()
函数(将pthread_exit()
替换为return NULL
),而不必担心线程。你在那里的评论表明你希望&#34;线程&#34;总是运行,但事实并非如此:它只会在原始进程运行的时候运行。
每个流程都是一个独立的实体。仅仅因为两个进程碰巧执行相同的二进制文件,并没有&#34;加入&#34;他们在一起以任何方式。你不能分享&#34;进程之间的线程;每个线程属于一个特定的进程,就是它。
您应该做的是使用套接字,通常是Unix domain stream socket。如果每台计算机上都有一个特权服务,那么该套接字通常会绑定到/var/run/servicename
地址,但由于这看起来像是每用户服务,我会改为使用/tmp/username-servicename
。
当用户调用您的程序时,它首先尝试bind()
套接字(属于有效用户;您可以使用例如getpwuid(geteuid())
获取用户名)。如果绑定失败,则会为用户提供现有服务,而程序connect()
则为该服务。
如果绑定成功,程序就知道还没有服务。因此,它在套接字上调用listen()
来告诉内核它期望传入的连接,然后fork()
是一个子进程,子进程从控制终端分离,创建一个新的会话,因此自己进行漫游。 (这样,如果在关闭特定窗口或SSH连接时在终端中启动它,它将不会被杀死 - 除非你使用systemd配置为破坏东西。)之后,子进程开始为请求提供服务。
父进程需要关闭该套接字(因为子进程使用该套接字描述的另一个实例),创建一个新的,并且connect()
到服务,以执行用户指定的任何操作这个特殊的命令。
经常被忽视的问题是,当子进程被分叉以服务请求时,父进程需要等到子进程准备好accept()
新连接,否则连接将失败。上面,通过在子分叉之前让绑定套接字上的原始进程调用listen()
来避免这种情况,以便内核知道并将在子进程分叉之前缓冲传入的连接请求 ,避免出现竞争条件的可能性。
糟糕的程序员&#34;修复&#34;相反,通过添加&#34; sleep()&#34;的变体。相反,在父进程中,假设将父进程暂停一秒左右肯定是孩子开始接受连接的足够时间。 (这不是一个修复,因为它只是暂停父进程,希望由设计不良引起的race window比这短。正确的修复总是避免竞争条件,或解决它。 )
在此方案中,监听套接字的存在会检测到启动后台/服务守护程序的需要。
如果作业需要是顺序的(并且不是并行处理的),那么服务程序一次只需要accept()
一个连接,为它服务,并close()
,然后再接受新的一个 - 基本上,accept()
从侦听套接字中分离出特定于连接的套接字。
每个作业都在同一个进程中提供服务,因此如果要同时并行提供作业,则通常使用线程(每个线程为一个连接提供服务)或进程(子进程分叉)来实现服务进程为每个连接服务。)
答案 1 :(得分:0)
这就是我通过实现进程间信号量来实现它的方法。希望它能帮助别人节省时间并学到一些东西。以下是更新的代码。完美地使用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;
}
编译和执行命令如下,
$ gcc util.c -o util -pthread
$ ./util -init
$ ./util -c:
答案 2 :(得分:-1)
解决方案已经存在,并且它被称为:mutex
。你可以在线获取大量的信息来教育自己。
以下是关于mutex
(即锁定)如何工作的一个简单示例:
#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);
}
示例中的两个函数使用mutex
锁定用于不同目的。 increment_count()
函数仅使用mutex
锁来确保共享变量的原子更新。 get_count()
函数使用mutex
锁来保证以原子方式读取64位数量。在32位架构上,long long
实际上是两个32位数量。