linux 进程信号传递参数,linux c信号(1)-基本概念

linux c信号(1)-基本概念

首页 计算机相关 linux系统编程 linux c信号(1)-基本概念

信号是事件发生时对进程的通知机制。有时也称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程,大多数情况下,无法预测信号到达的精确时间。

一个(具有合适权限的)进程能够向另一进程发送信号。信号的这一用法可作为一种同步技术,甚至是进程间通信( IPC )的原始形式。进程也可以向自身发送信一号。然而,发往进程的诸多信号,通常都是源于内核。引发内核为进程产生信号的各类事件如下。

1.硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。硬件异常的例子包括执行一条异常的机器语言指令,诸如,被 0 除,或者引用了无法访问的内存区域。

2.用户键入了能够产生信号的终端特殊字符。其中包括中断字符(通常是 Control-C )、暂停字符(通常是 Control-Z )。

3.发生了软件事件。例如,针对文件描述符的输出变为有效,调整了终端窗口大小,定时器到期,进程执行的 CPU 时间超限,或者该进程的某个子进程退出。

一般进程里为了防止重要的事情处理时被信号打断,一般用signal mask先屏蔽,此时收到的信号都会是pending状态,待后面取消屏蔽时候会触发。

/proc/PID/status 包含了各种位掩码字段,以16进制显示,最低位为信号1,相邻左边为信号2,以此类推。这些字段分别为名称描述

SigPnd线程等待信号

ShdPnd进程等待信号

SigBlk阻塞信号

SigIgn忽略信号

SigCgt捕获信号#defineSIGHUP1/* Hangup 重新读取配置文件 */

#defineSIGINT2/* Interrupt Control-c */

#defineSIGQUIT3/* Control-\ */

#defineSIGILL4/* Illegal instruction (ANSI). */

#defineSIGABRTSIGIOT/* produce a core dump for debugging */

#defineSIGTRAP5/* Trace trap (POSIX). */

#defineSIGIOT6/* IOT trap (4.2 BSD). */

#defineSIGEMT7/* EMT trap (4.2 BSD). */

#defineSIGFPE8/* Floating-point exception (ANSI). */

#defineSIGKILL9/* Kill, unblockable */

#defineSIGBUS10/* Bus error (4.2 BSD). */

#defineSIGSEGV11/* Segmentation violation (ANSI). */

#defineSIGSYS12/* Bad argument to system call (4.2 BSD). */

#defineSIGPIPE13/* Broken pipe (POSIX). */

#defineSIGALRM14/* 由 alarm() or setitimer() 生成 */

#defineSIGTERM15/* Termination kill的默认值 */

#defineSIGURG16/* Urgent condition on socket (4.2 BSD). */

#defineSIGSTOP17/* Stop, unblockable (POSIX). */

#defineSIGTSTP18/* job-control stop signal */

#defineSIGCONT19/* Continue (POSIX). */

#defineSIGCHLD20/* Child status has changed (POSIX). */

#defineSIGCLDSIGCHLD/* Same as SIGCHLD (System V). */

#defineSIGTTIN21/* Background read from tty (POSIX). */

#defineSIGTTOU22/* Background write to tty (POSIX). */

#defineSIGIO23/* I/O now possible (4.2 BSD). */

#defineSIGPOLLSIGIO/* Same as SIGIO? (SVID). */

#defineSIGXCPU24/* CPU limit exceeded (4.2 BSD). */

#defineSIGXFSZ25/* File size limit exceeded (4.2 BSD). */

#defineSIGVTALRM26/* Virtual alarm clock (4.2 BSD). */

#defineSIGPROF27/* Profiling alarm clock (4.2 BSD). */

#defineSIGWINCH28/* Window size change (4.3 BSD, Sun). */

#define SIGINFO29/* Information request (4.4 BSD). */

#defineSIGUSR130/* User-defined signal 1 (POSIX). */

#defineSIGUSR231/* User-defined signal 2 (POSIX). */

#define SIGLOST32/* Resource lost (Sun); server died (GNU). */

改变信号处置:signal()

signal() 为比较老的处理函数,可移植性不如sigaction(),功能也没它强大。sigaction()是首选的信号处理。不过signal()简单。#include

void ( *signal(int sig, void (*handler)(int)) ) (int);

//成功返回之前的处理函数指针,失败返回SIG_ERR

//函数原型也可以这样写

typedef void (*sighandler_t)(int);

sighandler_t signal(int sig, sighandler_t handler);

如下代码暂时建立信号处置,然后还原,signal() 做不到 获取到当前的信号处置而不改变信号处置,sigaction() 则能做到。void (*oldHandler)(int);

