當 pts 關閉時 read(2) 阻塞行為發生變化,導致 read() 返回錯誤:-1 (EIO)
我試圖弄清楚如何可靠地循環讀取我擁有的 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 時間。當cu
或picocom
什至只是一個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 標頭檔中的任何地方。請注意3
是comms_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 標頭檔中的任何地方。請注意,3
是comms_in->ptmx / masterfd
.
ioctl(TCGETS)
istcgetattr(3)
,也稱為 fromisatty(3)
和ptsname(3)
。它定義在/usr/include/asm-generic/ioctls.h
. 至於SNDCTL*
andSNDRV*
,它們是因為舊版本的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
.