網頁

顯示具有 linux 標籤的文章。 顯示所有文章
顯示具有 linux 標籤的文章。 顯示所有文章

2009年10月1日 星期四

How to mount JFFS2 image

Use memory to emulate the MTD device
mknod /tmp/mtdblock0 b 31 0
modprobe mtdblock
modprobe mtdram total_size=65536 erase_size=256

modprobe jffs2

dd if=image.jffs2 of=/tmp/mtdblock0

mkdir /mnt/jffs2
mount -t jffs2 /tmp/mtdblock0 /mnt/jffs2

2009年6月9日 星期二

How to show the page table entry


excerpt from arch/arm/mm/fault.c
void show_pte(struct mm_struct *mm, unsigned long addr)
{
pgd_t *pgd;

if (!mm)
mm = &init_mm;

printk(KERN_ALERT "pgd = %p\n", mm->pgd);
pgd = pgd_offset(mm, addr);
printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd));

do {
pmd_t *pmd;
pte_t *pte;

if (pgd_none(*pgd))
break;

if (pgd_bad(*pgd)) {
printk("(bad)");
break;
}

pmd = pmd_offset(pgd, addr);
#if PTRS_PER_PMD != 1
printk(", *pmd=%08lx", pmd_val(*pmd));
#endif

if (pmd_none(*pmd))
break;

if (pmd_bad(*pmd)) {
printk("(bad)");
break;
}

#ifndef CONFIG_HIGHMEM
/* We must not map this if we have highmem enabled */
pte = pte_offset_map(pmd, addr);
printk(", *pte=%08lx", pte_val(*pte));
printk(", *ppte=%08lx", pte_val(pte[-PTRS_PER_PTE]));
pte_unmap(pte);
#endif
} while(0);

printk("\n");
}

crontab

Crontab command
#export to the file
crontab -l > filename
# import from the file
crontab filename 
# import from the file for the specific user
crontab -u user filename 
# edit
crontab -e 
# delete all
crontab -r 

Crontab special usage 


EntryDescriptionEquivalent To
@rebootRun once, at startup.None
@yearlyRun once a year0 0 1 1 *
@annually(same as @yearly)0 0 1 1 *
@monthlyRun once a month0 0 1 * *
@weeklyRun once a week0 0 * * 0
@dailyRun once a day0 0 * * *
@midnight(same as @daily)0 0 * * *
@hourlyRun once an hour0 * * * *


Crontab Example

# .---------------- minute (0 - 59) 
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * command to be executed

minhourday/monthmonthday/week Execution time
30011,6,12*-- 00:30 Hrs  on 1st of Jan, June & Dec.

:

020*101-5--8.00 PM every weekday (Mon-Fri) only in Oct.

:

001,10,15**-- midnight on 1st ,10th & 15th of month

:

5,10010*1-- At 12.05,12.10 every Monday & on 10th of every month
:

 
*/8 * * * * root init 6  After 8 minutes , execute "reboot" command
ps: "*/8" in the minute time field is equivalent to "0,8,16,24,32,48,56"

2009年6月8日 星期一

Linux 2.6 Driver Template


#include <linux/module.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <asm/io.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

typedef struct
{
void **data;
struct cdev *cdev ;

}DBG_Dev_Tag;

#define DBG_MAJOR 111
#define DBG_MINOR 1
#define MAXDEVICENUM 1

#define DEBUG_IOC_MAGIC 'D'
#define DEBUG_IOCSHOWDEB _IOR(DEBUG_IOC_MAGIC, 1, int)

DBG_Dev_Tag *DBG_devices;


int dbg_major = DBG_MAJOR;
int dbg_minor;

static struct file_operations DBG_fops =
{
.ioctl = DBG_ioctl,
.read = DBG_read,
.write = DBG_write,
.open = DBG_open,
.release = DBG_release,
};

int DBG_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct net_device *dev = &_wpcm450_netdevice;

switch (cmd)
{
case DEBUG_IOCSHOWDEB:
//show something

break;
default:
break;
}

return 0;
}

ssize_t DBG_read (struct file *filp, char *buf, size_t count, loff_t *offset)
{
return 0;
}

ssize_t DBG_write (struct file *file, const char *buf, size_t count, loff_t *offset)
{
return 0;
}

static int DBG_open(struct inode *inode, struct file *filp)
{
DBG_Dev_Tag *dev;

dev = container_of (inode->i_cdev,DBG_Dev_Tag,cdev);

filp-> private_data = dev;

return 0;
}

static int DBG_release(struct inode *inode, struct file *filp)
{


return 0;
}



static void dbg_setup_cdev(DBG_Dev_Tag *dev,int index)
{
int err;
int dev_id = MKDEV(dbg_major,dbg_minor + index );

cdev_init(dev->cdev,&DBG_fops);

dev->cdev->owner = THIS_MODULE;

dev->cdev->ops = &DBG_fops;

err = cdev_add(dev->cdev,dev_id,1);

if (err)
printk ("error %x adding dbg%x\n",err,index);

}


static int __init DBG_init(void)
{
int result;
dev_t dev_id;


DBG_devices = kmalloc (sizeof (DBG_Dev_Tag) * MAXDEVICENUM ,GFP_KERNEL);

if (DBG_devices == NULL)
{
result = -ENOMEM;
return result;
}

memset(DBG_devices, 0, MAXDEVICENUM * sizeof(DBG_Dev_Tag));

if (dbg_major)
{
dev_id = MKDEV(dbg_major,dbg_minor);

result = register_chrdev_region(dev_id, 1, "debug");
}
else
{
result = alloc_chrdev_region(&dev_id,DBG_MINOR,1,"dbg");
dbg_major = MAJOR(dev_id);
}

if (result < 0 )
{
printk ("can't get major %d\n",dbg_major);
return result;
}

DBG_devices->cdev = cdev_alloc();

dbg_setup_cdev(DBG_devices,1);

return 0;
}

static void __exit DBG_exit(void)
{
dev_t dev_id;

kfree(DBG_devices);

cdev_del(DBG_devices ->cdev);

dev_id = MKDEV(dbg_major,dbg_minor);
unregister_chrdev_region(dev_id,1);
return;
}

MODULE_AUTHOR("XXXXXXXXXX");
MODULE_DESCRIPTION("Debug driver");
MODULE_LICENSE("GPL");

module_init(DBG_init);
module_exit(DBG_exit);

