Read

當 pts 關閉時 read(2) 阻塞行為發生變化,導致 read() 返回錯誤:-1 (EIO)

  • February 10, 2020

我試圖弄清楚如何可靠地循環讀取我擁有的 pt 主控。我像往常一樣打開 ptmx,授予和解鎖它:

* 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;

接下來我保存奴隸的名字(是的,我知道 sizeof(char) 總是 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);
};

然後,我創建一個指向從屬 ( /dev/pts/number)的可預測命名符號連結(使用 getopts 作為該程序的參數提供),並使用對、、的/dev/custom/predictable呼叫驗證其權限是否安全,並確認程序可以繼續執行,否則它呼叫符號連結並終止執行緒。access``lstat``readlink``symlink``unlink

最後程序結束在這個循環中

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 */
       }
   };
};

在系統上,我可以執行這個程序,一切都很好。讀取塊。當 pts 符號連結打開時,cu或者picocom然後字節被成功讀取到我端或核心端的緩衝區限制,具體取決於誰較低。當奴隸關閉時,問題就來了。此時,讀取返回-1->EIO並帶有錯誤文本:Input/output error並將繼續這樣做,如果我選擇不終止執行緒和循環,則會消耗大量 cpu 時間。當cupicocom什至只是一個echo -en "some text" > /dev/pts/number時,讀取再次阻塞,直到字節可用。在重定向到符號連結的情況下,顯然如果它填充的緩衝區少於一個緩衝區, read 只會獲取那個緩衝區並繼續返回-1->EIO再次。

這是怎麼回事?我需要一種不消耗大量 CPU 的方法,因為它執行在速度較慢的嵌入式應用程序處理器上,並且允許我在不失去字節的情況下重新建立讀取。

我注意到一個執行緒對此進行了呼叫: ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...})並且無法理解這 3 個選項是什麼,因為它們不在我的 Linux 標頭檔中的任何地方。請注意3comms_in->ptmx/ masterfd

這是符號連結上的 lstat 和一些額外資訊,請注意 st_mode 在成功和不成功讀取之前和之後都沒有變化。

‘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

這很簡單:您應該在處理主端的程序中打開並保持打開 pty 從端的句柄。

在你得到名字後ptsname(3)open(2)它。

我注意到一個執行緒對此進行了呼叫:ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...})並且無法理解這 3 個選項是什麼,因為它們不在我的 Linux 標頭檔中的任何地方。請注意,3comms_in->ptmx / masterfd.

ioctl(TCGETS)is tcgetattr(3),也稱為 fromisatty(3)ptsname(3)。它定義在/usr/include/asm-generic/ioctls.h. 至於SNDCTL*and SNDRV*,它們是因為舊版本的strace.

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

使您的程序不必要地不可移植是沒有意義的。改為使用posix_openpt(3)

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

strdup(3)就是為了 ;-)

而且您還應該處理read()被信號中斷的情況,除非您絕對確定您(以及您呼叫的所有庫函式)將所有信號處理程序設置為SA_RESTART.

引用自:https://unix.stackexchange.com/questions/478815