Linux进程通信:信号相关函数

1. kill函数

#include<sys/types.h>
#include<signal.h>

int kill(pid_t pid, int sig);
/*
功能:
    给进程pid发送信号sig
参数:
    pid:取值有4种情况:
        > 0:将sig信号发送给进程号为pid的进程;
        = 0:将sig信号发送给当前进程组中的所有进程;
        = -1:将sig信号发送给系统内的所有进程;
        < -1:将sig信号发送给进程组号为pid绝对值的进程组中的所有进程。
    sig:信号。推荐使用信号宏。
返回值:
    成功:0;
    失败:-1
*/

a)root权限用户可给任意用户发信号,普通用户不可给root权限用户发信号;

b)普通用户不允许:kill -9 root用户进程的pid; 普通用户也不允许向其他普通用户发送信号终止其进程,只能向自己创建的进程发送信号;普通用户只能给自己的进程发送信号。

kill函数示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>

int main(int argc, const char* argv[]) {

    pid_t pid = -1;
    int ret = -1;

    // 创建一个子进程
    pid = fork();
    if (-1 == pid) {
        perror("fork");
        return 1;
    }

    // 父进程杀死子进程
    if (pid > 0) {  // 父进程
        sleep(3);
        ret = kill(pid, SIGTERM);
        if (-1 == ret) {
            perror("kill");
            return 1;
        }
        printf("子进程被杀死!\n");
    } else {  // 子进程
        while (1) {
            printf("子进程运行中...\n");
            sleep(1);
        }
        exit(0);
    }

    return 0;
}

运行结果:


2. raise函数

#include<signal.h>

int raise(int sig);
/*
功能:
    当前进程给自己发送信号sig,相当于kill(getpid(), sig);
参数:
    sig:信号宏;
返回值:
    成功:0
    失败:非0
*/

其中,sig为信号的编号,见 Linux信号:信号 & 信号集 & 信号集函数 中的 “2. 信号的编号”。

raise函数示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>

int main(int argc, const char* argv[]) {

    int i = 4;

    while (1) {
        printf("进程%d自杀倒计时: %d\n", getpid(), i);
        sleep(1);
        if (0== --i) {
            printf("进程%d杀死自己.\n", getpid());
            raise(SIGTERM);
        }
    }

    return 0;
}

运行结果:


 3. abort函数

#include<stdlib.h>

void abort(void);
/*
功能:
    给自己发送异常终止信号,即6号信号SIGABTR,并产生core文件。
    等价于kill(getpid(), SIGABRT);
*/

abort函数示例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

int main(int argc, const char* argv[]) {

    int i = 3;

    while (1) {
        printf("终止倒计时:%d\n", i--);
        sleep(1);
        if (0 == i) {
            printf("进程终止!\n");
            abort();
        }
    }
    return 0;
}

运行结果:


4. alarm函数

定时器

#include<unistd.h>

unsigned int alarm(unsigned int seconds);
/*
功能:
    设置定时器:指定seconds后,内核给当前进程发送14号信号SIGALRM信号,
    进程收到该信号,默认终止进程。
    每个进程有且只有唯一的定时器。
参数:
    seconds:定时时间,单位s;
             为0时则表示取消定时器,并返回旧闹钟剩余秒数。
返回值:
    0或剩余秒数。
*/

注意:alarm函数与调用进程状态无关。进程处于任何状态(退出除外),alarm都会计时。

alarm函数示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(int argc, const char* argv[]) {

    unsigned int ret = 0;

    ret = alarm(5);
    sleep(3); // 进程的sleep状态不会影响闹钟的计时

    ret = alarm(4); // 覆盖之前的闹钟,重新计时

    /* 结果为2. 第一次计时5秒,sleep了3秒,
    执行到第二个闹钟时,第一次闹钟剩余2s*/
    printf("上次闹钟剩下的时间:%u\n", ret);
    getchar();
    return 0;
}

运行结果:

 前后总共计时7s


5. setitimer函数

多功能定时器

#include<sys/time.h>

