使用USR信号同步流程

时间:2015-04-27 07:09:30

标签: c linux

我正在编写一个程序:

  • 主进程逐行读取文件,并在每次替换旧行时将其写入另一个文件
  • 子进程读取另一个文件,并在控制台上输出行

我该怎么做?到目前为止,我有:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char **argv)
{
    int pid, c;
    FILE *fpr, *fpw;
    char str[512];

    if((pid = fork()) == 0)
    {
        fpr = fopen("bufor", "r");
        if(fpr == NULL)
            exit(EXIT_FAILURE);

        while(fscanf(fpr, "%[^\n]\n", str) != EOF)
        {
            printf("%s", str);
        }

        fclose(fpr);
        exit(0);
    }

    fpr = fopen("/etc/profile", "r");
    if(fpr == NULL)
        exit(EXIT_FAILURE);

    if(fpr)
    {
        while(fscanf(fpr, "%[^\n]\n", str) != EOF)
        {
            fpw = fopen("bufor", "w+");
            if(sizeof(str) > 0)
            {
                fputs(str, fpw);
            }
            fclose(fpw);
        }
        fclose(fpr);
    }

    return 0;
}

它从文件中读取,写入另一个文件等,但没有同步 - 输出只是最后一行。

我该怎么做?

1 个答案:

答案 0 :(得分:1)

此代码似乎有效。确保您在将其作为您自己的交付之前了解这一切。我调用了文件sigsync.c,因此调用了程序sigsync,因此选择了环境变量的名称。

汇编:

gcc -g -O3 -std=gnu11 -Wall -Wextra -Wmissing-prototypes \
    -Wstrict-prototypes -Werror sigsync.c -o sigsync 

代码:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int verbose = 0;
static volatile sig_atomic_t sig_num = 0;

static void sigusr1(int signum)
{
    sig_num = signum;
}

static void be_childish(const char *file, pid_t parent)
{
    char str[512];
    FILE *fpr = fopen(file, "r");
    if (fpr == NULL)
    {
        fprintf(stderr, "Failed to open file %s for reading\n", file);
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        rewind(fpr);
        pause();
        if (verbose)
            printf("Child:  got %d\n", sig_num);
        while (fscanf(fpr, "%511[^\n]\n", str) == 1)
            printf("%s\n", str);
        kill(parent, SIGUSR1);
        sig_num = 0;
    }

    /*NOTREACHED*/
    fclose(fpr);
}

static void be_parental(const char *file, pid_t child)
{
    char str[512];
    const char profile[] = "/etc/profile";

    FILE *fpr = fopen(profile, "r");
    if (fpr == NULL)
    {
        fprintf(stderr, "Failed to open file %s for reading\n", profile);
        exit(EXIT_FAILURE);
    }

    while (fscanf(fpr, "%511[^\n]\n", str) != EOF)
    {
        if (strlen(str) > 0)
        {
            FILE *fpw = fopen(file, "w");
            if (fpw == 0)
            {
                fprintf(stderr, "Failed to open file %s for reading\n", profile);
                kill(child, SIGTERM);
                exit(EXIT_FAILURE);
            }
            fprintf(fpw, "%s\n", str);
            fclose(fpw);
            kill(child, SIGUSR1);
            pause();
            if (verbose)
                printf("Parent: got %d\n", sig_num);
            sig_num = 0;
        }
    }
    fclose(fpr);
    kill(child, SIGTERM);
}

int main(void)
{
    int child;
    int parent = getpid();
    const char filename[] = "bufor";

    /* Make sure file exists and is empty */
    FILE *fp = fopen(filename, "w");
    if (fp == 0)
    {
        fprintf(stderr, "Failed to open file %s for writing\n", filename);
        exit(EXIT_FAILURE);
    }
    fclose(fp);

    if (getenv("SIGSYNC_VERBOSE") != 0)
        verbose = 1;

    struct sigaction sa;
    sa.sa_handler = sigusr1;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGUSR1, &sa, 0) != 0)
    {
        fprintf(stderr, "Failed to set signal handler\n");
        exit(EXIT_FAILURE);
    }

    if ((child = fork()) < 0)
    {
        fprintf(stderr, "Failed to fork\n");
        exit(EXIT_FAILURE);
    }
    else if (child == 0)
        be_childish(filename, parent);
    else
        be_parental(filename, child);

    return 0;
}

示例输出:

请注意,前导空格已完全删除。父阅读/etc/profile中的代码就是这样做的,其原因很微妙。 "%511[^\n]\n"格式不会跳过前导空格,但最后的\n不会被视为“仅匹配换行符”,而是“匹配一系列空格”。这意味着它会跳过下一行的换行符和前导空格。要保留空间,请使用fgets()getline()代替fscanf()。使用GCC 5.1.0在Ubuntu 14.04衍生产品上进行测试。

# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
umask 027
if [ "$PS1" ]; then
if [ "$BASH" ]; then
PS1='\u@\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi