Reading input using fgets returns duplicate lines in C

I experimented with some C code to implement a shell and found that fgets () returned duplicate lines when, after I processed a process that I could not understand, and I would really appreciate any help.

My question is: does forking change the offset in any open files in the parent process? This is similar to my program.

ANSWER BELOW @ Vadim Ponomarev and my understanding: fgets () is not thread safe (or, strictly speaking, it is, but branching the process causes stdin to be initialized in some way, which leads to a change in the offset of the shared file).

The code is as follows:

int main() {

  char buf[200];
  int r;
  pid_t pid = 0;

  while(getcmd(buf, 200, pid) >= 0) {
    fprintf(stderr, "current pid: %d\n", getpid());
    pid = fork();
    // Without forking the fgets() reads all lines normally
    if(pid == 0)
      exit(0);

    wait(&r);
  }

  return 0;
}

The getcmd () function is just a wrapper:

int
getcmd(char *buf, int nbuf, pid_t pid)
{
  memset(buf, 0, nbuf);
  if (fgets(buf, nbuf, stdin) == NULL) {
    fprintf(stderr, "EOF !!!\n");
    return -1;
  }
  fprintf(stderr, "pid: %d -- getcmd buf ======= --> %s\n", getpid(), buf);
  return 0;
}

temp :

line 1
line 2
line 3

a.out < temp, , 6 , .

pid = fork()
...

( , , fgets() 3 ).

, ?

( , ):

pid: 10361 -- getcmd buf ======= --> line1

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line2

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line2

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

current pid: 10361
EOF !!!

:

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line1

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line2

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

EOF

:

#include <stdio.h>
#include <stdlib.h>
#include <wait.h>
#include <zconf.h>
#include <unistd.h>
#include <memory.h>

int
getcmd(char *buf, int nbuf, pid_t pid)
{
  memset(buf, 0, nbuf);
  if (fgets(buf, nbuf, stdin) == NULL) {
    fprintf(stderr, "EOF !!!\n");
    return -1;
  }
  fprintf(stderr, "pid: %d -- getcmd buf ======= --> %s\n", getpid(), buf);
  return 0;
}

int main() {

  char buf[200];
  int r;
  pid_t pid = 0;

  while(getcmd(buf, 200, pid) >= 0) {
    fprintf(stderr, "current pid: %d\n", getpid());
    pid = fork();
    // Without forking the fgets() reads all lines normally
    if(pid == 0)
      exit(0);

    wait(&r);
  }

  return 0;
}

!

+4
2
  • , 0 (stdin)
  • , libc (stdin, stdout, stderr) , stdin:

    > strace -f ./a.out < temp 2>&1 | less
    ....
    write(2, "pid: 29487 -- getcmd buf ======="..., 45pid: 29487 -- getcmd buf ======= --> line 1
    clone(child_stack=0,flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,child_tidptr=0x7f34940f19d0) = 29488
    Process 29488 attached
    [pid 29487] wait4(-1,  <unfinished ...>
    [pid 29488] lseek(0, -14, SEEK_CUR)     = 7
    [pid 29488] exit_group(0)               = ?
    [pid 29488] +++ exited with 0 +++
    <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29488
    

lseek (0, -14, SEEK_CUR) (pid 29488)

  1. , (openSUSE Leap 42.2, glibc-2.22-4.3.1) EOF

  2. fgets() read()

    ....
    if (read(0, buf, nbuf) == 0) {
    ....
    while(getcmd(buf, 7, pid) >= 0) {
    ....
    

( EOF)

  1. strace -f - lseek() !

  2. - , ( stdio.h) - ( )

+4

fgets() , , TL;DR:

stdio .... , POSIX, 2.5.1:

http://pubs.opengroup.org/onlinepubs/007904875/functions/xsh_chap02_05.html

, , glibc 2.19 2.24.

:

, :

if (fork() == 0) {fclose (fd); (1);

if (fork() == 0) {_exit (1);

0

Source: https://habr.com/ru/post/1677321/


All Articles