进程间通信--信号

信号

信号共性:简单、不能携带大量信息、满足条件才发送。

信号的特质:信号是软件层面上的“中断”。一旦信号产生,无论程序执行到什么位置,必须立即停止运行,处理信号,处理结束,再继续执行后续指令。所有信号的产生及处理全部都是由【内核】完成的。

产生信号:
1. 按键产生:ctrl+c ctrl+z
2. 调用函数:kill raise abort
3. 定时器:alarm setitimer
4. 命令产生:kill
5. 硬件异常产生:段错误,浮点型错误,总线错误,SIGPIPE
状态:
未决:产生与递达之间状态。
递达:产生并且送达到进程。直接被内核处理掉。
信号处理方式: 执行默认处理动作、忽略、捕捉(自定义)
阻塞信号集(信号屏蔽字): 用来记录信号的屏蔽状态。一旦被屏蔽的信号,在解除屏蔽前,一直处于未决态。
未决信号集:本质:位图。用来记录信号的处理状态。该信号集中的信号,表示,已经产生,但尚未被处理。

信号4要素:编号、名称、对应事件、默认处理动作(忽略,终止,终止core,暂停,继续)。

kill命令 和 kill函数

int kill(pid_t pid, int signum)
参数:
pid: > 0:发送信号给指定进程
= 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。
= -1:发送信号给,有权限发送的所有进程。
signum:待发送的信号,kill -l查看。
返回值:
成功: 0
失败: -1 errno

//01_kill_parent_process.c

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

int main(int argc, char const *argv[])
{
    int i = 0;
    for (; i < 5; ++i)
    {
        pid_t pid = fork();
        if (pid == 0)
            break;
    }

    if (i == 2)
    {
        printf("child process, kill parent process after 5s.\n");
        sleep(5);
        kill(getppid(), SIGKILL);
        while (1)
        {
            sleep(1);
        }
    }
    else if (i == 5)
    {
        while (1)
        {
            printf("parent process.\n");
            sleep(1);
        }
    }
    return 0;
}
//02_kill_child3_process.c

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

int main(int argc, char const *argv[])
{
    int i = 0;
    pid_t pid = 0, pid_child_3 = 0;
    for (; i < 5; ++i)
    {
        pid = fork();
        if (pid == 0)
            break;
        else if (pid > 0)
        {
            if (i == 2)
            {
                pid_child_3 = pid;
            }
        }
    }

    if (i == 2)
    {
        while (1)
        {
            printf("child 3, pid = %d.\n", getpid());
            sleep(3);
        }
    }
    if (i == 5)
    {
        printf("parent process, kill child 3 after 5s.\n");
        sleep(5);
        kill(pid_child_3, SIGKILL);
    }
    return 0;
}
//03_raise.c

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

int main(int argc, char const *argv[])
{
    printf("kill myself after 2s.\n");
    sleep(2);
    raise(SIGKILL);     //kill(getpid(), SIGKILL)'
    //abort();
    return 0;
}

alarm

alarm 函数:使用自然计时法,定时发送SIGALRM给当前进程。
unsigned int alarm(unsigned int seconds);
参数:
seconds:定时秒数
返回值:上次定时剩余时间。无错误现象。

alarm(0); 取消闹钟。
time 命令 : 查看程序执行时间。 实际时间 = 用户时间 + 内核时间 + 等待时间。 --》 优化瓶颈 IO

//04_alarm.c

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

int main(int argc, char const *argv[])
{
    int ret = alarm(10);
    int i = 0;
    while (1)
    {
        printf("%d second\n", ++i);
        sleep(1);
    }

    return 0;
}

setitimer

setitimer函数:
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数:
which: ITIMER_REAL: 采用自然计时。 ——> SIGALRM
ITIMER_VIRTUAL: 采用用户空间计时 —> SIGVTALRM
ITIMER_PROF: 采用内核+用户空间计时 —> SIGPROF
new_value:定时秒数
old_value:传出参数,上次定时剩余时间。
返回值:
成功: 0
失败: -1 errno

