一、kill, raise, killpg 函數
int kill(pid_t pid, int sig);
int raise(int sig);
int killpg(int pgrp, int sig);
kill命令是調用kill函數實現的,kill函數可以給一個指定的進程或進程組發送指定的訊號,其中kill 函數的pid 參數取值不同表示不同含義,具體可man 一下。raise函數可以給當前進程發送指定的訊號(自己給自己發訊號)。killpg 函數可以給進程組發生訊號。這三個函數都是成功返回0,錯誤返回-1。下面是個小程式樣本:
/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: dameng34@163.com
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if (pid == -1)
ERR_EXIT("fork error");
if (pid == 0)
{
/*
pid = getpgrp(); // 得到進程組pid
kill(-pid, SIGUSR1); //向進程組發送訊號
*/
killpg(getpgrp(), SIGUSR1);
exit(EXIT_SUCCESS); // 子進程處理完訊號才退出
}
int n = 5;
do
{
n = sleep(n); // sleep會被訊號打斷,返回unslept的時間
}
while (n > 0);
return 0;
}
void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
/* raise(sig) 等價於 kill(getpid(), sig) 給自己發送訊號 */
程式中註冊訊號在fork之前,故子進程也會繼承,在子進程中對進程組發送了訊號,故訊號處理函數會被調用兩次:
simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./kill
recv a sig=10
recv a sig=10
simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$
因為sleep 函數會被訊號處理函數打斷(RETURN VALUE: Zero if the requested time has elapsed, or the number of seconds left to sleep, if the call was interrupted by a signal handler.),如果我們想讓其睡夠5s, 則可以用一個while迴圈判斷其傳回值。這裡需要注意的是輸出兩次recv之後繼續睡眠的時間是不一定的,也有可能是5s,即訊號處理函數在調用sleep之前已經被調用(子進程先被系統調度執行),sleep未被中斷。也表明一點:只要接收到訊號,訊號處理函數可以在任意某個時刻被調用,不僅僅只在進程主動調用sleep, pause等函數(讓cpu去調度運行其他程式)的時候,cpu一直都在進行進程的調度,進行使用者空間和核心空間的切換, 當某個時刻要從核心返回到該進程的使用者空間代碼繼續執行之前,首先就會處理PCB中記錄的訊號。