網頁

2006年6月7日 星期三

不定引數的例子

void _db_print(const char *format,...){
va_list args;
time_t tnow;
struct tm *tmp;
va_start(args, format);
{
tnow = time((time_t*)NULL);
tmp=localtime(&tnow);
fprintf(stderr,"%d-%02d-%02d %02d:%02d:%02d.",\
1900+tmp->tm_year,tmp->tm_mon+1,tmp->\
tm_mday,tmp->tm_hour,tmp->tm_min,tmp->tm_sec);
}
vfprintf(stderr,format, args);
va_end(args);
}
int debug_level=0;
#ifndef debug
#define debug(LEVEL) (LEVEL ? debug_level) ? (void) 0 : _db_print
#endif
#define dbgprint(format,args...) \
fprintf(stderr, format, ##args)

#define PRINTK(format, args...) {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);
#define debug(LEVEL) (LEVEL > debug_level) ? (void) 0 : _db_print

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月15日 星期六

簡單地說,va函數的實現就是對參數指針的使用和控制。
typedef char * va_list; // x86平台下va_list的定義
函數的固定參數部分,可以直接從函數定義時的參數名獲得;對於可選參數部分,先將指針指向第一個可選參數,然後依次後移指針,根據與結束標誌的比較來判斷是否已經獲得全部參數。因此,va函數中結束標誌必須事先約定好,否則,指針會指向無效的記憶體地址,導致出錯。
這裡,移動指針使其指向下一個參數,那麼移動指針時的偏移量是多少呢,沒有具體答案,因為這裡涉及到記憶體對齊(alignment)問題,記憶體對齊跟具體使用的硬體平臺有密切關係,比如大家熟知的32位x86平臺規定所有的變數地址必須是4的倍數(sizeof(int) = 4)。va機制中用宏_INTSIZEOF(n)來解決這個問題,沒有這些宏,va的可攜性無從談起。
首先介紹宏_INTSIZEOF(n),它求出變數佔用記憶體空間的大小,是va的實現的基礎。
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//第一個可選參數地址
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一個參數地址
#define va_end(ap) ( ap = (va_list)0 )
//將指針置為無效
下表是針對函數int TestFunc(int n1, int n2, int n3, …)
參數傳遞時的記憶體堆棧情況。(C編譯器默認的參數傳遞方式是__cdecl。)
對該函數的調用為int result = TestFunc(a, b, c, d. e); 其中e為結束標誌。

高位址
最後一個可選參數 => 用va_start後,arg_ptr指向下一個可選參數的位址
第一個可選參數 => 用va_start後,arg_ptr指向的位址
N3位址
N2位址
N1位址
函式返回位址
低位址

從上圖中可以很清楚地看出va_xxx宏如此編寫的原因。
1. va_start。為了得到第一個可選參數的地址,我們有三種辦法可以做到:
A) = &n3 + _INTSIZEOF(n3)
// 最後一個固定參數的地址 + 該參數佔用記憶體的大小
B) = &n2 + _INTSIZEOF(n3) + _INTSIZEOF(n2)
// 中間某個固定參數的地址 + 該參數之後所有固定參數佔用的記憶體大小之和
C) = &n1 + _INTSIZEOF(n3) + _INTSIZEOF(n2) + _INTSIZEOF(n1)
// 第一個固定參數的地址 + 所有固定參數佔用的記憶體大小之和
從編譯器實現角度來看,方法B),方法C)為了求出地址,編譯器還需知道有多少個固定參數,以及它們的大小,沒有把問題分解到最簡單,所以不是很聰明的途徑,不予採納;相對來說,方法A)中運算的兩個值則完全可以確定。va_start()正是採用A)方法,接受最後一個固定參數。調用va_start()的結果總是使指針指向下一個參數的地址,並把它作為第一個可選參數。在含多個固定參數的函數中,調用va_start()時,如果不是用最後一個固定參數,對於編譯器來說,可選參數的個數已經增加,將給程式帶來一些意想不到的錯誤。(當然如果你認為自己對指針已經知根知底,遊刃有餘,那麼,怎麼用就隨你,你甚至可以用它完成一些很優秀(高效)的代碼,但是,這樣會大大降低代碼的可讀性。)
注意:宏va_start是對參數的地址進行操作的,要求參數地址必須是有效的。一些地址無效的類型不能當作固定參數類型。比如:寄存器類型,它的地址不是有效的記憶體地址值;數組和函數也不允許,他們的長度是個問題。因此,這些類型時不能作為va函數的參數的。
2. va_arg身兼二職:返回當前參數,並使參數指針指向下一個參數。
初看va_arg宏定義很彆扭,如果把它拆成兩個語句,可以很清楚地看出它完成的兩個職責。
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一個參數地址
// 將( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )拆成:
/* 指針ap指向下一個參數的地址 */
1. ap += _INTSIZEOF(t); // 當前,ap已經指向下一個參數了
/* ap減去當前參數的大小得到當前參數的地址,再強制類型轉換後返回它的值 */
2. return *(t *)( ap - _INTSIZEOF(t))
回想到printf/scanf系列函數的%d %s之類的格式化指令,我們不難理解這些它們的用途了- 明示參數強制轉換的類型。
(注:printf/scanf沒有使用va_xxx來實現,但原理是一致的。)
3.va_end很簡單,僅僅是把指針作廢而已。
#define va_end(ap) (ap = (va_list)0) // x86平臺
四、 簡潔、靈活,也有危險
從va的實現可以看出,指針的合理運用,把C語言簡潔、靈活的特性表現得淋漓盡致,叫人不得不佩服C的強大和高效。不可否認的是,給編程人員太多自由空間必然使程式的安全性降低。va中,為了得到所有傳遞給函數的參數,需要用va_arg依次遍歷。其中存在兩個隱患:
1)如何確定參數的類型。
2)va_arg在類型檢查方面與其說非常靈活,不如說是很不負責,因為是強制類型轉換,va_arg都把當前指針所指向的內容強制轉換到指定類型;
3)2)結束標誌。如果沒有結束標誌的判斷,va將按默認類型依次返回記憶體中的內容,直到訪問到非法記憶體而出錯退出。例2中SqSum()求的是自然數的平方和,所以我把負數和0作為它的結束標誌。例如scanf把接收到的回車符作為結束標誌,大家熟知的printf()對字符串的處理用'\0'作為結束標誌,無法想像C中的字符串如果沒有'\0', 代碼將會是怎樣一番情景,估計那時最流行的可能是字符數組,或者是malloc/free。
4)允許對記憶體的隨意訪問,會留給不懷好意者留下攻擊的可能。當處理cracker精心設計好的一串字符串後,程式將跳轉到一些惡意代碼區域執行,以使cracker達到其攻擊目的。(常見的exploit攻擊)所以,必需禁止對記憶體的隨意訪問和嚴格控制記憶體訪問邊界。

