Linux从文件中读取奇怪的行为

时间:2014-01-11 21:12:38

标签: c linux unix signals

我正在尝试创建一个创建文件的程序,将0写入其中,然后使用子进程交替增加该值。例如,子项应将该值递增为1,并将其写回文件。在此之后,父级将其增加到2并将其写入文件。孩子到3岁,依此类推。过程之间的同步通过信号完成。这是源代码:

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

void sig_hand (int sig);

int fd;

int main()
{
    pid_t pid;
    int i;
    fd = open ("abc.txt",O_RDWR|O_TRUNC);
    if (fd == -1)
    {
        fd = creat ("abc.txt",S_IRUSR|S_IWUSR);
    }
    write (fd,"0",2);
    if ((pid = fork()) < 0)
    {
        fprintf (stderr, "Error creating process\n");
        exit (1);
    } else
    {
        if (pid == 0)
        {
            signal (SIGUSR1,sig_hand);
            for (;;)
            {
                pause();
                kill (getppid(),SIGUSR1);
            }
        }
        if (pid > 0)
        {
            sleep (1);
            signal (SIGUSR1,sig_hand);
            for (i=0;i<5;i++)
            {
                kill (pid,SIGUSR1);
                pause ();
            }
            kill (pid,SIGTERM);
        }
    }
    return 0;
}

void sig_hand (int sig)
{
    int x;
    char c;
    read (fd,&c,2);
    x=atoi(&c);
    x++;
    printf ("x=%d\n",x);
    snprintf (&c,3,"%d",x);
    lseek (fd,0,SEEK_SET);
    printf ("PID %d is writing\n",getpid());
    write (fd,&c,2);
}

运行此程序会产生以下输出:

x=1
PID 4434 is writing
x=1
PID 4433 is writing
x=2
PID 4434 is writing
x=2
PID 4433 is writing
x=3
PID 4434 is writing
x=3
PID 4433 is writing
x=4
PID 4434 is writing
x=4
PID 4433 is writing
x=5
PID 4434 is writing
x=5
PID 4433 is writing

为什么子节点和父节点在一次迭代中递增相同的值?

谢谢!

3 个答案:

答案 0 :(得分:2)

问题不在于您的同步,而是您的文件访问。您从文件中读取,回头查找,然后写入文件。您认为下一次阅读将来自哪里?每次写入后,您都需要回到文件的开头。

您还有一些缓冲区溢出,因为您使用char作为单字符字符串。它不起作用,因为你没有空间终止器。

答案 1 :(得分:1)

您的主要问题是您无法以这种方式计时。你的程序在第一次和每次我运行它时都会陷入僵局。一个程序可以(并且经常会)在另一个程序处于等待状态之前发送信号。您完全依赖于调度程序,您无法预测或预测它。因此,正确的程序是阻止信号,因此在您准备好处理之前它不会被传送。以下程序使用sigrocmasksigsuspend来完成此操作。

你的第二个问题是Chris J. Kiick触及的问题。您正在从1字节的内存位置读取和写入2个字节但是,更重要的是,文件描述符在父级和子级之间共享,因此您需要每次都找到正确的位置,否则其他进程会将其留在您不在的位置期待。

#define _POSIX_C_SOURCE 1

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

void sig_hand(int sig);

int fd;

int main()
{
    pid_t pid;
    int i;

    if ((fd = open("abc.txt", O_RDWR | O_TRUNC)) == -1)
        errExit("open");

    struct sigaction sa;
    sigset_t saveMask, blockMask;

    //block SIGUSR1
    sigemptyset(&blockMask);
    sigaddset(&blockMask, SIGUSR1);

    if (sigprocmask(SIG_BLOCK, &blockMask, &saveMask) == -1)
        errExit("sigprocmask");

    //set up signal handler (both parent & child)
    sigemptyset(&sa.sa_mask);

    sa.sa_flags = 0;
    sa.sa_handler = sig_hand;

    if (sigaction(SIGUSR1, &sa, NULL) == -1)
        errExit("sigaction");

    if (write (fd, "\0", sizeof(char)) == -1)
        errExit("initial write");

    if ((pid = fork()) < 0)
        errExit("fork");

    if (pid == 0)
    {
        for (;;)
        {
            if (sigsuspend(&saveMask) == -1 && errno != EINTR)
                errExit("sigsuspend");

            kill(getppid(), SIGUSR1);
        }
    }

    if (pid > 0)
    {
        for (i = 0;i < 5;i++)
        {
            kill(pid, SIGUSR1);

            if (sigsuspend(&saveMask) == -1 && errno != EINTR)
                errExit("sigsuspend");
        }

        kill(pid, SIGTERM);
    }

    return 0;
}


void sig_hand(int sig)
{
    char c;

    if (lseek(fd, 0, SEEK_SET) == (off_t) - 1)
        errExit("lseek");

    if (read(fd, &c, sizeof(char)) == -1)
        errExit("read");

    c++;

    printf("c = %d\n", c);

    if (lseek(fd, 0, SEEK_SET) == (off_t) - 1)
        errExit("lseek");

    printf("PID %d is writing\n", getpid());

    if (write (fd, &c, sizeof(char)) == -1)
        errExit("write");
}

答案 2 :(得分:1)

这是更正后的sig_hand

void sig_hand (int sig) {
    int x;
    char c[3];
    read (fd,c,2);
    x=atoi(c);
    x++;
    printf ("x=%d\n",x);
    snprintf (c,3,"%d",x);
    lseek (fd,0,SEEK_SET);
    printf ("PID %d is writing\n",getpid());
    write (fd,c,2);
    lseek (fd,0,SEEK_SET);
}