一 . 信号
1. 信号:是内核发送给某一进程的一种消息 。
2. 信号机制:是Linux系统中用于进程之间相互通信或操作的一种机制。
3. 信号的来源:信号来源于内核
4. 产生原因: (1)用户通过终端输入 (2)进程执行(3)一个进程调用kill向另一个进程发送信号
5. 信号分类:1)同步信号 异步信号
2)可靠信号 : 信号被递送多次 不可靠信号: 只被递送一次的信号
6. 信号处理方式
A. 按系统默认方式处理 ——每个信号都有一个默认动作,典型的默认动作是终止进程。 man signal 7 列出系统对各信号的默认处理 。
B. 忽略信号——接收信号,但是不做任何操作 。
C. 捕捉信号——程序提前告诉内核,当信号到来时应该调用哪个函数。
二. 信号相关函数
1. 简单的信号处理
函数原型: sighandler_t signal(int signum, sighandler_t handler);
参 数:signum ——要响应的信号,handler ——信号发生时要执行的操作。
返回值:非-1 执行成功,返回以前的信号处理函数指针
-1 遇到错误
//捕捉信号
#include
#include
#include
#include
void capture(int signum){
printf("SIGINT is capture!\n");
}
int main(int argc, char *argv[]){
int i=10;
if(signal(SIGINT,capture) == SIG_ERR){
printf("Can't catch SIGINT");
exit(1);
}
printf("waiting for signal...\n");
while(i > 0){
printf("Now i=%d \n",i);
sleep(1);
i--;
}
return 0;
}
//忽略信号
#include
#include
#include
int main(int argc,char *argv[]){
int i=10;
signal(SIGINT,SIG_IGN);
printf("waiting for signal ...\n");
while(i > 0){
sleep(1);
i--;
printf("Now i = %d,but you can't stop this program by Ctrl + C\n",i);
}
return 0;
}
//恢复信号的默认处理
#include
#include
void capthensfl(int signum){
printf("SIGINT is capture !\n");
signal(SIGINT,SIG_DFL);
printf("SIGINT now is defaulted !\n");
}
int main(int argc,char *argv[]){
int i=10;
signal(SIGINT,capthensfl);
printf("waiting for signal...\n");
while(i > 0){
sleep(1);
printf("Now i = %d\n",i);
i--;
}
return 0;
}
2. 信号处理函数:指定一个信号的处理函数
函数原型:int sigaction(int signum, struct sigaction *action, struct sigaction *oldaction);
参 数:signum ,要处理的信号
action 要安装的信号处理操作的结构
oldaction 之前的信号处理操作的结构
返回值:0 成功 -1,失败
说 明:oldaction不为NULL,则用其来存储以前设置的与此信号关联的操作。
action不为NULL: 则指定信号关联的操作为此参数指向的结构 否则,信号处理保持不变。(此方式用来查询当前对指定信号的处理方式)
sigaction的结构:
struct sigaction{
union{ __sighandler_t _sa_handler;
void(*_sa_sigaction)(int , struct siginfo *, void *);
} __u;
sigset_t sa_mask;
unsigned long sa_flags;
};
sa_sigaction的说明: void (*sa_sigaction)(int, struct siginfo *,void *);
第一个参数:要响应的信号 。
第二个参数:记录导致产生信号的原因、类型等 。
第三个参数:信号发生时被中断的上下文环境 信号能传递简单的信息。
//使用sa_sigaction类型函数
#include
#include
#include
#include
void fun(int signo, siginfo_t *info, void *context){
printf("Test for sa_sigaction !\n");
}
int main(int argc,char *argv[]){
struct sigaction action,oldaction;
sigemptyset(&action.sa_mask);
action.sa_flags=SA_SIGINFO;
action.sa_sigaction=fun;
sigaction(SIGINT,&action,&oldaction);
printf("waiting for signal...\n");
while(1){
pause();
}
return 0;
}
3. 初始化信号集
函数原型:int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
参 数:set 待初始化的信号集
返回值:0 成功 -1,失败
4. 添加、删除信号
函数原型:int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
参 数:set 待初始化的信号集 . signo 待添加/删除的信号编号
返回值:0 成功 -1,失败
5. 修改屏蔽信号集
函数原型:int sigprocmask(int how,sigset_t *set, sigset_t * oldset);
参 数:how 如何修改信号掩码
set 要使用的信号集
oldset 之前的信号集
返回值:0 成功 -1,失败
对参数how的说明: SIG_BLOCK:信号集中增加set中的信号
SIG_UNBLOCK:信号集中删除set中的信号
SIG_SETMASK:信号集被设置为set信号集
三. 系统调用函数
1. kill —— 向进程发送一个信号
函数原型:int kill(pid_t pid, int sig);
参 数:pid 目标进程id, sig 要发送的信号
返回值:0 成功 -1,失败
说 明: pid>0 目标进程的进程号为pid
pid=0 将信号发送给和当前进程在同一进程租的所有进程
pid=-1 将信号发送给系统内的所有进程
pid<0 将信号发送给进程组号PGID为pid绝对值的所有进程
一个进程可使用kill函数将信号发送给另一进程或进程组。
要求:发送信号的进程的用户ID和目标进程的用户ID相同,或发送信号的进程的owner是一个超级用户。
//kill 使用示例
#include
#include
#include
void fun(int signo){
printf("Process capture SIGINT");
signal(SIGINT,SIG_DFL);
}
int main(int argc,char *argv[]){
int pid;
if((pid=fork()) == -1){
perror("fork");
exit(EXIT_FAILURE);
}
else if( pid == 0){
signal(SIGINT,fun);
printf("Child %d waiting for parent %d send signal\n",getpid(),getppid());
pause();
pause();
}
else{
sleep(1);
printf("Parent %d will send signal to child %d \n",getppid(),getpid());
kill(pid,SIGINT);
wait(NULL);
}
return 0;
}
2. raise —— 自举一个信号
函数原型:int raise(int signo);
参 数:signo 待发送的信号
返回值:0 成功 -1,失败
说 明:raise(signo)=kill(getpid(),signo)
3. alarm —— 设置计时器
函数原型:int alarm(int seconds);
参 数:seconds 计时的秒数
返回值: 0 之前未调用过alarm
>0 之前调用alarm设置的闹钟时间的余留秒数
4. pause —— 挂起调用进程
函数原型:int pause(void);
返回值:-1 并将errno设置为EINTR
四. 信号集合操作应用示例
/* 信号的应用示例:
父进程执行文件复制操作,如果收到SIGUSR1信号,就打印出当前的复制进度
子进程每隔一个固定时间向父进程发送SIGUSR1信号
*/
#include
#include
#include
#include
#include
#include
int count; //当前复制大小
int file_size; //文件的大小
//父进程对SIGUSR1信号的处理函数
void sig_usr(int signum){
float i;
//求出复制进程
i = (float)count/(float)file_size;
printf("current over : %0.0f%%\n",i*100);
}
//子进程对SIGUSR1信号的处理,即向父进程发送SIGUSR1信号
void sig_alarm(int signo){
kill(getppid(),SIGUSR1);
}
//主函数
int main(int argc ,char *argv[]){
pid_t pid;
int i;
int fd_src,fd_des;
char buf[128];
if(argc != 3){
printf("the format must be : command src_file des_file \n");
return -1;
}
//以只读方式打开源文件
if((fd_src = open(argv[1],O_RDONLY)) == -1){
perror("open fd_src");
exit(EXIT_FAILURE);
}
//获取源文件大小
file_size = lseek(fd_src,0,SEEK_END);
//重新设置读写位置为文件头
lseek(fd_src,0,SEEK_SET);
//以读写方式打开目标文件,如果不存在就创建
if((fd_des = open(argv[2],O_RDWR|O_CREAT,0644)) == -1){
perror("open fd_des");
exit(EXIT_FAILURE);
}
//进程创建失败
if((pid = fork() == -1)){
perror("fork");
exit(EXIT_FAILURE);
}
//父进程
else if(pid > 0){
//安装信号
signal(SIGUSR1,sig_usr);
do{
memset(buf,'\0',128);
//复制数据
if((i = read(fd_src,buf,1)) == -1){
perror("read");
exit(EXIT_FAILURE);
}
//如果复制完成,向子进程发送SIGINT信号,终止
else if(i == 0){
kill(pid,SIGINT);
break;
}
else{
//执行复制操作
if(write(fd_des,buf,i) == -1){
perror("write");
exit(EXIT_FAILURE);
}
count += i; //更新已经复制的文件的大小
}
}while(i != 0);
//等待子进程退出
wait(pid,NULL,0);
exit(EXIT_SUCCESS);
}
//子进程
else if(pid == 0){
usleep(1);
signal(SIGALRM,sig_alarm);
ualarm(1,1);
while(i){
;
}
exit(EXIT_SUCCESS);
}
return 0;
}