等到上一个进程实例完成

时间:2016-06-23 00:42:14

标签: c multithreading mutex semaphore interprocess

我是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;
}

3 个答案:

答案 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;
}

编译和执行命令如下,

  1. $ gcc util.c -o util -pthread
  2. 在1号航站楼上运行 - $ ./util -init
  3. 在2,3,4号航站楼上运行 - $ ./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位数量。