struct itimerval {
    struct timeval {
        time_t      tv_sec;         /* seconds */
        suseconds_t tv_usec;        /* microseconds */
    }it_interval;//---> 周期定时秒数

     struct timeval {
        time_t      tv_sec;         
        suseconds_t tv_usec;        
    }it_value;  //---> 第一次定时秒数  
};

e.g.
            struct itimerval new_t; 
            struct itimerval old_t; 

            new_t.it_interval.tv_sec = 0;
            new_t.it_interval.tv_usec = 0;
            new_t.it_value.tv_sec = 1;
            new_t.it_value.tv_usec = 0;

            int ret = setitimer(&new_t, &old_t);  定时1
//05_setitimer.c

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    struct itimerval myit = { { 0, 0 }, { 3, 0 } };     //定义3s以后发送SIGALRM信号
    setitimer(ITIMER_REAL, &myit, NULL);
    while (1)
    {
        printf("Who whill kill me?\n");
        sleep(1);
    }

    return 0;
}
//06_setitimer_cycle.c

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>

void catch_sig(int num)
{
    printf("cat %d sig\n", num);
}

int main(int argc, char const *argv[])
{
    signal(SIGALRM, catch_sig);

    struct itimerval myit = { { 3, 0 }, { 5, 0 } };     //5s后,每隔3s发送1次SIGALRM信号
    setitimer(ITIMER_REAL, &myit, NULL);
    while (1)
    {
        printf("Who whill kill me?\n");
        sleep(1);
    }

    return 0;
}
//07_myalarm.c

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

unsigned int myalarm(unsigned int seconds)
{
    struct itimerval oldit, myit = { { 0, 0 }, { 0, 0 } };
    myit.it_value.tv_sec = seconds;
    setitimer(ITIMER_REAL, &myit, &oldit);        //seconds后发送SIGALARM信号
    printf("tv_sec = %d, tv_mirsec = %d\n", oldit.it_value.tv_sec, oldit.it_value.tv_usec);
    return oldit.it_value.tv_sec;
}

int main(int argc, char const *argv[])
{
    int ret = -1;
    ret = myalarm(5);
    printf("ret = %d\n", ret);
    //sleep(3);
    ret = myalarm(3);
    printf("ret = %d\n", ret);
    while (1)
    {
        printf("came on baby!\n");
        sleep(1);
    }

    return 0;
}

信号集操作函数:

sigset_t set;  自定义信号集。
sigemptyset(sigset_t *set); 清空信号集
sigfillset(sigset_t *set);  全部置1
sigaddset(sigset_t *set, int signum);   将一个信号添加到集合中
sigdelset(sigset_t *set, int signum);   将一个信号从集合中移除
sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。 在--》1, 不在--》0
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 设置信号屏蔽字和解除屏蔽
参数:
    how:SIG_BLOCK:  设置阻塞
        SIG_UNBLOCK:    取消阻塞
        SIG_SETMASK:    用自定义set替换mask。
    set:    自定义set
    oldset:旧有的 mask。
    
int sigpending(sigset_t *set);  查看未决信号集:
    set: 传出的 未决信号集。
//08_sigpending.c

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

int main(int argc, char const *argv[])
{
    sigset_t pend, sigproc;
    //设置阻塞信号,等待按键产生信号
    sigemptyset(&sigproc);             //先清空
    sigaddset(&sigproc, SIGTSTP);

    //设置阻塞信号集
    sigprocmask(SIG_BLOCK, &sigproc, NULL);

    //循环取未决信号
    while(1)
    {
        sigpending(&pend);
        int i = 1;
        for (; i < 32; ++i)
        {
            if (sigismember(&pend, i) == 1)
            {
                printf("1");
            }
            else
            {
                printf("0");
            }
        }
        printf("\n");
        sleep(1);
    }
    
    return 0;
}

信号捕捉