//**************************Makefile *******************************
KERNELDIR =   obj-m := test_drv.o
#combine two drivers into the one. 
#test_drv-objs := testA_drv.o testB_drv.o  
ifneq ($(KERNELRELEASE),)  
CFLAGS +=  -D__KERNEL__ -DMODULE  -Wall -DEXPORT_SYMTAB -O2  
else  
all:  
 $(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules endif 

2008年3月1日 星期六

FIND的使用方式

一、find 命令格式
1、find命令的一般形式為;
find pathname -options [-print -exec -ok ...]
2、find命令的參數;
pathname: find命令所查找的目錄路徑。例如用.來表示當前目錄,用/來表示系統根目錄。
-print: find命令將匹配的文件輸出到標準輸出。
-exec: find命令對匹配的文件執行該參數所給出的shell命令。相應命令的形式為'command' { } ;,注意{ }和;之間的空格。
-ok: 和-exec的作用相同,只不過以一種更為安全的模式來執行該參數所給出的shell命令,在執行每一個命令之前,都會給出提示,讓用戶來確定是否執行。
3、find命令選項
-name
按照文件名查找文件。
-perm
按照文件權限來查找文件。
-prune
使用這一選項可以使find命令不在當前指定的目錄中查找,如果同時使用-depth選項,那麼-prune將被find命令忽略。
-user
按照文件屬主來查找文件。
-group
按照文件所屬的組來查找文件。
-mtime -n +n
按照文件的更改時間來查找文件, - n表示文件更改時間距現在n天以內,+ n表示文件更改時間距現在n天以前。find命令還有-atime和-ctime 選項,但它們都和-m time選項。
-nogroup
查找無有效所屬組的文件,即該文件所屬的組在/etc/groups中不存在。
-nouser
查找無有效屬主的文件,即該文件的屬主在/etc/passwd中不存在。
-newer file1 ! file2
查找更改時間比文件file1新但比文件file2舊的文件。
-type
查找某一類型的文件,諸如:
b - 塊設備文件。
d - 目錄。
c - 字符設備文件。
p - 管道文件。
l - 符號鏈接文件。
f - 普通文件。
-size n:[c] 查找文件長度為n塊的文件,帶有c時表示文件長度以字節計。
-depth:在查找文件時,首先查找當前目錄中的文件,然後再在其子目錄中查找。
-fstype:查找位於某一類型文件系統中的文件,這些文件系統類型通常可以在配置文件/etc/fstab中找到,該配置文件中包含了本系統中有關文件系統的信息。
-mount:在查找文件時不跨越文件系統mount點。
-follow:如果find命令遇到符號鏈接文件,就跟蹤至鏈接所指向的文件。
-cpio:對匹配的文件使用cpio命令,將這些文件備份到磁帶設備中。
另外,下面三個的區別:
-amin n
  查找系統中最後N分鐘訪問的文件
  -atime n
  查找系統中最後n*24小時訪問的文件
  -cmin n
  查找系統中最後N分鐘被改變文件狀態的文件
  -ctime n
  查找系統中最後n*24小時被改變文件狀態的文件
 -mmin n
  查找系統中最後N分鐘被改變文件數據的文件
  -mtime n
  查找系統中最後n*24小時被改變文件數據的文件
4、使用exec或ok來執行shell命令
使用find時,只要把想要的操作寫在一個文件裡,就可以用exec來配合find查找,很方便的
在有些操作系統中只允許-exec選項執行諸如l s或ls -l這樣的命令。大多數用戶使用這一選項是為了查找舊文件並刪除它們。建議在真正執行rm命令刪除文件之前,最好先用ls命令看一下,確認它們是所要刪除的文件。
exec選項後面跟隨著所要執行的命令或腳本,然後是一對兒{ },一個空格和一個,最後是一個分號。為了使用exec選項,必須要同時使用print選項。如果驗證一下find命令,會發現該命令只輸出從當前路徑起的相對路徑及文件名。
例如:為了用ls -l命令列出所匹配到的文件,可以把ls -l命令放在find命令的-exec選項中
# find . -type f -exec ls -l { } \;
-rw-r--r-- 1 root root 34928 2003-02-25 ./conf/httpd.conf
-rw-r--r-- 1 root root 12959 2003-02-25 ./conf/magic
-rw-r--r-- 1 root root 180 2003-02-25 ./conf.d/README
上面的例子中,find命令匹配到了當前目錄下的所有普通文件,並在-exec選項中使用ls -l命令將它們列出。
在/logs目錄中查找更改時間在5日以前的文件並刪除它們:
$ find logs -type f -mtime +5 -exec rm { } \;
記住:在shell中用任何方式刪除文件之前,應當先查看相應的文件,一定要小心!當使用諸如mv或rm命令時,可以使用-exec選項的安全模式。它將在對每個匹配到的文件進行操作之前提示你。
在下面的例子中, find命令在當前目錄中查找所有文件名以.LOG結尾、更改時間在5日以上的文件,並刪除它們,只不過在刪除之前先給出提示。
$ find . -name "*.conf" -mtime +5 -ok rm { } \;
< rm ... ./conf/httpd.conf > ? n
按y鍵刪除文件,按n鍵不刪除。
任何形式的命令都可以在-exec選項中使用。
在下面的例子中我們使用grep命令。find命令首先匹配所有文件名為「 passwd*」的文件,例如passwd、passwd.old、passwd.bak,然後執行grep命令看看在這些文件中是否存在一個sam用戶。
# find /etc -name "passwd*" -exec grep "sam" { } \;
sam:x:501:501::/usr/sam:/bin/bash
二、find命令的例子;
1、查找當前用戶主目錄下的所有文件:
下面兩種方法都可以使用
$ find $HOME -print
$ find ~ -print
2、讓當前目錄中文件屬主具有讀、寫權限,並且文件所屬組的用戶和其他用戶具有讀權限的文件;
$ find . -type f -perm 644 -exec ls -l { } \;
3、為了查找系統中所有文件長度為0的普通文件,並列出它們的完整路徑;
$ find / -type f -size 0 -exec ls -l { } \;
4、查找/var/logs目錄中更改時間在7日以前的普通文件,並在刪除之前詢問它們;
$ find /var/logs -type f -mtime +7 -ok rm { } \;
5、為了查找系統中所有屬於root組的文件;
$find . -group root -exec ls -l { } \;
-rw-r--r-- 1 root root 595 10月 31 01:09 ./fie1
6、find命令將刪除當目錄中訪問時間在7日以來、含有數字後綴的admin.log文件。
該命令只檢查三位數字,所以相應文件的後綴不要超過999。先建幾個admin.log*的文件 ,才能使用下面這個命令
$ find . -name "admin.log[0-9][0-9][0-9]" -atime -7 -ok
rm { } ;
< rm ... ./admin.log001 > ? n
< rm ... ./admin.log002 > ? n
< rm ... ./admin.log042 > ? n
< rm ... ./admin.log942 > ? n
7、為了查找當前文件系統中的所有目錄並排序;
$ find . -type d sort
8、為了查找系統中所有的rmt磁帶設備;
$ find /dev/rmt -print
三、xargs
xargs - build and execute command lines from standard input
在使用find命令的-exec選項處理匹配到的文件時, find命令將所有匹配到的文件一起傳遞給exec執行。但有些系統對能夠傳遞給exec的命令長度有限制,這樣在find命令運行幾分鐘之後,就會出現溢出錯誤。錯誤信息通常是「參數列太長」或「參數列溢出」。這就是xargs命令的用處所在,特別是與find命令一起使用。
find命令把匹配到的文件傳遞給xargs命令,而xargs命令每次只獲取一部分文件而不是全部,不像-exec選項那樣。這樣它可以先處理最先獲取的一部分文件,然後是下一批,並如此繼續下去。
在有些系統中,使用-exec選項會為處理每一個匹配到的文件而發起一個相應的進程,並非將匹配到的文件全部作為參數一次執行;這樣在有些情況下就會出現進程過多,系統性能下降的問題,因而效率不高;
而使用xargs命令則只有一個進程。另外,在使用xargs命令時,究竟是一次獲取所有的參數,還是分批取得參數,以及每一次獲取參數的數目都會根據該命令的選項及系統內核中相應的可調參數來確定。
來看看xargs命令是如何同find命令一起使用的,並給出一些例子。
下面的例子查找系統中的每一個普通文件,然後使用xargs命令來測試它們分別屬於哪類文件
#find . -type f -print xargs file
./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text
./.kde/Autostart/.directory: ISO-8859 text在整個系統中查找內存信息轉儲文件(core dump) ,然後把結果保存到/tmp/core.log 文件中:
$ find / -name "core" -print xargs echo "" >/tmp/core.log
上面這個執行太慢,我改成在當前目錄下查找
#find . -name "file*" -print xargs echo "" > /temp/core.log
# cat /temp/core.log
./file6
在當前目錄下查找所有用戶具有讀、寫和執行權限的文件,並收回相應的寫權限:
# ls -l
drwxrwxrwx 2 sam adm 4096 10月 30 20:14 file6
-rwxrwxrwx 2 sam adm 0 10月 31 01:01 http3.conf
-rwxrwxrwx 2 sam adm 0 10月 31 01:01 httpd.conf
# find . -perm -7 -print xargs chmod o-w
# ls -l
drwxrwxr-x 2 sam adm 4096 10月 30 20:14 file6
-rwxrwxr-x 2 sam adm 0 10月 31 01:01 http3.conf
-rwxrwxr-x 2 sam adm 0 10月 31 01:01 httpd.conf
用grep命令在所有的普通文件中搜索hostname這個詞:
# find . -type f -print xargs grep "hostname"
./httpd1.conf:# different IP addresses or hostnames and have them handled by the
./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
on your
用grep命令在當前目錄下的所有普通文件中搜索hostnames這個詞:
# find . -name * -type f -print xargs grep "hostnames"
./httpd1.conf:# different IP addresses or hostnames and have them handled by the
./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
on your
注意,在上面的例子中, 用來取消find命令中的*在shell中的特殊含義。

find命令配合使用exec和xargs可以使用戶對所匹配到的文件執行幾乎所有的命令。
四、find 命令的參數
下面是find一些常用參數的例子,有用到的時候查查就行了,像上面前幾個貼子,都用到了其中的的一些參數,也可以用man或查看論壇裡其它貼子有find的命令手冊
1、使用name選項
文件名選項是find命令最常用的選項,要麼單獨使用該選項,要麼和其他選項一起使用。
可以使用某種文件名模式來匹配文件,記住要用引號將文件名模式引起來。
不管當前路徑是什麼,如果想要在自己的根目錄$HOME中查找文件名符合*.txt的文件,使用~作為 'pathname'參數,波浪號~代表了你的$HOME目錄。
$ find ~ -name "*.txt" -print
想要在當前目錄及子目錄中查找所有的『 *.txt』文件,可以用:

$ find . -name "*.txt" -print
想要的當前目錄及子目錄中查找文件名以一個大寫字母開頭的文件,可以用:
$ find . -name "[A-Z]*" -print
想要在/etc目錄中查找文件名以host開頭的文件,可以用:
$ find /etc -name "host*" -print
想要查找$HOME目錄中的文件,可以用:
$ find ~ -name "*" -print 或find . -print
要想讓系統高負荷運行,就從根目錄開始查找所有的文件。
$ find / -name "*" -print
如果想在當前目錄查找文件名以兩個小寫字母開頭,跟著是兩個數字,最後是.txt的文件,下面的命令就能夠返回名為ax37.txt的文件:
$find . -name "[a-z][a-z][0--9][0--9].txt" -print
2、用perm選項
按照文件權限模式用-perm選項,按文件權限模式來查找文件的話。最好使用八進制的權限表示法。
如在當前目錄下查找文件權限位為755的文件,即文件屬主可以讀、寫、執行,其他用戶可以讀、執行的文件,可以用:
$ find . -perm 755 -print
還有一種表達方法:在八進制數字前面要加一個橫槓-,表示都匹配,如-007就相當於777,-006相當於666
# ls -l
-rwxrwxr-x 2 sam adm 0 10月 31 01:01 http3.conf
-rw-rw-rw- 1 sam adm 34890 10月 31 00:57 httpd1.conf
-rwxrwxr-x 2 sam adm 0 10月 31 01:01 httpd.conf
drw-rw-rw- 2 gem group 4096 10月 26 19:48 sam
-rw-rw-rw- 1 root root 2792 10月 31 20:19 temp
# find . -perm 006
# find . -perm -006
./sam
./httpd1.conf
./temp
-perm mode:文件許可正好符合mode
-perm +mode:文件許可部分符合mode
-perm -mode: 文件許可完全符合mode
3、忽略某個目錄
如果在查找文件時希望忽略某個目錄,因為你知道那個目錄中沒有你所要查找的文件,那麼可以使用-prune選項來指出需要忽略的目錄。在使用-prune選項時要當心,因為如果你同時使用了-depth選項,那麼-prune選項就會被find命令忽略。
如果希望在/apps目錄下查找文件,但不希望在/apps/bin目錄下查找,可以用:
$ find /apps -path "/apps/bin" -prune -o -print
4、使用find查找文件的時候怎麼避開某個文件目錄
比如要在/usr/sam目錄下查找不在dir1子目錄之內的所有文件
find /usr/sam -path "/usr/sam/dir1" -prune -o -print
find [-path ..] [expression] 在路徑列表的後面的是表達式
-path "/usr/sam" -prune -o -print 是 -path "/usr/sam" -a -prune -o
-print 的簡寫表達式按順序求值, -a 和 -o 都是短路求值,與 shell 的 && 和 類似如果 -path "/usr/sam" 為真,則求值 -prune , -prune 返回真,與邏輯表達式為真;否則不求值 -prune,與邏輯表達式為假。如果 -path "/usr/sam" -a -prune 為假,則求值 -print ,-print返回真,或邏輯表達式為真;否則不求值 -print,或邏輯表達式為真。
這個表達式組合特例可以用偽碼寫為
if -path "/usr/sam" then
-prune
else
-print
避開多個文件夾
find /usr/sam ( -path /usr/sam/dir1 -o -path /usr/sam/file1 ) -prune -o -print
圓括號表示表達式的結合。
表示引用,即指示 shell 不對後面的字符作特殊解釋,而留給 find 命令去解釋其意義。
查找某一確定文件,-name等選項加在-o 之後
#find /usr/sam (-path /usr/sam/dir1 -o -path /usr/sam/file1 ) -prune -o -name "temp" -print
5、使用user和nouser選項
按文件屬主查找文件,如在$HOME目錄中查找文件屬主為sam的文件,可以用:
$ find ~ -user sam -print
在/etc目錄下查找文件屬主為uucp的文件:
$ find /etc -user uucp -print

為了查找屬主帳戶已經被刪除的文件,可以使用-nouser選項。這樣就能夠找到那些屬主在/etc/passwd文件中沒有有效帳戶的文件。在使用-nouser選項時,不必給出用戶名; find命令能夠為你完成相應的工作。

例如,希望在/home目錄下查找所有的這類文件,可以用:
$ find /home -nouser -print
6、使用group和nogroup選項
就像user和nouser選項一樣,針對文件所屬於的用戶組, find命令也具有同樣的選項,為了在/apps目錄下查找屬於gem用戶組的文件,可以用:
$ find /apps -group gem -print
要查找沒有有效所屬用戶組的所有文件,可以使用nogroup選項。下面的find命令從文件系統的根目錄處查找這樣的文件
$ find / -nogroup-print
7、按照更改時間或訪問時間等查找文件
如果希望按照更改時間來查找文件,可以使用mtime,atime或ctime選項。如果系統突然沒有可用空間了,很有可能某一個文件的長度在此期間增長迅速,這時就可以用mtime選項來查找這樣的文件。
用減號-來限定更改時間在距今n日以內的文件,而用加號+來限定更改時間在距今n日以前的文件。
希望在系統根目錄下查找更改時間在5日以內的文件,可以用:
$ find / -mtime -5 -print
為了在/var/adm目錄下查找更改時間在3日以前的文件,可以用:
$ find /var/adm -mtime +3 -print
8、查找比某個文件新或舊的文件
如果希望查找更改時間比某個文件新但比另一個文件舊的所有文件,可以使用-newer選項。它的一般形式為:
newest_file_name ! oldest_file_name
其中,!是邏輯非符號。
查找更改時間比文件sam新但比文件temp舊的文件:
例:有兩個文件
-rw-r--r-- 1 sam adm 0 10月 31 01:07 fiel
-rw-rw-rw- 1 sam adm 34890 10月 31 00:57 httpd1.conf
-rwxrwxr-x 2 sam adm 0 10月 31 01:01 httpd.conf
drw-rw-rw- 2 gem group 4096 10月 26 19:48 sam
-rw-rw-rw- 1 root root 2792 10月 31 20:19 temp
# find -newer httpd1.conf ! -newer temp -ls
1077669 0 -rwxrwxr-x 2 sam adm 0 10月 31 01:01 ./httpd.conf
1077671 4 -rw-rw-rw- 1 root root 2792 10月 31 20:19 ./temp
1077673 0 -rw-r--r-- 1 sam adm 0 10月 31 01:07 ./fiel
查找更改時間在比temp文件新的文件:
$ find . -newer temp -print
9、使用type選項
在/etc目錄下查找所有的目錄,可以用:
$ find /etc -type d -print
在當前目錄下查找除目錄以外的所有類型的文件,可以用:
$ find . ! -type d -print
在/etc目錄下查找所有的符號鏈接文件,可以用
$ find /etc -type l -print
10、使用size選項
可以按照文件長度來查找文件,這裡所指的文件長度既可以用塊(block)來計量,也可以用字節來計量。以字節計量文件長度的表達形式為N c;以塊計量文件長度只用數字表示即可。
在按照文件長度查找文件時,一般使用這種以字節表示的文件長度,在查看文件系統的大小,因為這時使用塊來計量更容易轉換。
在當前目錄下查找文件長度大於1 M字節的文件:
$ find . -size +1000000c -print
在/home/apache目錄下查找文件長度恰好為100字節的文件:
$ find /home/apache -size 100c -print
在當前目錄下查找長度超過10塊的文件(一塊等於512字節):
$ find . -size +10 -print
11、使用depth選項
在使用find命令時,可能希望先匹配所有的文件,再在子目錄中查找。使用depth選項就可以使find命令這樣做。這樣做的一個原因就是,當在使用find命令向磁帶上備份文件系統時,希望首先備份所有的文件,其次再備份子目錄中的文件。
在下面的例子中, find命令從文件系統的根目錄開始,查找一個名為CON.FILE的文件。
它將首先匹配所有的文件然後再進入子目錄中查找。
$ find / -name "CON.FILE" -depth -print
12、使用mount選項
在當前的文件系統中查找文件(不進入其他文件系統),可以使用find命令的mount選項。
從當前目錄開始查找位於本文件系統中文件名以XC結尾的文件:
$ find . -name "*.XC" -mount -print

Linux Module的介紹

module
其實是一般的程序。但是它可以被動態載到 kernel
裡成為 kernel的一部分。載到 kernel 裡的 module 它具有跟
kernel 一樣的權力。可以 access 任何 kernel 的 data
structure。你聽過 kdebug 嗎 ? 它是用來 debug kernel
的。它就是先將它本身的一個 module 載到 kernel
裡,而在 user space 的 gdb 就可以經由跟這個 module
溝通,得知 kernel 裡的 data structure
的值,除此之外,還可以經由載到 kernel 的 module
去更改 kernel 裡 data structure。


我們知道,在寫 C 程序的時候,一個程序只能有一個
main。Kernel 本身其實也是一個程序,它本身也有個
main,叫 start_kernel()。當我們把一個 module 載到 kernel
裡的時候,它會跟 kernel 整合在一起,成為 kernel
的一部分。請各位想想,那 module 可以有 main 嗎 ?
答案很明顯的,是
No。理由很簡單。一個程序只能有一個 main。在使用
module 時,有一點要記住的是 module
是處於被動的角色。它是提供某些功能讓別人去使用的。


Kernel 裡有一個變量叫 module_list,每當 user 將一個 module
載到 kernel 裡的時候,這個 module 就會被記錄在
module_list 裡面。當 kernel 要使用到這個 module 提供的
function 時,它就會去 search 這個 list,找到
module,然後再使用其提供的 function 或 variable。每一個
module 都可以 export 一些 function
或變量來讓別人使用。除此之外,module
也可以使用已經載到 kernel 裡的 module 提供的
function。這種情形叫做 module stack。比方說,module A
用到 module B 的東西,那在加載 module A
之前必須要先加載 module B。否則 module A
會無法加載。除了 module 會 export 東西之外,kernel
本身也會 export 一些 function 或 variable。同樣的,module
也可以使用 kernel 所 export
出來的東西。由於大家平時都是撰寫 user space
的程序,所以,當突然去寫 module
的時候,會把平時寫程序用的 function 拿到 module
裡使用。像是 printf
之類的東西。我要告訴各位的是,module 所使用的
function 或 variable,要嘛就是自己寫在 module
裡,要嘛就是別的 module 提供的,再不就是 kernel
所提供的。你不能使用一般 libc 或 glibc所提供的
function。像 printf
之類的東西。這一點可能是各位要多小心的地方。(也許你可以先
link 好,再載到 kernel,我好像試過,但是忘了)


剛才我們說到 kernel 本身會 export 出一些 function 或
variable 來讓 module
使用,但是,我們不是萬能的,我們怎?知道 kernel
有開放那裡東西讓我們使用呢 ? Linux 提供一個
command,叫 ksyms,你只要執行 ksyms -a 就可以知道 kernel
或目前載到 kernel 裡的 module 提供了那些 function 或
variable。底下是我的系統的情形:


c0216ba0 drive_info_R744aa133
c01e4a44 boot_cpu_data_R660bd466
c01e4ac0 EISA_bus_R7413793a
c01e4ac4 MCA_bus_Rf48a2c4c
c010cc34 __verify_write_R203afbeb
. . . . .


在 kernel 裡,有一個 symbol table 是用來記錄 export
出去的 function 或 variable。除此之外,也會記錄著那個
module export 那些 function。上面幾行中,表示 kernel
提供了 drive_info 這個
function/variable。所以,我們可以在 kernel
裡直接使用它,等載到 kernel 裡時,會自動做好 link
的動作。由此,我們可以知道,module
本身其實是還沒做 link 的一些 object
code。一切都要等到 module 被加載 kernel 之後,link
才會完成。各位應該可以看到 drive_info
後面還接著一些奇怪的字符串。_R744aa133,這個字符串是根據目前
kernel 的版本再做些 encode
得出來的結果。為什?額外需要這一個字符串呢 ?


Linux 不知道從那個版本以來,就多了一個 config
的選項,叫做 Set version number in symbols of
module。這是為了避免對系統造成不穩定。我們知道
Linux 的 kernel 更新的很快。在 kernel
更新的過程,有時為了效率起見,會對某些舊有的 data
structure 或 function 做些改變,而且一變可能有的 variable
被拿掉,有的 function 的 prototype
跟原來的都不太一樣。如果這種情形發生的時候,那可能以前
2.0.33 版本的 module 拿到 2.2.1 版本的 kernel
使用,假設原來 module 使用了 2.0.33 kernel 提供的變量叫
A,但是到了 2.2.1 由於某些原因必須把 A 都設成
NULL。那當此 module 用在 2.2.1 kernel
上時,如果它沒去檢查 A
的值就直接使用的話,就會造成系統的錯誤。也許不會整個系統都死掉,但是這個
module 肯定是很難發揮它的功能。為了這個原因,Linux
就在 compile module 時,把 kernel 版本的號碼 encode 到各個
exported function 和 variable 裡。


所以,剛才也許我們不應該講 kernel 提供了
drive_info,而應該說 kernel 提供了 driver_info_R744aa133
來讓我們使用。這樣也許各位會比較明白。也就是說,kernel
認為它提供的 driver_info_R744aa133 這個東西,而不是
driver_info。所以,我們可以發現有的人在加載 module
時,系統都一直告訴你某個 function 無法
resolved。這就是因為 kernel 裡沒有你要的
function,要不然就是你的 module 裡使用的 function 跟
kernel encode 的結果不一樣。所以無法
resolve。解決方式,要嘛就是將 kernel 裡的 set version
選項關掉,要嘛就是將 module compile 成 kernel
有辦法接受的型式。


那有人就會想說,如果 kernel 認定它提供的 function
名字叫做 driver_info_R744aa133
的話,那我們寫程序時,是不是用到這個 funnction
的地方都改成 driver_info_R744aa133 就可以了。答案是
Yes。但是,如果每個 function
都要你這樣寫,你不會覺得很煩嗎 ?
比方說,我們在寫 driver 時,很多人都會用到 printk
這個 function。這是 kernel 所提供的 function。它的功能跟
printf 很像。用法也幾乎都一樣。是 debug
時很好用的東西。如果我們 module 裡用了一百次
printk,那是不是我們也要打一百次的 printk_Rdd132261 呢 ?
當然不是,聰明的人馬上會想到用 #define printk
printk_Rdd132261 就好了嘛。所以囉,Linux
很體貼的幫我們做了這件事。


如果各位的系統有將 set version
的選項打開的話,那大家可以到
/usr/src/linux/include/linux/modules
這個目錄底下。這個目錄底下有所多的
..ver檔案。這些檔案其實就是用來做 #define
用的。我們來看看 ksyms.ver
這個檔案裡,裡面有一行是這樣子的 :


#define printk _set_ver(printk)


set_ver 是一個 macro,就是用來在 printk 後面加上 version
number 的。有興趣的朋友可以自行去觀看這個 macro
的寫法。用了這些 ver 檔,我們就可以在 module
裡直接使用 printk 這樣的名字了。而這些 ver
檔會自動幫我們做好 #define
的動作。可是,我們可以發現這個目錄有很多很多的
ver 檔。有時候,我們怎?知道我們要呼叫的 function
是在那個 ver 檔裡有定義呢 ? Linux
又幫我們做了一件事。/usr/src/linux/include/linux/modversions.h
這個檔案已經將全部的 ver
檔都加進來了。所以在我們的 module 裡只要 include
這個檔,那名字的問題都解決了。但是,在此,我們奉勸各位一件事,不要將
modversions.h 這個檔在 module 裡 include
進來,如果真的要,那也要加上以下數行:


#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif


加入這三行的原因是,避免這個 module 在沒有設定
kernel version 的系統上,將 modversions.h 這個檔案 include
進來。各位可以去試試看,當你把 set version
的選項關掉時,modversions.h 和 modules
這個目錄都會不見。如果沒有上面三行,那 compile
就不會過關。所以一般來講,modversions.h 我們會選擇在
compile 時傳給 gcc 使用。就像下面這個樣子。


gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS main.c -include usr/src/linux/include/linux/modversions.h


在這個 command line 裡,我們看到了
-D__KERNEL__,這是說要定義 __KERNEL__ 這個
constant。很多跟 kernel 有關的 header
file,都必須要定義這個 constant 才能 include
的。所以建議你最好將它定義起來。另外還有一個
-DMODVERSIONS。這個 constant
我剛才忘了講。剛才我們說要解決 fucntion 或 variable
名字 encode 的方式就是要 include
modversions.h,其實除此之外,你還必須定義 MODVERSIONS
這個 constant。再來就是 MODULE 這個
constant。其實,只要是你要寫 module
就一定要定義這個變量。而且你還要 include module.h
這個檔案,因為 _set_ver 就是定義在這裡的。


講到這裡,相信各位應該對 module
有一些認識了,以後遇到 module unresolved
應該不會感到困惑了,應該也有辦法解決了。


剛才講的都是使用別人的 function 上遇到的名字 encode
問題。但是,如果我們自己的 module 想要 export
一些東西讓別的 module 使用呢。很簡單。在 default
上,在你的 module 裡所有的 global variable 和 function
都會被認定為你要 export 出去的。所以,如果你的
module 裡有 10 個 global variable,經由
ksyms,你可以發現這十個 variable 都會被 export
出去。這當然是個很方便的事啦,但是,你知道,有時候我們根本不想把所有的
variable 都 export 出去,萬一有個 module 沒事亂改我們的
variable 怎?辦呢 ?
所以,在很多時候,我們都只會限定幾個必要的東西
export 出去。在 2.2.1 之前的 kernel (不是很確定)
可以利用 register_symtab
來幫我們。但是,現在更新的版本早就出來了。所以,在此,我會介紹
kernel 2.2.1 裡所提供的。kernel 2.2.1 裡提供了一個
macro,叫做 EXPORT_SYMBOL,這是用來幫我們選擇要 export
的 variable 或 function。比方說,我要 export 一個叫 full
的 variable,那我只要在 module 裡寫:


EXPORT_SYMBOL(full);


就會自動將 full export 出去,你馬上就可以從 ksyms
裡發現有 full 這個變量被 export 出去。在使用
EXPORT_SYMBOL 之前,要小心一件事,就是必須在 gcc
裡定義 EXPORT_SYMTAB 這個 constant,否則在 compile
時會發生 parser error。所以,要使用 EXPORT_SYMBOL
的話,那 gcc 應該要下:


gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS -DEXPORT_SYMTAB main.c -include /usr/src/linux/include/linux/modversions.h


如果我們不想 export 任何的東西,那我們只要在 module
裡下


EXPORT_NO_SYMBOLS;


就可以了。使用 EXPORT_NO_SYMBOLS 用不著定義任何的
constant。其實,如果各位使用過舊版的 register_symbol
的話,一定會覺得新版的方式比較好用。至少我是這樣覺得啦。因為使用
register_symbol 還要先定義出自己的
symbol_table,感覺有點麻煩。


當我們使用 EXPORT_SYMBOL 把一些 function 或 variable export
出來之後,我們使用 ksyma -a 去看一些結果。我們發現
EXPORT_SYMBOL(full) 的確是把 full export出來了 :


c8822200 full [my_module]
c01b8e08 pci_find_slot_R454463b5
. . .


但是,結果怎?跟我們想像中的不太一樣,照理說,應該是
full_Rxxxxxx 之類的東西才對啊,怎?才出現 full 而已呢
? 奇怪,問題在那裡呢 ?


其實,問題就在於我們沒有對本身的 module 所 export
出來的 function 或 variable 的名字做
encode。想想,如果在 module 的開頭。我們加入一行


#define full full_Rxxxxxx


之後,我們再重新 compile module 一次,載到 kernel
之後,就可以發現 ksyms -a 顯示的是


c8822200 full_Rxxxxxx [my_module]
c01b8e08 pci_find_slot_R454463b5
. . . . .


了。那是不是說,我們要去對每一個 export 出來的
variable 和 function 做 define 的動作呢 ?
當然不是囉。記得嗎,前頭我們講去使用 kernel export
的 function 時,由於 include 了一些 .ver
的檔案,以致於我們不用再做 define
的動作。現在,我們也要利用 .ver
的檔案來幫我們,使我們 module export 出來的 function
也可以自動加入 kernel version 的 information。也就是變成
full_Rxxxxxx 之類的東西。


Linux 裡提供了一個 command,叫
genksyms,就是用來幫我們產生這種 .ver
的檔案的。它會從 stdin 裡讀取 source code,然後檢查
source code 裡是否有 export 的 variable 或
function。如果有,它就會自動為每個 export
出來的東西產生一些 define。這些 define
就是我們之前說的。等我們有了這些 define
之後,只要在我們的 module 裡加入這些 define,那 export
出來的 function 或 variable 就會變成上面那個樣子。


假設我們的程序都放在一個叫 main.c
的檔案裡,我們可以使用下列的方式產生這些 define。


gcc -E -D__GENKSYMS__ main.c genksyms -k 2.2.1 > main.ver


gcc 的 -E 參數是指將 preprocessing 的結果 show
出來。也就是說將它 include 的檔案,一些 define
的結果都展開。-D__GENKSYMS__
是一定要的。如果沒有定義這個
constant,你將不會看到任何的結果。用一個管線是因為
genksyms 是從 stdin 讀資料的,所以,經由管線將 gcc
的結果傳給 genksyms。-k 2.2.1 是指目前使用的 kernel
版本是 2.2.1,如果你的 kernel
版本不一樣,必須指定你的 kernel 的版本。產生的
define 將會被放到 main.ver 裡。產生完 main.ver
檔之後,在 main.c 裡將它 include 進來,那一切就 OK
了。有件事要告訴各位的是,使用這個方式產生的
module,其 export 出來的東西會經由 main.ver 的 define
改頭換面。所以如果你要讓別人使用,那你必須將
main.ver 公開,不然,別人就沒辦法使用你 export
出來的東西了。


講了這?多,相信各位應該都已經比較清楚 module 在
kernel 中是怎?樣一回事,也應該知道為什?有時候
module
會無法加載了。除此之外,各位應該還知道如何使自己
module export 出來的東西也具有 kernel version 的
information。


接下來,要跟各位講的就是,如何寫一個 module
了。其實,寫一個 module
很簡單的。如果你瞭解我上面所說的東西。那我再講一次,再用個例子,相信大家就都會了。要寫一個
module,必須要提供兩個 function。這兩個 function 是給
insmod 和 rmmod 使用的。它們分別是 init_module(),以及
cleanup_module()。


int init_module();
void cleanup_module();


相信大家都知道在 Linux 裡可以使用 insmod 這個 command
來將某個 module 加載。比方說,我有一個 module 叫
hello.o,那使用 insmod hello.o 就可以將 hello 這個 module
載到 kernel 裡。觀察 /etc/modules 應該就可以看到 hello
這個 module 的名字。如果要將 hello 這個 module
移除,則只要使用 rmmod hello 就可以了。insmod 在加載
module 之後,就會去呼叫 module 所提供的
init_module()。如果傳回 0 表示成功,那 module
就會被加載。如果失敗,那加載的動作就會失敗。一般來講,我們在
init_module()
做的事都是一些初始化的工作。比方說,你的 module
需要一塊內存,那你就可以在 init_module() 做 kmalloc
的動作。想當然爾。cleanup_module() 就是在 module
要移除的時候做的事。做的事一般來講就是一些善後的工作,比方像把之前
kmalloc 的內存 free 掉。


由於 module 是載到 kernel 使用的,所以,可能別的 module
會使用你的 module,甚至某些 process 也會使用到你的
module,為了避免 module 還有人使用時就被移除,每個
module 都有一個 use count。用來記錄目前有多少個 process
或 module 正在使用這個 module。當 module 的 use count
不等於 0 時,module 是不會被移除掉的。也就是說,當
module 的 use count 不等於 0 時,cleanup_module()
是不會被呼叫的。


在此,我要介紹三個 macro,是跟 module 的 use count
有關的。


MOD_INC_USE_COUNT
MOD_DEC_USE_COUNT
MOD_IN_USE


MOD_INC_USE_COUNT 是用來增加 module 的 use count,而
MOD_DEC_USE_COUNT 是用來減少 module 的 use count。至於
MOD_IN_USE 則是用來檢查目前這個 module
是不是被使用中。也就是檢查 use count 是否為 0。module
的 use count 必須由寫 module 的人自己來
maintain。系統並不會自動為你把 use count
加一或減一。一切都得由自己控制。下面有一個例子,但是,並不會介紹這三個
macro
的使用方法。將來如果有機會,我再來介紹這三個
macro 的用法。


這個例子很簡單。其實只是示範如何使用 init_module()
以及 cleanup_module() 來寫一個 module。當然,這兩個
function 只是構成 module 的基本條件罷了。至於 module
裡要提供的功能則是看各人的需要。


main.c
#define MODULE
#include <linux/module.h>
#include <asm/uaccess.h>
int full;
EXPORT_SYMBOL(full); /* 將 full export 出去 */
int init_module( void )
{
printk( "<5> Module is loadedn" );
return 0;


}


void cleanup_module( void )
{
printk( "<5> Module is unloadedn" );


}


關於 printk 是這樣子的,它是 kernel
所提供的一個打印訊息的 function。kernel 有 export 這個
function。所以你可以自由的使用它。它的用法跟 printf
幾乎一模一樣。唯獨訊息的開頭是
<5>,其實,不見得這三個字符啦。也可以是
<4>,<3>,<7> 等等的東西。這是代表這個訊息的 prioirty
或 level。<5> 表示的是跟 KERNEL 有關的訊息。

main.ver:


利用 genksyms 產生出來的。


gcc -E -D__GENKSYMS__ main.c genksyms -k 2.2.1 > main.ver


接下來,就是要把 main.c compile 成 main.o


gcc -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -c -I/usr/src/linux/include/linux -include /usr/src/linux/include/linux/modversions.h -include ./main.ver main.c


好了。main.o 已經成功的 compile 出來了,現在下一個
command,


insmod main.o


檢查看 /proc/modules 裡是否有 main 這個
module。如果有,表示 main 這個 module 已經載到 kernel
了。再下一個指令,看看 full export 出去的結果。


ksyms


結果顯示


Address Symbol Defined by
c40220e0 full_R355b84b2 [main]
c401d04c ne_probe [ne]
c401a04c ei_open [8390]
c401a094 ei_close [8390]
c401a504 ei_interrupt [8390]
c401af1c ethdev_init [8390]
c401af80 NS8390_init [8390]


可以看到 full_R355b84b2,表示,我們已經成功的將 full
的名字加上 kernel version 的 information
了。當我們不需要這個 module 時,我們就可以下一個
command,


rmmod main


這樣 main 就會被移除掉了。再檢查看看 /proc/modules
就可以發現 main 那一行不見了。各位現在可以看一下
/var/log/message 這個檔案,應該可以發現以兩行


Apr 12 14:19:05 host kernel: Module is loaded
Apr 12 14:39:29 host kernel: Module is unloaded


這兩行就是 printk 印出來的。


關於 module 的介紹已經到此告一段落了。其實,使用
module 實在是很簡單的一件事。對於要發展 driver
或是增加 kernel 某些新功能的人來講,用 module
不啻為一個方便的方式。希望這篇文章對各位能有所幫助。

2006年5月11日 星期四

Linux上使用RamDisk

方法一
tmpfs 是一套以使用記憶體為基礎的檔案系統,不同於 ramdisk 的最大差別在於, ramdisk 雖然也使用記憶體..但它的行為屬於 Block device ,所以使用前必須先 format ( mkfs ) ,而 tmpfs 使用上就很方便囉.. 直接 mount 即可,例如把系統的 /tmp 改為 tmpfs ,您只要在 /etc/fstab 加入底下這段即可:
tmpfs? ?/tmp? ?tmpfs? ?size=100m,mode=1777? ?0 0
其中 size 的單位為 mega byte ,請依照您的記憶體容量作適度調整,當您這麼做之後任何放在 /tmp 底下的檔案都將佔掉記憶體的容量! tmpfs 的最大好處自然是速度快囉!但請留意因為是使用記憶體空間,所以重新開機後資料將全部消失!
debian 跟其他 linux distribution 很大不同點是,系統設計上會在每次開機時清除 /tmp 資料,所以把 /tmp 改為 tmpfs 對於 debian 來說是非常合適的應用.

方法二
#! /bin/sh # # mounttmpdir Mount /tmp after mounvirtfs exec-ed # # Version: @(#)mounttmpdir 0.01 24-Dec-2004 antony shen # # Script needs to be robust and continue when parts fail, # so we're not setting the "-e" flag. #set -e PATH=/lib/init:/bin:/sbin if [ -d /dev/shm ] then if [ ! -d /dev/shm/tmp ] then echo -n "Mounting /tmp under /dev/shm..." mkdir /dev/shm/tmp chmod 1777 /dev/shm/tmp mount --bind /dev/shm/tmp /tmp echo "OK. fi else echo "Failed to find /dev/shm, unable to link /tmp under it!" fi 接者,可以用 update-rc.d 指令: update-rc.d mounttmpdir start 37 S . 將這個 init.d script 加到 rcS 裡面

2006年5月9日 星期二

samba issue

Nautilus連上windows網芳


debain 安裝libgnomevfs2-extra


中文顯示


smbmount //x.x.x.x/name /mnt/CORDITE2 -o username=cordite2,password=cordite2,unicode,codepage=unicode,iocharset=utf8

Adobe Reader 無法啟動的問題

Adobe Reader和Scim衝突
把這一行加入到/usr/bin/acroread

export GTK_IM_MODULE=xim

2006年4月4日 星期二

Linux Signal_4

.3.2 信號集及其操作
信號集的操作函數, sigsetops
============================================================= #include
int sigemptyset (sigset_t *set);
int sigfillset (sigset_t *set);
int sigaddset (sigset_t *set, int signum);
int sigdelset (sigset_t *set, int signum);
int sigismember (const sigset_t *set, int signum);
-------------------------------------------------------------------------------
上述函數的一種實現:
#define sigemptyset (ptr) ( *(ptr) = 0 )
#define sigfillset (ptr) ( *(ptr) = ~(sigset_t)0, 0 )
/* C 語言的逗號運算符: 返回右邊的值作為表達式的返回值 */
#include
#include
#define SIGBAD (signo) ((signo) <=0 (signo) >= NSIG)
int sigaddset (sigset_t *set, int signo)
{
if (SIGBAD (signo)) {errno = EINVAL; return -1;}
*set != 1 << (signo -1);
return 0;
}
int sigdelset (sigset_t *set, int signo)
{
if (SIGBAD (signo)) {errno = EINVAL; return -1;}
*set &= ~(1 << (signo -1));
return 0;
}
int sigismember (sigset_t *set, int signo)
{
if (SIGBAD (signo)) {errno = EINVAL; return -1;}
return ((*set & (1<< (signo - 1))) != 0);
}
=============================================================
7.3.3 可靠信號系統調用
sigaction, sigprocmask, sigpending, sigsuspend
============================================================= #include
int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact);
int sigprocmask (int how, const sigset_t *set, sigset_t *oldset);
int sigpending (sigset_t *set);
int sigsuspend (const sigset_t *mask);
-------------------------------------------------------------------------------
struct sigaction {
void (*sa_handler) (int);
void (*sa_sigaction) (int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void); // 廢棄的元素, 不應使用
}
-------------------------------------------------------------------------------
sigaction:
* sigaction 用於改變進程在接收到信號時的動作.
* sigaction 可指定除 SIGKILL 和 SIGSTOP 之外的信號的動作 (act->sa_handler),
可取 SIG_DFL, SIG_IGN 和用戶定義函數.
* sigaction 可指定在處理 signum 信號時, 應該阻塞的信號集 (act->sa_mask).
* sigaction 可指定信號處理過程中的行為 (act->sa_flags), 下述標誌 "或" 的結果:
o SA_NOCLDSTOP: 子進程停止時不接收 SIGCHLD.
o SA_ONESHOT 或 SA_RESETHAND: 重置信號動作為默認值.
o SA_RESTART: 如果該信號中斷慢系統調用, 則重新啟動系統調用.
o SA_NOMASK 或 SA_NODEFER: 不要避免在信號處理函數中接收同一信號.
o SA_SIGINFO: 信號處理函數接受三個參數. 這時, 必須設置 act->sa_sigaction
元素, 其中 siginfo_t 是內核傳遞到信號處理函數中的發生信號時進程的狀態
信息. 詳細信息, 可參閱 sigaction(2) 手冊頁.
* oldact 非空時, 可返回先前的設置.
* 利用 SA_SIGINFO 和 siginfo_t 實現有效的存儲管理.
-------------------------------------------------------------------------------
sigprocmask:
* sigprocmask 用於改變進程的當前阻塞信號集.
* how 可取 SIG_BLOCK, SIG_UNBLOCK 和 SIG_MASKSET. 前兩個動作分別在當前阻塞
信號集中添加或刪除由 set 指定的信號集, SIG_MASK 用於完全設置阻塞信號集.
* oldset 非空時, 可返回先前的設置.
-------------------------------------------------------------------------------
sigpending:
* sigpending 用於檢驗掛起的信號.
-------------------------------------------------------------------------------
sigsuspend:
* sigsuspend 用於在接收到某個信號之前, 臨時用 mask 替換進程的信號掩碼, 並
暫停進程執行.
* sigsuspend 返回後將恢復調用之前的信號掩碼.
* 該系統調用始終返回 -1, 並將 errno 設置為 EINTR.
* 該系統調用實際是阻塞並暫停兩個動作的原子操作.
=============================================================保護關鍵代碼段示例
============================================================= #include
#include
#include
#include
#include
#include
static void sig_int (int);
void err_sys (const char* info)
{
perror (info);
exit (1);
}
void pr_mask (const char* str)
{
sigset_t sigset;
int errno_save;
errno_save = errno; /* this function may be called by signal handler */
if (sigprocmask (0, NULL, &sigset) < 0)
err_sys ("sigprocmask error");
printf ("%s", str);
if (sigismember (&sigset, SIGINT)) printf ("SIGINT ");
if (sigismember (&sigset, SIGQUIT)) printf ("SIGQUIT ");
if (sigismember (&sigset, SIGUSR1)) printf ("SIGUSR1 ");
if (sigismember (&sigset, SIGALRM)) printf ("SIGALRM ");
printf ("\n");
errno = errno_save;
}
int main (void)
{
sigset_t newmask, oldmask, zeromask;
if (signal (SIGINT, sig_int) == SIG_ERR)
err_sys ("signal (SIGINT) error");
sigemptyset (&zeromask);
sigemptyset (&newmask);
sigaddset (&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if (sigprocmask (SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys ("SIG_BLOCK error");

/* critical region of code */
pr_mask ("in critical region: ");
/* allow all signals and pause */
if (sigsuspend (&zeromask) != -1)
err_sys ("sigsuspend error");
pr_mask ("after return from sigsuspend: ");
/* reset signal mask whick unblocks SIGINT */
if (sigprocmask (SIG_SETMASK, &oldmask, NULL) < 0)
err_sys ("SIG_SETMASK error");
/* and continue processing ... */
exit (0);
}
static void sig_int (int signo)
{
pr_mask ("\nin sig_int: ");
return;
}
-------------------------------------------------------------------------------
* 在 Linux 下, 必須包含頭文件:
#include
以便使用可靠的 signal 函數. 或者使用 sigaction 函數.
=============================================================7.3.4 sigsetjmp 和 siglongjmp
sigsetjmp 和 siglongjmp 專用於從信號處理函數中進行遠跳轉
並能夠明確指定是否保存信號掩碼
============================================================= #include
int sigsetjmp (sigjmp_buf env, int savesigs);
void siglongjmp (sigjmp_buf env, int val);
-------------------------------------------------------------------------------
* 如果 savesigs 非零, 則 siglongjmp 在跳轉之後將恢復保存的信號掩碼.
=============================================================

Linux Signal_3

一、信號生命週期
從信號發送到信號處理函數的執行完畢
對於一個完整的信號生命週期(從信號發送到相應的處理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:信號誕生;信號在進程中註冊完畢;信號在進程中的註銷完畢;信號處理函數執行完畢。相鄰兩個事件的時間間隔構成信號生命週期的一個階段。
下面闡述四個事件的實際意義:
信號"誕生"。信號的誕生指的是觸發信號的事件發生(如檢測到硬件異常、定時器超時以及調用信號發送函數kill()或sigqueue()等)。
信號在目標進程中"註冊";進程的task_struct結構中有關於本進程中未決信號的數據成員:
struct sigpending pending:
struct sigpending{
struct sigqueue *head, **tail;
sigset_t signal;
};
第三個成員是進程中所有未決信號集,第一、第二個成員分別指向一個sigqueue類型的結構鏈(稱之為"未決信號信息鏈")的首尾,信息鏈中的每個sigqueue結構刻畫一個特定信號所攜帶的信息,並指向下一個sigqueue結構:
struct sigqueue{
struct sigqueue *next;
siginfo_t info;
}
信號在進程中註冊指的就是信號值加入到進程的未決信號集中(sigpending結構的第二個成員sigset_t signal),並且信號所攜帶的信息被保留到未決信號信息鏈的某個sigqueue結構中。只要信號在進程的未決信號集中,表明進程已經知道這些信號的存在,但還沒來得及處理,或者該信號被進程阻塞。
註:
當一個實時信號發送給一個進程時,不管該信號是否已經在進程中註冊,都會被再註冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。這意味著同一個實時信號可以在同一個進程的未決信號信息鏈中佔有多個sigqueue結構(進程每收到一個實時信號,都會為它分配一個結構來登記該信號信息,並把該結構添加在未決信號鏈尾,即所有誕生的實時信號都會在目標進程中註冊);
當一個非實時信號發送給一個進程時,如果該信號已經在進程中註冊,則該信號將被丟棄,造成信號丟失。因此,非實時信號又叫做"不可靠信號"。這意味著同一個非實時信號在進程的未決信號信息鏈中,至多佔有一個sigqueue結構(一個非實時信號誕生後,(1)、如果發現相同的信號已經在目標結構中註冊,則不再註冊,對於進程來說,相當於不知道本次信號發生,信號丟失;(2)、如果進程的未決信號中沒有相同信號,則在進程中註冊自己)。
信號在進程中的註銷。在目標進程執行過程中,會檢測是否有信號等待處理(每次從系統空間返回到用戶空間時都做這樣的檢查)。如果存在未決信號等待處理且該信號沒有被進程阻塞,則在運行相應的信號處理函數前,進程會把信號在未決信號鏈中佔有的結構卸掉。是否將信號從進程未決信號集中刪除對於實時與非實時信號是不同的。對於非實時信號來說,由於在未決信號信息鏈中最多只佔用一個sigqueue結構,因此該結構被釋放後,應該把信號在進程未決信號集中刪除(信號註銷完畢);而對於實時信號來說,可能在未決信號信息鏈中佔用多個sigqueue結構,因此應該針對佔用sigqueue結構的數目區別對待:如果只佔用一個sigqueue結構(進程只收到該信號一次),則應該把信號在進程的未決信號集中刪除(信號註銷完畢)。否則,不應該在進程的未決信號集中刪除該信號(信號註銷完畢)。
進程在執行信號相應處理函數之前,首先要把信號在進程中註銷。
信號生命終止。進程註銷信號後,立即執行相應的信號處理函數,執行完畢後,信號的本次發送對進程的影響徹底結束。
註:
1)信號註冊與否,與發送信號的函數(如kill()或sigqueue()等)以及信號安裝函數(signal()及sigaction())無關,只與信號值有關(信號值小於SIGRTMIN的信號最多只註冊一次,信號值在SIGRTMIN及SIGRTMAX之間的信號,只要被進程接收到就被註冊)。
2)在信號被註銷到相應的信號處理函數執行完畢這段時間內,如果進程又收到同一信號多次,則對實時信號來說,每一次都會在進程中註冊;而對於非實時信號來說,無論收到多少次信號,都會視為只收到一個信號,只在進程中註冊一次。
二、信號編程注意事項
防止不該丟失的信號丟失。如果對八中所提到的信號生命週期理解深刻的話,很容易知道信號會不會丟失,以及在哪裡丟失。
程序的可移植性
考慮到程序的可移植性,應該盡量採用POSIX信號函數,POSIX信號函數主要分為兩類:
POSIX 1003.1信號函數: Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。
POSIX 1003.1b信號函數。POSIX 1003.1b在信號的實時性方面對POSIX 1003.1做了擴展,包括以下三個函數: sigqueue()、sigtimedwait()、sigwaitinfo()。其中,sigqueue主要針對信號發送,而sigtimedwait及sigwaitinfo()主要用於取代sigsuspend()函數,後面有相應實例。
#include <signal.h>
int sigwaitinfo(sigset_t *set, siginfo_t *info).
該函數與sigsuspend()類似,阻塞一個進程直到特定信號發生,但信號到來時不執行信號處理函數,而是返回信號值。因此為了避免執行相應的信號處理函數,必須在調用該函數前,使進程屏蔽掉set指向的信號,因此調用該函數的典型代碼是:
sigset_t newmask;
int rcvd_sig;
siginfo_t info;
sigemptyset(&newmask);
sigaddset(&newmask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &newmask, NULL);
rcvd_sig = sigwaitinfo(&newmask, &info)
if (rcvd_sig == -1) {
..
}
調用成功返回信號值,否則返回-1。sigtimedwait()功能相似,只不過增加了一個進程等待的時間。
程序的穩定性。
為了增強程序的穩定性,在信號處理函數中應使用可重入函數。
信號處理程序中應當使用可再入(可重入)函數(註:所謂可重入函數是指一個可以被多個任務調用的過程,任務在調用時不必擔心數據是否會出錯)。因為進程在收到信號後,就將跳轉到信號處理函數去接著執行。如果信號處理函數中使用了不可重入函數,那麼信號處理函數可能會修改原來進程中不應該被修改的數據,這樣進程從信號處理函數中返回接著執行時,可能會出現不可預料的後果。不可再入函數在信號處理函數中被視為不安全函數。
滿足下列條件的函數多數是不可再入的:
(1)使用靜態的數據結構,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;
(2)函數實現時,調用了malloc()或者free()函數;
(3)實現時使用了標準I/O函數的。The Open Group視下列函數為可再入的:
_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown()、close()、creat()、dup()、dup2()、execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、kill()、link()、lseek()、mkdir()、mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid()、setpgid()、setsid()、setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、 umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。
即使信號處理函數使用的都是"安全函數",同樣要注意進入處理函數時,首先要保存errno的值,結束時,再恢復原值。因為,信號處理過程中,errno值隨時可能被改變。另外,longjmp()以及siglongjmp()沒有被列為可再入函數,因為不能保證緊接著兩個函數的其它調用是安全的。
三、深入淺出:信號應用實例
linux下的信號應用並沒有想像的那麼恐怖,程序員所要做的最多只有三件事情:
安裝信號(推薦使用sigaction());
實現三參數信號處理函數,handler(int signal,struct siginfo *info, void *);
發送信號,推薦使用sigqueue()。
實際上,對有些信號來說,只要安裝信號就足夠了(信號處理方式採用缺省或忽略)。其他可能要做的無非是與信號集相關的幾種操作。
實例一:信號發送及處理
實現一個信號接收程序sigreceive(其中信號安裝由sigaction())。
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op;
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d", signum);
sleep(5);
}
說明,命令行參數為信號值,後台運行sigreceive signo &,可獲得該進程的ID,假設為pid,然後再另一終端上運行kill -s signo pid驗證信號的發送接收及處理。同時,可驗證信號的排隊問題。
註:可以用sigqueue實現一個命令行信號發送程序sigqueuesend,見附錄1。
實例二:信號傳遞附加信息
主要包括兩個實例:
向進程本身發送信號,並傳遞指針參數;
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
union sigval mysigval;
int i;
int sig;
pid_t pid;
char data[10];
memset(data,0,sizeof(data));
for(i=0;i < 5;i++)
data[i]='2';
mysigval.sival_ptr=data;
sig=atoi(argv[1]);
pid=getpid();
sigemptyset(&act.sa_mask);
act.sa_sigaction=new_op;//三參數信號處理函數
act.sa_flags=SA_SIGINFO;//信息傳遞開關
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
sigqueue(pid,sig,mysigval);//向本進程發送信號,並傳遞附加信息
}
}
void new_op(int signum,siginfo_t *info,void *myact)//三參數信號處理函數的實現
{
int i;
for(i=0;i<10;i++)
{
printf("%c\n ",(*( (char*)((*info).si_ptr)+i)));
}
printf("handle signal %d over;",signum);
}
這個例子中,信號實現了附加信息的傳遞,信號究竟如何對這些信息進行處理則取決於具體的應用。
2、 不同進程間傳遞整型參數:把1中的信號發送和接收放在兩個程序中,並且在發送過程中傳遞整型參數。
信號接收程序:
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
pid_t pid;
pid=getpid();
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_sigaction=new_op;
act.sa_flags=SA_SIGINFO;
if(sigaction(sig,&act,NULL)<0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("the int value is %d \n",info->si_int);
}
信號發送程序:命令行第二個參數為信號值,第三個參數為接收進程ID。
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
main(int argc,char**argv)
{
pid_t pid;
int signum;
union sigval mysigval;
signum=atoi(argv[1]);
pid=(pid_t)atoi(argv[2]);
mysigval.sival_int=8;//不代表具體含義,只用於說明問題
if(sigqueue(pid,signum,mysigval)==-1)
printf("send error\n");
sleep(2);
}
註:實例2的兩個例子側重點在於用信號來傳遞信息,目前關於在linux下通過信號傳遞信息的實例非常少,倒是Unix下有一些,但傳遞的基本上都是關於傳遞一個整數,傳遞指針的我還沒看到。我一直沒有實現不同進程間的指針傳遞(實際上更有意義),也許在實現方法上存在問題吧,請實現者email我。
實例三:信號阻塞及信號集操作
#include "signal.h"
#include "unistd.h"
static void my_op(int);
main()
{
sigset_t new_mask,old_mask,pending_mask;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=(void*)my_op;
if(sigaction(SIGRTMIN+10,&act,NULL))
printf("install signal SIGRTMIN+10 error\n");
sigemptyset(&new_mask);
sigaddset(&new_mask,SIGRTMIN+10);
if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask))
printf("block signal SIGRTMIN+10 error\n");
sleep(10);
printf("now begin to get pending mask and unblock SIGRTMIN+10\n");
if(sigpending(&pending_mask)<0)
printf("get pending mask error\n");
if(sigismember(&pending_mask,SIGRTMIN+10))
printf("signal SIGRTMIN+10 is pending\n");
if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0)
printf("unblock signal error\n");
printf("signal unblocked\n");
sleep(10);
}
static void my_op(int signum)
{
printf("receive signal %d \n",signum);
}
編譯該程序,並以後台方式運行。在另一終端向該進程發送信號(運行kill -s 42 pid,SIGRTMIN+10為42),查看結果可以看出幾個關鍵函數的運行機制,信號集相關操作比較簡單。
註:在上面幾個實例中,使用了printf()函數,只是作為診斷工具,pringf()函數是不可重入的,不應在信號處理函數中使用。
用sigqueue實現的命令行信號發送程序sigqueuesend,命令行第二個參數是發送的信號值,第三個參數是接收該信號的進程ID,可以配合實例一使用:
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char**argv)
{
pid_t pid;
int sig;
sig=atoi(argv[1]);
pid=atoi(argv[2]);
sigqueue(pid,sig,NULL);
sleep(2);
}