2006年4月12日 星期三

Apache-Enabling a cgi directory to each user

Enabling a cgi directory for each user
In order to give each user their own cgi-bin directory, you can use a directive to make a particular subdirectory of a user's home directory cgi-enabled.
<Directory /home/*/public_html/cgi-bin/>Options ExecCGISetHandler cgi-script</Directory>

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的信號最多只註冊一次,信號值在SIGRTMINSIGRTMAX之間的信號,只要被進程接收到就被註冊)。
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主要針對信號發送,而sigtimedwaitsigwaitinfo()主要用於取代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) {
..
}
調用成功返回信號值,否則返回-1sigtimedwait()功能相似,只不過增加了一個進程等待的時間。
程序的穩定性。
為了增強程序的穩定性,在信號處理函數中應使用可重入函數。
信號處理程序中應當使用可再入(可重入)函數(註:所謂可重入函數是指一個可以被多個任務調用的過程,任務在調用時不必擔心數據是否會出錯)。因為進程在收到信號後,就將跳轉到信號處理函數去接著執行。如果信號處理函數中使用了不可重入函數,那麼信號處理函數可能會修改原來進程中不應該被修改的數據,這樣進程從信號處理函數中返回接著執行時,可能會出現不可預料的後果。不可再入函數在信號處理函數中被視為不安全函數。
滿足下列條件的函數多數是不可再入的:
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 pidSIGRTMIN+1042),查看結果可以看出幾個關鍵函數的運行機制,信號集相關操作比較簡單。
註:在上面幾個實例中,使用了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);
}