我是 Linux C 编程世界的新手,所以请您耐心等待。使用互斥锁和信号量找到了几个关于进程间同步(相同进程但不同实例)的线程,但与我的情况不完全匹配。我试图跟随他们并尝试创建一些样本,但没有一个对我有用。
最后在这里发帖寻求帮助。
我正在努力创建一个将通过以太网 telnet session 执行的实用程序。正如下面 USAGE 注释中所述,第一次调用将传递命令行参数 -init 将启动一个将始终运行的线程。在 -init all 调用之后将具有 -c: 使用不同十六进制代码指定的参数。
问题是当一个实例仍在处理另一个调用时,会带有不同的 -c: hex 代码值。有时这会造成第二次调用返回的响应返回到第一次调用的问题。 (这是因为终端设备正在快速发送对第二个命令的响应,而第一个命令仍在进行中)
寻找一些伪代码或引用来帮助我同步可以防止在接收到第一个命令的响应之前发送第二个命令的代码部分。
希望我解释得当。
#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;
}
最佳答案
不。
您创建了一个工作线程(之所以如此命名,是因为它完成了繁重的工作),但让原始线程等待它存在。这没有任何意义;您可以调用random_generator()
直接运行(将 pthread_exit()
替换为 return NULL
),根本不用担心线程。您在那里的评论表明您希望“线程”始终运行,但这不是真的:它只会在原始进程运行时运行。
每个进程都是一个单独的实体。仅仅因为两个进程碰巧执行相同的二进制文件,就不会以任何方式将它们“连接”在一起。您不能在进程之间“共享”线程;每个线程都属于一个特定的进程,仅此而已。
您应该做的是使用套接字,通常是 Unix domain stream socket .如果每台机器上只有一个特权服务,该套接字通常会绑定(bind)到 /var/run/servicename
地址,但因为这看起来像一个每个用户的服务,我会使用 /tmp/username-servicename
反而。
当用户调用您的程序时,它首先尝试 bind()
套接字(属于有效用户;您可以使用例如 getpwuid(geteuid())
获取用户名)。如果绑定(bind)失败,则说明用户已有服务,程序 connect()
代替它。
如果绑定(bind)成功,程序就知道还没有服务。所以,它调用 listen()
在套接字上告诉内核它需要传入的连接,然后 fork()
s 一个子进程,子进程与控制终端分离,创建一个新 session ,因此对自身进行 daamonizing。 (这样,如果您在关闭该特定窗口或 SSH 连接时在终端中启动它,它就不会被杀死——除非您使用 systemd 被配置为破坏事物。)之后,子进程开始为请求提供服务。
父进程需要关闭该套接字(因为子进程使用了该套接字描述的另一个实例),创建一个新实例,然后 connect()
到服务,执行用户为此特定命令指定的任何操作。
一个经常被忽视的问题是,当子进程被派生来为请求提供服务时,父进程需要等到子进程准备好 accept()
新连接,否则连接将失败。上面,通过让原始进程调用 listen()
来避免这种情况。在子进程被 fork 之前在绑定(bind)的套接字上,以便内核在子进程被 fork 之前知道并将缓冲传入的连接请求,避免出现竞争条件的可能性。
可怜的程序员通过在父进程中添加“sleep()”的变体来“修复”这个问题,假设暂停父进程一秒钟左右肯定有足够的时间让子进程开始接受连接。 (这不是一个修复,因为它只是暂停父进程,希望由糟糕的设计引起的 race window 比这更短。正确的修复总是避免竞争条件,或者解决它。)
在这个方案中,需要启动后台/服务守护进程是通过监听套接字的存在来检测的。
如果作业需要是顺序的(而不是并行处理),那么服务程序只需要 accept()
一次一个连接,服务它,close()
它,在接受一个新的之前——本质上,accept()
将特定于连接的套接字从监听套接字中分离出来。
每个作业都在同一个进程中提供服务,因此如果要同时并行提供作业,则服务进程通常使用线程(每个线程服务一个连接)或进程(一个子进程 fork 来服务每个连接)来实现联系)。
关于c - 等到上一个进程实例完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37980466/