[技術] Interrupted system calls

Written on 2:41 下午 by Yu Lai

這是最近寫程式遇到的,沒想到使用好好的UNIX Socket API居然會有爆走的時候。
在Survey了老半天,總算知道問題點所在。以下是看完The Linux GCC HOWTO的心得。

會發生Interrupted system calls主要是POSIX的系統檢查信號的次數,比起一些舊版的Unix是要多那麼一點。依照此規範,在Linux裡會在下列系統呼叫(system calls)的執行期間執行signal handlers: select(), pause(), connect(),accept(), read() on terminals, sockets, pipes or files in /proc, write() on terminals, sockets, pipes or the line printer, open() on FIFOs, PTYs or serial lines,ioctl() on terminals, fcntl() with command F_SETLKW, wait4(), syslog(), any TCP or NFS operations。而就其它的作業系統而言,你需要注意的可能就是下面這些系統呼叫(system calls)了: creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send(), wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop() to this list.

而問題點在於在系統呼叫(system calls)期間,若有一信號(那支程式本身應準備好handler因應了)產生,handler就會被呼叫。當handler將控制權轉移回系統呼叫時,它會偵測出它已經產生中斷,而且傳回值會立刻設定成-1,errno設定成EINTR。程式並沒有想到會發生這種事,所以就會bottles out了。

有兩種修正的方法可以選擇:

(1) 對每個你自行安裝(install)的signal handler,都須在sigaction旗號加上SA_RESTART。例如,把下列的程式,

     signal (sig_nr, my_signal_handler);

改成
     signal (sig_nr, my_signal_handler);
{ struct sigaction sa;
sigaction (sig_nr, (struct sigaction *)0, &sa);
#ifdef SA_RESTART
sa.sa_flags |= SA_RESTART;
#endif
#ifdef SA_INTERRUPT
sa.sa_flags &= ~ SA_INTERRUPT;
#endif
sigaction (sig_nr, &sa, (struct sigaction *)0);
}

要注意的是,當這部份的變更大量應用到系統呼叫之後,呼叫read(), write(),ioctl(), select(), pause() 與 connect()時,你仍然得自行檢查(check for)EINTR。如方法二所示。

(2) 你自己得很明確地(explicitly)檢查EINTR:

這裡有兩個針對read()與ioctl()的例子。

原始的程式片段,使用read()。
   int result;
while (len > 0) {
result = read(fd,buffer,len);
if (result <> 0) {
result = read(fd,buffer,len);
if (result < 0) { if (errno != EINTR) break; }
else { buffer += result; len -= result; }
}

原始的程式片段,使用ioctl()。
   int result;
result = ioctl(fd,cmd,addr);

修改成
   int result;
do { result = ioctl(fd,cmd,addr); }
while ((result == -1) && (errno == EINTR));

注意一點,有些版本的BSD Unix,其內定的行為(default behaviour)是重新執行系統呼叫。若要讓系統呼叫中斷,得使用SV_INTERRUPT或SA_INTERRUPT旗號。

另外補充一個由Richard Steven所提供給connect()的修改法。
   /* Start with fd just returned by socket(), blocking, SOCK_STREAM... */
while ( connect (fd, &name, namelen) == -1 && errno != EISCONN )
if ( errno != EINTR ) {
perror ("connect");
exit (EXIT_FAILURE);
}

[技術] 嵌入式linux下常見的文件系統

Written on 2:14 下午 by Yu Lai

嵌入式linux下常見的文件系統
• RomFS:只讀文件系統,可以放在ROM空間,也可以在系統的RAM中,嵌入式linux中常用來作根文件系統
• RamFS:利用VFS自身結構而形成的內存文件系統,使用系統的RAM空間
• JFFS/JFFS2:為Flash設計的日誌文件系統
• Yaffs:專門為Nand Flash設計
• proc:為Kernel和Module將Message發送給Process提供一種機制,可以查看系統Module裝載的信息
• devFS:設備文件系統

Linux上的Ext2fs
• 支持4 TB存儲、文件名稱最長1012字符
• 可選擇邏輯塊
• 快速符號鏈接
• Ext2不適合flash設備
• 是為象IDE 設備那樣的塊設備設計的,邏輯塊大小必須是512 byte、1 KB、2KB等
• 沒有提供對基於扇區的擦除/寫操作的良好管理
• 如果在一個扇區中擦除單個字節,必須將整個扇區複製到RAM,然後擦除,再重寫入
• 在出現電源故障時,Ext2fs 是不能防止崩潰的
• 文件系統不支持損耗平衡,縮短了flash的壽命

jffs/jffs2文件系統的優缺點
• 日誌文件系統
• 提供了更好的崩潰、掉電安全保護
• jffs2支持對flash的均勻磨損
• 在扇區級別上執行閃存擦除/寫/讀操作要比Ext2文件系統好
• 文件系統接近滿時,JFFS2 會大大放慢運行速度——垃圾收集

Nand上yaffs文件系統的優勢
• 專門為Nand flash設計的日誌文件系統
• jffs/jffs2不適合大容量的Nand flash
• jffs的日誌通過jffs_node建立在RAM中,佔用RAM空間:對於128MB的Nand大概需要4MB的空間來維護節點
• 啟動的時候需要掃瞄日誌節點,不適合大容量的Nand flash
• FAT系統沒有日誌編譯yaffs文件系統
• mtd的最新補丁升級?
• 接口更新,適合與yaffs
• 與原有的mtd驅動程序不兼容,需要重寫
• 如果使用舊mtd驅動需要定義Makefile中MTD_OLD = -DCONFIG_YAFFS_USE_OLD_MTD
• 參考文檔: yaffs-rootfs-howto
• 最新版的yaffs網站:
http://www.aleph1.co.uk/armlinux/projects/yaffs

使用yaffs文件系統
• 通過cat /proc/yaffs命令可以看到yaffs系統的相關信息
• mount -t yaffs /dev/mtdblock/0 /mnt/yaffs