C语言:高级并发操作(信号)

引言

同步和异步的使用。

异步事件处理的两种方法:查询法、通知法。(单核机器不存在异步)

一、信号

1. 信号的概念

信号是软件中断。信号的响应依赖于中断。中断是底层硬件的机制。

2. signal函数

kill -l命令查看所有的信号。1 -31 属于标准信号 34 - 64属于实时信号


       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
展示形式为: void (* signal(int signum, void (*func)(int) )    ) (int )

信号会打断阻塞的系统调用。

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

void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(SIGINT,SIG_IGN);//运行过程中忽略这个信号
    signal(SIGINT,mySig);
    
    int i;
    for(i = 0;i<10;i++)
    {
        write(1,"*",1);
        sleep(1);
    }
    return 0;
}

重构之前的mycpy.c  对于open  write read 函数增加出现系统调用打断的行为判断

 mycpy.c

#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define BUF_SIZE    1024*100
int main(int argc,char **argv)
{

    int fps,fpd;
    int ret;
    char buf[BUF_SIZE];
    int len,pos;
    if(argc <3)
    {
        fprintf(stderr,"Usage:%s <src_file> <dest_file>\n",argv[0]);
        exit(1);
    }
    do
    {
        fps = open(argv[1],O_RDONLY);
        if(fps <0)
        {
            if(errno != EINTR)
            {
                perror("open");
                exit(1);
            }
        }
    }while(fps<0);
    do
    {
        fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600 );
        if(fpd <0)
        {
            if(errno != EINTR)
            {
                close(fps);
                perror("open");
                exit(1);
            }
        }
    }while(fpd<0);
    while(1)
    {
        len = read(fps,buf,BUF_SIZE);
        if(len < 0)
        {
            if(errno == EINTR)
                continue;
            perror("read()");
            break;
        }
        if(len ==0)
            break;

        pos = 0;
        while(len > 0)
        {
            ret = write(fpd,buf+pos,len);
            if(ret <0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len-=ret;
        }
    
    }

    close(fpd);
    close(fps);

    return 0;
}

3. 信号的不可靠

标准的信号会丢失,实时信号不会丢失。指的是信号的行为不可靠。(第一次调用还未结束,第二次调用就开始了)

4. 可重入函数

第一次调用还未结束,第二次调用不会出错叫可重入函数(reentrant)。所有的系统调用都是可重入的,一部分库函数也是可重入的,比如说:memcpy rand_r localtime_r   memove

5. 信号的响应过程

mask(信号屏蔽字,32位一般情况都是1)pending(32位 位图,初始都为0),收到信号后pending位置1,内核在kernal态进入到user态时,会用mask值 & pending 获取发生的信号,将mask与pending全部置为0,执行函数,执行完毕将mask位置1。

信号从收到到响应有一个不可避免的延迟。
思考:
如何忽略掉一个信号?  将mask位置0
标准信号为什么会丢失?  在执行信号时,pending位置多次1,此时mask位是0
标准信号的响应没有严格的顺序。
不能从信号处理函数中随意的往外跳。(sigsetjmp,siglongjmp)

6. 相关常用函数

kill

发送一个信号到一个进程或进程组

raise

给当前进程或者线程发送一个信号

alarm

指定秒数,发送SIGALRM信号。默认alarm会杀掉当前程序。alarm不能实现多任务的计时器,只能执行最后一次设置的计时器。

alarm.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void mySig()
{

    write(1,".",1);
}

int main()
{
    alarm(5);
    alarm(2);
    alarm(10);
    while(1)
        pause();
#if 0
   signal(SIGALRM,mySig);
    
    int i;
    for(i = 0;i<10;i++)
    {
        write(1,"*",1);
        alarm(1);
        sleep(1);
    }
#endif 
    return 0;
}

5秒进行计数,不用alarm实现

5sec.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>


int main()
{
    time_t end;
    long long  count = 0;
   end = time(NULL)+5;
    while(time(NULL)<= end)
    {
        count++;
    }
    printf("count = %lld \n",count);
    return 0;
}

 使用alarm实现

5sec_signal.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>

static int loop = 1;

void alrm_handler(int num)
{
    loop = 0;
}

int main()
{

    signal(SIGALRM,alrm_handler);
    alarm(5);

    long long  count=0;
    while(loop)
    {
        count++;
    }
    printf("count = %lld \n",count);
    return 0;
}

gcc -S 5sec_signal.c 会产生 5sec_singal.s的汇编文件  

使用-O1优化之后,程序会一直执行下去,因为程序中while中没有用到loop,优化后loop的值会直接从寄存器取程序while会一直成立,volatile关键字告诉程序要去真正的地址取数据,不要从内存获取

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

static volatile int loop = 1;  //从真实的空间去取数据

void alrm_handler(int num)
{
    loop = 0;
}

int main()
{

    signal(SIGALRM,alrm_handler);
    alarm(5);

    long long  count=0;
    while(loop)
    {
        count++;
    }
    printf("
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

only-lucky

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

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

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

打赏作者

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

抵扣说明:

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

余额充值