oldHandler = signal(SIGINT, newHandler);

if (oldHandler == SIG_ERR)

perror("signal");

//do something

if (signal(SIGINT, oldHandler) == SIG_ERR)

perror("signal");

handler 也可以指定为如下值,所以调用signal() 也有可能返回如下其中一个值。

SIG_DEL 将信号重置为默认值。

SIG_IGN 忽略信号。if (signal(SIGINT, SIG_DFL) == SIG_ERR) perror("signal");

if (signal(SIGINT, SIG_IGN) == SIG_ERR) perror("signal");

信号处理简介

调用信号处理器程序,可能会随时打断主程序流程。内核代表进程来调用处理器程序,当处理器返回时,主程序会在处理器打断的位置恢复执行。

9fd380c05c08b8daa68c47b215af1d12.png#include

#include /* Type definitions used by many programs */

#include /* Standard I/O functions */

#include /* Prototypes of commonly used library functions,

plus EXIT_SUCCESS and EXIT_FAILURE constants */

#include /* Prototypes for many system calls */

#include /* Declares errno and defines error constants */

#include /* Commonly used string-handling functions */

static void

sigHandler(int sig)

{

printf("hello freecls!\n");

}

int

main(int argc, char *argv[])

{

int j;

if (signal(SIGINT, sigHandler) == SIG_ERR)

return 1;

for (j = 0; ; j++) {

printf("%d\n", j);

sleep(3); /* Loop slowly... */

}

}

该程序执行时,当你按下ctrl+c时,不会终止程序(因为默认行为被改变了)而会输出 helo freecls 然后继续执行。最后按下 ctrl+\终止。

发送信号:kill()

与shell 的kill 命令类似,一个进程可以向另一个进程发送信号(必须要有权限)。#include

int kill(pid_t pid, int sig);

//成功返回0, 失败-1

//pid > 0发送为指定进程

//pid = 0发送给当前进程组里所有进程包括自己

//pid < -1 发送给当前进程组里的进程组id = pid\

//的绝对值 = killpg(pid_t pgrp, int sig);

//pid = -1发送给所有进程(必须有权限)除了init和自己

//sig = 0为空信号用来检测进程是否存在,不可靠因为进程id可以重复使用,或是僵尸进程

特权级进程可以向任何进程发送信号,非特权级必须满足如下图条件。

8fb986b22613136281b44caf1e731385.png

向自己发送信号:raise()#include

int raise(int sig);

//成功返回0,失败返回非0

//单线程程序相当于

kill(getpid(), sig);

//多线程相当于

pthread_kill(pthread_self(), sig);

向进程组发送信号#include

int killpg(pid_t pgrp, int sig);

//成功返回0,失败-1

//相当于

kill(-pgrp, sig);

如果pgrp=0,则向进程组所有进程发送信号。

信号描述

每个信号都有一段与之相对应的描述,可以用sys_siglist[sig] 来获取,但是推荐用strsignal() 函数,因为它支持locale。#define _BSD_SOURCE

#include

extern const char *const sys_siglist[];

#define _GNU_SOURCE

#include

char *strsignal(int sig);

直接打印到错误输出#include

void psignal(int sig, const char *msg);#include

#include

#include

extern const char *const sys_siglist[];

char *strsignal(int sig);

