Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Почему в linux в терминале программа (Си,С++) может читать stdout и писать в stdin?

#c #cpp #linux


Случайно наткнулся на загадочное (для меня) поведение программы.
Вот протокол
avp@avp-ubu1:~/src/ig/tst$ cat tpoll.c
#include 
#include 
#include 

main ()
{
  int lw = write(fileno(stdin),"xaxa\n",5);
  char buf[100];
  int lr = read(fileno(stdout),buf,99);
  buf[(lr > 0)? lr:0] = 0;
  printf ("lw = %d lr = %d buf = [%s]\n",lw,lr,buf);

  exit(0);
}
avp@avp-ubu1:~/src/ig/tst$ gcc tpoll.c
avp@avp-ubu1:~/src/ig/tst$ ./a.out 
xaxa
js39993
lw = 5 lr = 8 buf = [js39993
]
avp@avp-ubu1:~/src/ig/tst$ g++ tpoll.c 
avp@avp-ubu1:~/src/ig/tst$ ./a.out 
xaxa
jsk393
lw = 5 lr = 7 buf = [jsk393
]
avp@avp-ubu1:~/src/ig/tst$ 
avp@avp-ubu1:~/src/ig/tst$ cat /etc/issue
Ubuntu 10.04.4 LTS \n \l

avp@avp-ubu1:~/src/ig/tst$ env | grep TERM
TERM=xterm
COLORTERM=gnome-terminal
avp@avp-ubu1:~/src/ig/tst$ ps -ef | grep term
avp       1536     1  0 Oct24 ?        00:00:03 gnome-terminal
avp       2223  1538  0 01:17 pts/0    00:00:00 grep --color=auto term
avp@avp-ubu1:~/src/ig/tst$ 
avp@avp-ubu1:~/src/ig/tst$

Скажу откровенно, сам пока еще ответ нигде не искал. Возможно это общеизвестный факт,
а может нет. 
Если кто знает, почему такое происходит, объясните, пожалуйста.
В винде все, как и ожидается
c:/Users/avp/src/cc/hashcode $ gcc tpoll.c 
c:/Users/avp/src/cc/hashcode $ ./a
lw = -1 lr = -1 buf = []
c:/Users/avp/src/cc/hashcode $

Реально убунта стоит под виндой в VirtualBox.
UPD
Дополнил программу и немного позапускал. Видимо обычно sh запускается с дапами одного
девайса и это наследуется. В виде исключения нашел mpi на кластере. 
#include 
#include 
#include 
#include 
#include 

static void
pristat (char *what, struct stat *sbuf)
{
  printf ("%s stat:\n S_ISREG %s, S_ISCHR %s, S_ISFIFO %s, S_ISSOCK %s\n\
st_dev=%ld st_ino=%ld st_rdev=%ld st_mode=%lx\n", what,
      S_ISREG(sbuf->st_mode)? "Yes":"No",
      S_ISCHR(sbuf->st_mode)? "Yes":"No",
      S_ISFIFO(sbuf->st_mode)? "Yes":"No",
      S_ISSOCK(sbuf->st_mode)? "Yes":"No",
      (long)sbuf->st_dev, (long)sbuf->st_ino, (long)sbuf->st_rdev, (long)sbuf->st_mode);
}

static char*
stdiff (struct stat *sbuf1, struct stat *sbuf2, char *buf)
{
  *buf = 0;
  if (sbuf1->st_dev != sbuf2->st_dev)
    strcat(buf,"st_dev ");
  if (sbuf1->st_ino != sbuf2->st_ino)
    strcat(buf,"st_ino ");
  if (sbuf1->st_mode != sbuf2->st_mode)
    strcat(buf,"st_mode ");
  if (sbuf1->st_nlink != sbuf2->st_nlink)
    strcat(buf,"st_nlink ");
  if (sbuf1->st_uid != sbuf2->st_uid)
    strcat(buf,"st_uid ");
  if (sbuf1->st_gid != sbuf2->st_gid)
    strcat(buf,"st_gid ");
  if (sbuf1->st_rdev != sbuf2->st_rdev)
    strcat(buf,"st_rdev ");
  if (sbuf1->st_size != sbuf2->st_size)
    strcat(buf,"st_size ");
  if (sbuf1->st_blksize != sbuf2->st_blksize)
    strcat(buf,"st_blksize ");
  if (sbuf1->st_blocks != sbuf2->st_blocks)
    strcat(buf,"st_blocks ");
  if (sbuf1->st_atime != sbuf2->st_atime)
    strcat(buf,"st_atime ");
  if (sbuf1->st_mtime != sbuf2->st_mtime)
    strcat(buf,"st_mtime ");
  if (sbuf1->st_ctime != sbuf2->st_ctime)
    strcat(buf,"st_ctime ");
  return buf;
}