int setitimer(int which, const struct itimerval* new_value, struct itimerval* old_value);
/*
功能:
    设置定时器。可替代alarm,精度μs。可实现周期定时。
参数:
    which:定时方式:
        a) 自然定时:ITIMER_REAL(14号信号,SIGALRM),计算自然时间,默认终止进程;
        b) 虚拟空间计时(用户空间):ITIMER_VIRTUAL(26号信号,SIGVTALRM),只计算占CPU时间;
        c) 运行时计时(用户+内核):ITIMER_PROF(27号信号,SIGPROF),计算占用CPU+系统调用时间;
    new_value:超时时间;
        itimerval.it_value:第一次执行function延迟的秒数;
        itimerval.it_interval:以后执行function的时间间隔
    old_value:存放旧的timeout值,一般为NULL
返回值:
    成功:0;
    失败:-1
*/

struct itimerval {
    struct timerval it_value;    // 闹钟触发时间
    struct timerval it_interval; // 闹钟触发周期
}

struct timeval {
    long tv_sec;   // 秒
    long tv_usec;  // 微秒
}

setitimer示例:

#include<stdio.h>
#include<sys/time.h>
#include<stdlib.h>
#include<string.h>

int main(int argc, const char* argv[]) {

    int ret = -1;

    struct itimerval tmo;

    // 第一次触发时间: 3s
    tmo.it_value.tv_sec = 3;
    tmo.it_value.tv_usec = 0;

    // 触发周期: 2s一次。 此处无效果,需结合后面的信号捕捉使用。
    tmo.it_interval.tv_sec = 2;
    tmo.it_interval.tv_usec = 0;

    // 设置定时器, 默认动作:终止进程
    ret = setitimer(ITIMER_REAL, &tmo, NULL);
    if (-1 == ret) {
        perror("setitimer");
        return 1;
    }

    printf("按下任意键继续\n");
    getchar();

    return 0;
}

运行结果:


6. signal函数

注册信号处理函数。

注:由于历史原因,signal函数在不同版本的类Unix系统中含义不同,应避免使用signal函数。

#include<signal.h>

typedef void(*sighandler_t)(int);
/*
函数指针。
名字:sighandler_t,返回值:void,形参:一个int类型;
*/

sighandler_t signal(int signum, sighandler_t handler);
/*
功能:
    注册信号处理函数:收到signum信号,则执行handler函数。
    不可用于SIGKILL、SIGSTOP信号。
参数:
    signum:信号编号,建议写信号的宏;
    handler:3中取值情况:
            SIG_IGN:忽略该信号;
            SIG_DFL:执行默认处理方式;
            回调函数名:自定义处理方式;
                    回调函数定义如下:
                        void func(int signo) {
                            // signo为触发的信号,为signal函数的第一个参数
                        }
返回值:
    成功:第一次返回NULL,下一次返回此信号上次注册的信号处理函数的地址;
        若需要使用此返回值,则必须在前面声明此函数的指针类型。
    失败:SIG_ERR
*/

signal示例1:

捕捉Ctrl+c、Ctrl+\信号,并自定义各自的处理方式。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>

void func0(int signum) {
    printf("捕捉到信号:%d\n", signum);
}

void func1(int signum) {
    printf("捕捉到信号:%d\n", signum);
}

int main(int argc, const char* argv[]) {

    /*注册信号处理函数。*/
    // SIGINT: Ctrl + c
    signal(SIGINT, func0); // 处理异步信号

    /*SIGQUIT: Ctrl + \*/
    signal(SIGQUIT, func1);

    while (1) {
        getchar();
    }

    return 0;
}

运行结果:

signal示例2:

捕捉setitimer信号,修改它默认终止进程的行为。

#include<stdio.h>
#include<sys/time.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>

void func(int signum) {
    printf("捕捉到SIGALRM信号.\n");
}

int main(int argc, const char* argv[]) {

    int ret = -1;

    struct itimerval tmo;

    // 第一次触发时间: 3s
    tmo.it_value.tv_sec = 3;
    tmo.it_value.tv_usec = 0;

    // 触发周期: 2s一次。 此处无效果,需结合后面的信号捕捉使用。
    tmo.it_interval.tv_sec = 2;
    tmo.it_interval.tv_usec = 0;

    // 捕捉信号SIGALRM,修改它的默认处行为。
    signal(SIGALRM, func);

    // 设置定时器, 默认动作:终止进程
    ret = setitimer(ITIMER_REAL, &tmo, NULL);
    if (-1 == ret) {
        perror("setitimer");
        return 1;
    }

    printf("按下任意键继续\n");
    getchar();

    return 0;
}