int main(){

char *tmp = strsignal(SIGINT);

printf("%s\n", tmp);

psignal(SIGINT,"sigerr");

}[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out

Interrupt

sigerr: Interrupt

信号集

许多信号相关的系统调用都需要能表示一组不同的信号。多个信号可使用一个称之为信号集的数据结构来表示。数据类型为 sigset_t。

sigemptyset() 初始化一个未包含任何成员的信号集。sigfillset() 初始化一个信号集,包含所有信号(包括实时信号)。#include

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

//成功返回0,失败-1

必须使用上述2个函数初始化信号集,因为C语言不会对自动变量进行初始化。

添加或移除单个信号#include

int sigaddset(sigset_t *set, int sig);

int sigdelset(sigset_t *set, int sig);

//成功返回0,失败-1

测试信号 sig 是否为 set的成员#include

int sigismember(const sigset_t *set, int sig);

//1表示是,0不是

另外3个非标准函数#define _GNU_SOURCE

#include

//left与right交集

int sigandset(sigset_t *set, sigset_t *left, sigset_t *right);

//left与right并集

int sigorset(sigset_t *dest, sigset_t *left, sigset_t *right);

//成功返回0,失败-1

//判断是否为空,是返回1,不是0

int sigisemptyset(const sigset_t *set);

信号掩码(阻塞信号传递)

也可叫信号遮罩,信号屏蔽。内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其针对该进程传递。在多线程环境中,每个线程都可以使用pthread_sigmask()函数来独立检查和修改其信号。#include

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

//Returns 0 on success, or –1 on error

sigprocmask() 既可以修改信号掩码,又可获取现有的掩码,或两者兼具。

how参数指定了该函数信号掩码带来的变化:

SIG_BLOCK 将set 指向信号集并集到当前信号掩码中。

SIG_UNBLOCK 将set指向的信号集中的信号从信号掩码中移除,就算信号掩码中没有要解除的信号也不报错。

SIG_SETMASK 将set指向的信号集设置为信号掩码。

如果 oldset 参数不为NULL,则其指向sigset_t 结构缓冲区,用于返回之前的信号掩码。

如果只是想获取信号掩码, set 参数设为NULL即可,这时将忽略how 参数。

如果解除了对某个等待信号的阻塞,会立刻将该信号传递给进程。

下面代码时暂时阻止 SIGINT 信号的传递。sigset_t blockSet, prevMask;

sigemptyset(&blockSet);

sigaddset(&blockSet, SIGINT);

if (sigprocmask(SIG_BLOCK, &blockSet, &prevMask) == -1)

perror("sigprocmask1");

if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)

perror("sigprocmask2");

SIGKILL 和 SIGSTOP 信号是不允许阻塞的,也就是下面的代码会阻塞除这2个信号以外的任何信号。sigfillset(&blockSet);

if (sigprocmask(SIG_BLOCK, &blockSet, NULL) == -1)

perror("sigprocmask");

处于等待中的信号

如果某进程接收了一个正在阻塞的信号,那么会将该信号添加到进程的等待信号集中。之后如果解除了对该信号的阻塞,就会把该信号传递给此进程(就算在阻塞期间发生了N次,解除时只会传递1次,而实时信号可以排队)。

sigpending() 系统调用返回进程处于等待状态的信号集,并存入set 指向的sigset_t 结构中。#include

int sigpending(sigset_t *set);

//Returns 0 on success, or –1 on error

改变信号处置:sigaction()

除signal() 之外,sigaction() 系统调用是设置信号处置的另一选择。用法复杂一点,但是功能强大,且可移植性强。#include

struct sigaction {

void (*sa_handler)(int);

sigset_t sa_mask;

int sa_flags; /* Flags controlling handler invocation */

void (*sa_restorer)(void); /* Not for application use */

};

int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);

//Returns 0 on success, or –1 on error

sig为想要获取或改变的信号编号,除去 SIGKILL SIGSTOP。act是指向信号新处置的数据结构,oldact用来返回之前的信号处置结构,都可以设置为 NULL。

sigaction 结构实际更为复杂,下面文章再讲。sa_handler 对应于 signal()的handler。其所对应的值为信号处理器函数或是SIG_IGN、SIG_DFL。仅当sa_handler 为函数地址时,才会对sa_mask 和 sa_flags 加以处理。

sa_mask 定义了一组信号,在调用sa_handler 所定义的处理程序时阻塞,也就是在调用处理器程序之前,将这些信号添加到信号掩码中。处理器函数返回时自动删除。利用这一特性可以指定哪些信号不允许打断处理器的执行。此外,引发此处理器程序的信号也将自动添加到信号掩码中。这就保证了同一个信号抵达多次不会递归中断自己。

sa_flags字段是一个位掩码,可以是如下的选项相或(|):

SA_NOCLDSTOP 若 sig 为SIGCHLD 信号,则当因接受一信号而停止或恢复某一子进程时,将不产生此信号。

SA_NOCLDWAIT 若 sig 为SIGCHLD 信号,则当子进程终止时不会将其转化为僵尸进程。

SA_NODEFER 捕获信号时,不会在执行处理器程序时自动将该信号添加到进程掩码中。别名 SA_NOMASK。

SA_ONSTACK 针对此信号调用处理器函数时,使用了由 sigaltstack() 安装的备选栈。

SA_RESETHAND 当捕获该信号时,会在调用处理函数之前将信号处置重置为默认值(SIG_DFL)。

SA_RESTART 自动重启由信号处理器程序中断的系统调用。

SA_SIGINFO 调用信号处理器程序时携带额外的参数。

等待信号:pause()#include

int pause(void);

//Always returns –1 with errno set to EINTR

暂停进程的执行,直至信号处理器函数中断该调用为止(或者直至一个未处理信号终止进程为止)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值