main ()
{
  int lw = write(fileno(stdin),"xaxa\n",5);
  char buf[1000];
  int lr = read(fileno(stdout),buf,99);
  buf[(lr > 0)? lr:0] = 0;
  printf ("lw = %d lr = %d buf = [%s]\n",lw,lr,buf);
  fprintf(stderr,"Try read stderr:");
  lr = read(fileno(stderr),buf,99);
  buf[(lr > 0)? lr:0] = 0;
  printf ("stderr: lr = %d buf = [%s]\n",lr,buf);

  struct stat sbufi, sbufo, sbufe;
  fstat(fileno(stdin),&sbufi);
  fstat(fileno(stdout),&sbufo);
  fstat(fileno(stderr),&sbufe);

  int diffio = memcmp(&sbufi,&sbufo,sizeof(sbufi)),
    diffoe = memcmp(&sbufi,&sbufo,sizeof(sbufi));

  if (diffio == 0 && diffoe == 0)
    pristat("All",&sbufi);
  else {
    printf ("stdin & stdout differ in %s\nstdout & stderr differ in %s\n",
        stdiff(&sbufi,&sbufo,buf), stdiff(&sbufo,&sbufe,&buf[500]));

    pristat("stdin",&sbufi);
    pristat("stdout",&sbufo);
    pristat("stderr",&sbufe);
  }

  exit(0);
}

Это обычная Xubuntu (в Emacs eshell то же самое)
avp@avp-xub11:~/src/tst$ gcc tstdio.c 
avp@avp-xub11:~/src/tst$ ./a.out 
xaxa
read STDOUT
lw = 5 lr = 12 buf = [read STDOUT
]
Try read stderr:read STDERR
stderr: lr = 12 buf = [read STDERR
]
All stat:
 S_ISREG No, S_ISCHR Yes, S_ISFIFO No, S_ISSOCK No
st_dev=11 st_ino=6 st_rdev=34819 st_mode=2190
avp@avp-xub11:~/src/tst$ 
avp@avp-xub11:~/src/tst$

Это RedHat на кластере, сначала вычислительный узел по mpi, потом "рабочая среда".
[root@manager soft]# mpirun -hosts cn01 ./a.out
lw = -1 lr = -1 buf = []
stderr: lr = -1 buf = []
stdin & stdout differ in st_ino 
stdout & stderr differ in st_ino 
stdin stat:
 S_ISREG No, S_ISCHR No, S_ISFIFO Yes, S_ISSOCK No
st_dev=8 st_ino=333128 st_rdev=0 st_mode=1180
stdout stat:
 S_ISREG No, S_ISCHR No, S_ISFIFO Yes, S_ISSOCK No
st_dev=8 st_ino=333129 st_rdev=0 st_mode=1180
stderr stat:
 S_ISREG No, S_ISCHR No, S_ISFIFO Yes, S_ISSOCK No
st_dev=8 st_ino=333130 st_rdev=0 st_mode=1180
Try read stderr:[root@manager soft]# 
[root@manager soft]# 
[root@manager soft]# ./a.out 
xaxa
jkks
lw = 5 lr = 5 buf = [jkks
]
Try read stderr:Cluster manager
stderr: lr = 16 buf = [Cluster manager
]
All stat:
 S_ISREG No, S_ISCHR Yes, S_ISFIFO No, S_ISSOCK No
st_dev=11 st_ino=3 st_rdev=34816 st_mode=2190
[root@manager soft]#

Можно подвести итог. Когда увидишь такое в первый раз, то крайне неожиданно (а я
наткнулся в результате своей невнимательности в тестовой программе), а потом понимаешь,
что а почему бы и нет?    


Ответы

Ответ 1



Хе, забавно. Наверное, все эти стандартные файловые дескрипторы присоединены к /dev/tty, который во всех случаях открывался с O_RDWR.

Ответ 2



Не сразу дошло, что не так. Да, в линуксе можно писать в stdin, я даже как-то обнаружил у себя такой код. Читать из stdout не догадался. Дело в реализации, в винде так нельзя.

Комментариев нет:

Отправить комментарий