运行结果:


7.  sigaction函数

改变指定信号的处理方式,功能类似于signal。

#include<signal.h>

int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact);
/*
功能:
    修改或检查signum信号的处理方式。
参数:
    signum:要操作的信号;
    act:信号的新处理方式;传入参数;
    oldact:信号原先的处理方式;传出参数;
    
    若act非空,则将信号原处理方式变为act;若oldact非空,则将先前的处理方式存入oldact。
返回值:
    成功:0
    失败:-1
*/

struct sigaction {
    void(*sa_handler)(int);                      // 旧的信号处理函数指针
    void(*sa_sigaction)(int, siginfo_t*, void*); // 新的信号处理函数指针
    sigset_t sa_mask;         // 阻塞信号集
    int sa_flags;             // 信号处理方式
    void(*sa_restorer)(void); // 已弃用
}
/*
(1) sa_handler、sa_sigaction:信号处理函数指针,与signal函数中的函数指针用法一样,
                              根据情况给两者之一赋值。
    a) SIG_IGN:忽略该信号
    b) SIG_DFL:执行默认处理方式
    c) 回调函数名:自定义处理方式
(2) sa_mask:阻塞信号集。信号处理函数执行过程中,临时屏蔽指定信号。
(3) sa_flags:指定信号处理行为,通常设置为0(使用旧的信号处理函数). 也可是以下值的"按位或"组合
        SA_NOCLDSTOP:使父进程在其子进程暂停或继续运行时不会收到SIGCHLD;
        SA_NOCLDWAIT:使父进程在其子进程退出时不会收到SIGCHLD信号,此时子进程也不会成为僵尸进程;
        SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能收到该信号;
        SA_RESETHAND:信号处理之后重新设置为默认的处理方式;
        SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数;
        SA_RESTART:使被信号打断的系统调用自动重新发起(已废弃);
*/

void(*sa_sigaction)(int, siginfo_t*, void*);
/*
功能:
    新的信号处理函数指针,
参数:
    signum:信号编号;
    info:记录信号发送进程信息的结构体;
    context:赋给指向ucontext_t类型的一个对象指针,
             以引用在传递信号是被中断的接收进程或线程的上下文。
*/

sigaction示例1:

旧的信号处理函数:

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>

// 信号处理函数
void func(int signum) {
    printf("捕捉到信号%d.\n", signum);
}

int main(int argc, const char* argv[]) {

    int ret = -1;
    struct sigaction act;

    act.sa_handler = func;  // 自定义旧的信号处理函数
    act.sa_flags = 0;  // 使用旧的信号处理函数

    ret = sigaction(SIGINT, &act, NULL);
    if (-1 == ret) {
        perror("sigaction");
        return 1;
    }

    printf("按下任意键继续...\n");
    getchar();
    return 0;
}

运行结果:

sigaction示例2:

新的信号处理函数:

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>

// 信号处理函数
void func(int signum, siginfo_t* info, void* context) {
    printf("捕捉到信号%d.\n", signum);
}

int main(int argc, const char* argv[]) {

    int ret = -1;
    struct sigaction act;

    act.sa_sigaction = func;  // 自定义新的信号处理函数
    act.sa_flags = SA_SIGINFO;  // 使用新的信号处理函数

    ret = sigaction(SIGINT, &act, NULL);
    if (-1 == ret) {
        perror("sigaction");
        return 1;
    }

    printf("按下任意键继续...\n");
    getchar();
    return 0;
}

运行结果:


8. sigqueue函数

#include<signal.h>

int sigqueue(pid_t pid, int sig, const union sigval value);
/*
功能:
    给指定进程发送信号。
参数:
    pid:进程号;
    sig:信号;
    value:通过信号传递的参数。
        union sigval {
            union sigval {
                int sival_int;
                void* sival_ptr;
            }
        }
返回值:
    成功:0
    失败:-1
*/
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的马师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值