sighandler_t signal(int signum, sighandler_t handler);
参数:
signum :待捕捉信号
handler:捕捉信号后的操纵函数

信号捕捉特性:
1. 捕捉函数执行期间,信号屏蔽字 由 mask --> sa_mask , 捕捉函数执行结束。 恢复回mask
2. 捕捉函数执行期间,本信号自动被屏蔽(sa_flgs = 0).
3. 捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次!

//09_sigaction_mask.c

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>

void catch_sig(int num)
{
    printf("begin call, catch %d sig\n", num);
    sleep(5);           //模拟捕捉函数执行时间较长
    printf("end call, catch %d sig\n", num);
}

int main(int argc, char const *argv[])
{
    //注册以下捕捉函数
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = catch_sig;
    sigemptyset(&act.sa_mask);

    //注册捕捉
    sigaction(SIGINT, &act, NULL);

    //setitimer
    while (1)
    {
        printf("who can kill me?\n");
        sleep(1);
    }

    return 0;
}
//10_sigaction_mask.c

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>

void catch_sig(int num)
{
    printf("begin call, catch %d sig\n", num);
    sleep(5);           //模拟捕捉函数执行时间较长
    printf("begin call, catch %d sig\n", num);
}

int main(int argc, char const *argv[])
{
    //注册以下捕捉函数
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = catch_sig;
    sigemptyset(&act.sa_mask);

    //注册捕捉
    sigaction(SIGINT, &act, NULL);

    //setitimer
    while (1)
    {
        printf("who can kill me?\n");
        sleep(1);
    }
    
    return 0;
}
//11_child_catch.c

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>

void catch_sig()
{
    pid_t wpid;
    while ( (wpid = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("wait child %d ok\n", wpid);
    }
}

int main(int argc, char const *argv[])
{
    int i = 0;
    pid_t pid;

    //在创建子进行横之前屏蔽SIGCHLD信号
    sigset_t myset, oldset;
    sigemptyset(&myset);
    sigaddset(&myset, SIGCHLD);
    //oldset保留现场,设置了SIGCHLD到阻塞信号集
    sigprocmask(SIG_BLOCK, &myset, &oldset);

    for (; i < 10; ++i)
    {
        pid = fork();
        if (pid == 0)
            break;
    }

    if (i == 10)
    {
        //parent
        sleep(2);       //模拟注册晚于子进程死亡
        struct sigaction act;
        act.sa_flags = 0;
        sigemptyset(&act.sa_mask);
        act.sa_handler = catch_sig;
        //捕捉
        sigaction(SIGCHLD, &act, NULL);
        //解除屏蔽现场
        sigprocmask(SIG_SETMASK, &oldset, NULL);
        while (1)
        {
            sleep(1);
        }
    }
    else if (i < 10)
    {
        //child
        printf("I am %d child, pid = %d\n", i, getpid());
        //sleep(10 - i);
    }

    return 0;
}
//12_sig_count.c

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

pid_t pid;
int count = 0;
int flag = 0;

void cat_sig_father()
{
    sleep(1);
    flag = 1;
    printf("%d\n", count);
    count += 2;
}

void cat_sig_child()
{
    sleep(1);
    flag = 1;
    printf("%d\n", count);
    count += 2;
}

int main(int argc, char const *argv[])
{
    pid = fork();
    if (pid < 0)
    {
        printf("fork error!\n");
        return;
    } 
    else if(pid == 0)
    {
        //child
        count = 1;
        signal(SIGUSR2, cat_sig_child);
        pid_t ppid = getppid();
        while (1)
        {
            if (flag == 1)
            {
                kill(ppid, SIGUSR1);
                flag = 0;
            }
        }
    }
    else 
    {
        sleep(0.01);
        count = 2;
        signal(SIGUSR1, cat_sig_father);
        kill(pid, SIGUSR2);
        while (1)
        {
            if (flag == 1)
            {
                kill(pid, SIGUSR2);
                flag = 0;
            }
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值