leia (2) comportamento de bloqueio muda quando pts é fechado resultando em read () retornando erro: -1 (EIO)

1

Estou tentando descobrir como posso fazer loop de maneira confiável em uma leitura em um mestre de pt que possuo. Eu abro o ptmx, concedo e destranco como de costume:

* ptmx stuff */
/* get the master (ptmx) */
int32_t masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
if(masterfd < 0){
    perror("open");
    exit(EXIT_FAILURE);
};

/* grant access to the slave */
if(grantpt(masterfd) < 0){
    perror("grantpt");
    exit(EXIT_FAILURE);
};

/* unlock the slave */
if(unlockpt(masterfd) < 0){
    perror("unlockpt");
    exit(EXIT_FAILURE);
};

comms_in->ptmx = masterfd;

Em seguida, salve o nome do escravo (sim, eu sei que sizeof (char) é sempre 1)

/* get the path to the slave */
char * slavepathPtr;
char * slavePath;
size_t slavepathLen;
if((slavepathPtr = ptsname(masterfd)) == NULL){
    perror("ptsname");
    exit(EXIT_FAILURE);
}else{
    slavepathLen = strlen(slavepathPtr);
    slavePath = (char *) malloc(sizeof(char) * (slavepathLen + 1));
    strcpy(slavePath, slavepathPtr);
};

Eu então crio um symlink previsivelmente nomeado para o escravo ( /dev/pts/number ) em /dev/custom/predictable (que foi fornecido como um argumento para este programa usando getopts) e verifico que suas permissões são seguras usando chamadas para access , lstat , readlink , symlink e confirme se o programa pode continuar a execução, caso contrário ele chama unlink no symlink e finaliza o encadeamento.

Finalmente, o programa termina neste loop

ssize_t read_result;
ssize_t write_result;
while(1){
    if((read_result = read(comms_in->ptmx, ptmxio_read_buffer, sizeof ptmxio_read_buffer)) <= 0){
        { /** calls thread ender routine */
            pthread_mutex_lock(&COMMS_MUTEX);
            comms_in->thread_statuses[PTMXIO_THREAD] = THREAD_FAILED;
            pthread_mutex_unlock(&COMMS_MUTEX);
            pthread_cond_signal(&SIG_PROGRAM_FINISHED);
            pthread_exit((void *) comms_in);
        }
    }else if((write_result = write(STDOUT_FILENO, ptmxio_read_buffer, read_result)) != read_result){
        {
            /** same as above */
        }
    };
};

No sistema, posso executar este programa e tudo está cheio. Os blocos de leitura. Quando o link simbólico pts é aberto com cu ou picocom , então os bytes são lidos com sucesso até os limites do buffer no meu final ou no final do kernel, dependendo de quem está abaixo. O problema surge quando o escravo está fechado. Neste ponto, a leitura retorna -1 - > EIO com o texto do erro: Input/output error e continuará a fazê-lo, consumindo muito tempo de CPU se eu optar por não terminar o encadeamento e o loop. Quando cu ou picocom ou mesmo apenas echo -en "some text" > /dev/pts/number , a leitura bloqueia novamente, até que os bytes estejam disponíveis. No caso do redirecionamento para o symlink, obviamente, se ele preenche menos que um buffer, o read recebe aquele buffer e continua a retornar -1 - > EIO novamente.

O que está acontecendo? Eu preciso de um método que não consuma muita CPU, pois isso é executado em um processador de aplicativo incorporado lento e permite-me restabelecer as leituras sem perder bytes.

Eu notei um thread fazendo uma ligação para isso: ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) e não pode fazer muito sentido de quais são as 3 opções, pois elas não estão nos meus cabeçalhos do Linux em qualquer lugar. Observe que 3 é comms_in->ptmx / masterfd .

Aqui está um lstat sobre o symlink e algumas informações extras, note que o st_mode é inalterado antes e depois de leituras bem sucedidas e mal-sucedidas.

‘ptmxio_thread’ failed read (-1) on /dev/pts/13 /dev/pts/13: Input/output error
‘ptmxio_thread’ ptsNum (from ioctl) 13
‘ptmxio_thread’ st_dev: 6, st_ino: 451, st_mode: 0000A1FF, st_nlink: 1
‘ptmxio_thread’ st_uid: 000003E8, st_gid: 000003E8, st_rdev: 0, st_size: 11
‘ptmxio_thread’ st_blksize: 4096, st_blocks: 0, st_atime: 1540963806, st_mtime: 1540963798
‘ptmxio_thread’ st_ctime: 1540963798
    
por Supernovah 31.10.2018 / 05:48

1 resposta

2

É muito simples: você deve abrir e manter uma alça aberta para o lado do escravo do programa no lado do mestre.

Depois de obter o nome com ptsname(3) , open(2) it.

I noticed a thread making a call to this: ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) and can't make much sense of what the 3 options are as they're not in my Linux headers anywhere.. Note that 3 is comms_in->ptmx / masterfd.

ioctl(TCGETS) é tcgetattr(3) , que também é chamado de isatty(3) e ptsname(3) . Está definido em /usr/include/asm-generic/ioctls.h . Quanto ao SND* , strace poderia ser um pouco mais inteligente; você não vai fazer operações alsa em um pseudo-terminal.

int32_t masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);

Não faz sentido tornar seu programa desnecessariamente não-portável. Use posix_openpt(3) em vez disso.

slavepathLen = strlen(slavepathPtr);
slavePath = (char *) malloc(sizeof(char) * (slavepathLen + 1));
strcpy(slavePath, slavepathPtr);

É para isso que serve o strdup(3) ; -)

E você também deve manipular seu read() sendo interrompido por um sinal, a menos que esteja absolutamente certo de que você (e todas as funções de biblioteca que você chama) definem todos os manipuladores de sinal com SA_RESTART .

    
por 31.10.2018 / 20:24