简体   繁体   中英

Linux reading from file strange behaviour

I'm trying to make a program that creates a file, writes 0 into it and then, using a child process, alternates the incrementation of that that value. For example, the child should increment that value to 1, and writes it back to the file. After this, the parent increments it to 2 and writes it in the file. The child to 3, and so on. Synchronization between the processes is done via signals. Here is the source code:

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

Running this program produces the following output:

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

Why is the child and the parent incrementing the same value in one iteration?

Thanks!

The problem isn't your synchronization, it's your file accesses. You read from the file, seek back to the beginning, and then write to the file. Where do you think the next read is going to come from? You need to seek back to the start of the file after every write, as well.

You also have some buffer overflows, because you are using a char as a single-character string. It doesn't work that way, because you don't have room for the null-terminator.

Your primary problem is that you can't time the signals that way. Your program deadlocked the first and every time I ran it. One program can (and often will) send the signal before the other is in a position to wait for it. You are completely dependent on the scheduler and you can't anticipate or predict it. So the proper procedure is to block the signal so it isn't delivered until you are ready to deal with it. The program below uses sigrocmask and sigsuspend to accomplish that.

Your second problem is the one Chris J. Kiick touched upon. You were reading and writing 2 bytes from a 1 byte memory location But, more importantly, the file descriptor is shared between the parent and the child so you need to seek to the proper position each time or the other process will leave it somewhere you weren't expecting.

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

Here is a corrected 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM