等到上一个进程实例完成

wait until the previous instance of process finish

我是 Linux C 编程世界的新手,请耐心等待。使用互斥锁和信号量发现了几个关于进程间同步(相同进程但不同实例)的线程,但与我的情况不完全匹配。我试图跟随他们并尝试创建一些样本,但其中 none 对我有用。

终于在这里发帖寻求帮助。

我正在努力创建一个将通过以太网 telnet 会话执行的实用程序。如下 USAGE 注释中所述,第一次调用将传递命令行参数 -init,它将启动一个线程,该线程将始终为 运行。在 -init 之后,所有调用都会有 -c: 参数指定不同的十六进制代码。

问题是当一个实例仍在处理另一个带有不同 -c: 十六进制代码值的调用时。有时这会产生一个问题,即第二次调用返回的响应返回到第一次调用。 (这是因为终端设备正在快速发送对第二个命令的响应,而第一个命令仍在进行中)

正在寻找一些伪代码或参考,它们可以帮助我同步代码的一部分,从而防止在收到第一个命令的响应之前发送第二个命令。

希望我解释得当。

#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;
}

解决方案已经存在,名为: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 位数量。

没有

您创建了一个工作线程(如此命名是因为它执行繁重的工作),但让原始线程等待它存在。这是没有意义的;您可以直接调用 random_generator() 函数(将 pthread_exit() 替换为 return NULL),而根本不用担心线程。你在那里的评论表明你希望 "thread" 总是 运行,但事实并非如此:只要原始进程 运行s,它只会 运行。

每个进程都是一个单独的实体。仅仅因为两个进程碰巧执行相同的二进制文件,所以 "join" 它们不会以任何方式在一起。进程之间不能 "share" 线程;每个线程都属于一个特定的进程,仅此而已。


您应该改为使用 套接字 ,通常是 Unix domain stream socket。如果每台机器上只有一个特权服务,该套接字通常会绑定到一个 /var/run/servicename 地址,但因为这看起来像一个针对每个用户的服务,所以我会改用 /tmp/username-servicename

当用户调用您的程序时,它首先会尝试 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() 访问它。

如果绑定成功,程序就知道还没有服务。因此,它调用 listen() on the socket to tell the kernel it expects incoming connections, then fork()s 一个子进程,子进程从控制终端分离,创建一个新会话,因此将其自身守护进程。 (这样,如果您在关闭特定 window 或 SSH 连接时在终端中启动它,它就不会被杀死——除非您使用 systemd 被配置为破坏事物。)在那之后,子进程开始为请求提供服务。

父进程需要关闭该套接字(因为子进程使用了​​该套接字描述的另一个实例),创建一个新的,然后 connect() 到服务,以执行用户的任何操作为此特定命令指定。

一个经常被忽视的问题是,当子进程被分叉为请求提供服务时,父进程需要等待子进程准备好 accept() 新连接,否则连接将失败。上面,通过在子进程分叉之前在绑定的套接字上调用 listen() 来避免这种情况,这样内核就会知道并将在 之前 缓冲传入的连接请求。进程被分叉,避免了竞争条件的可能性。

可怜的程序员 "fix" 这不是通过在父进程中添加 "sleep()" 的变体来代替,假设暂停父进程一秒钟左右肯定有足够的时间让子进程开始接受连接。 (这不是修复,因为它只是暂停父进程,希望由不良设计引起的 race window 比这短。正确的修复总是避免竞争条件,或解决它。 )

在这个方案中,需要启动 background/service 守护进程是通过侦听套接字的存在来检测的。

如果作业需要按顺序进行(而不是并行处理),则服务程序一次只需要 accept() 一个连接,服务它,然后 close() 它,然后再接受一个新的——本质上,accept() 将特定于连接的套接字从侦听套接字中拆分出来。

每个作业都在同一个进程中提供服务,因此如果要同时并行处理多个作业,则通常使用线程(每个线程为一个连接提供服务)或进程(一个子进程)来实现服务进程进程分叉为每个连接提供服务)。

这就是我通过实现进程间信号量使其工作的方式。希望它能帮助人们节省时间并学到一些东西。下面是更新后的代码。在 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;
}

编译执行命令如下,

  1. $ gcc util.c -o util -pthread
  2. 在 1 号航站楼 运行 - $ ./util -init
  3. 在 2、3、4 号航站楼 运行 - $ ./util -c: