1. C/C++
  2. Linux

Linux网络编程之信号的处理 && Win10 Linux子系统

1.Linux子系统安装和配置

要想体验Win10 的linux子系统

  • 首先需要去Window功能里面勾选适用于Linux的Windows子系统

  • 去微软商店下载一个子系统即可

这里我下载的是Ubuntu 18.04 LTS 哈哈

就像安装软件一样,啥时Linux也搞一个windows子系统呢?

这时就会有一个小问题,这个linux的用户目录是否可以在windows文件管理器上找到?答案是肯定的!

C:\Users\Michael Jiang\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\rootfs\home\michael

如上所示,就是我的用户目录所在位置。

当然最好不要把写的代码放在这里面,容易丢失(如果你像我一样被重置电脑就能感受的这个痛点)

对于使用linux进行linux网络编程,其实你可以这样:

1.使用Windows文件管理器切换到代码所在目录

2.然后按住Shift同时点击鼠标右键可以看到

这样就可以进行编译,测试了,美滋滋!

2.Linux网络编程之信号量的处理

几乎所有的信号都将终止接收到该信号的进程。对于一些简单的程序,这完全能满足要求。用户按了中断或者退出键,就可以停止一个有问题的程序的运行。但是在大型的程序中,一些意料之外的信号会导致大问题。例如,正当在对一个重要的数据库进行修改期间,由于不小心碰到了中断键,而使程序被意外的终止,从而产生严重的后果。UNIX 的系统调用signal()用于接收一个指定类型的信号,并可以指定相应的方法。这就是说,signal()能够将指定的处理函数与信号向关联。它在Linux 系统库signal.h 中的函
数声明如下:
int signal (int sig, __sighandler_t handler);
Signal()有两个参数:
第一个参数sig 指明了所要处理的信号类型,它可以取除了SIGKILL 和SIGSTOP 外的任何一种信号。

第二个参数handler 描述了与信号关联的动作,它可以取以下三种值:

1. 一个返回值为整数的函数地址。
此函数必须在signal()被调用前声明,handler 中为这个函数的名字。当接收到一个类型为sig 的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
int func(int sig);
sig 是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig 的信号,
不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制
权返回进程被中断的那一点继续执行。

2.SIG_IGN
这个符号表示忽略信号。执行了相应的signal()调用好,进程会忽略类型为sig 的信号。

3.SIG_DFL
这个符号表示恢复系统对信号的默认处理。
函数如果执行成功,就返回信号在此次signal()调用之前的关联。
如果函数执行失败,就返回SIG_ERR。通常这种情况只有当sig 参数不是有效的信号
时才会发生。函数不对handler 的有效性进行检查。

在Linux 程序中常常利用SIG_IGN 和SIG_DFL 屏蔽SIGINT 和SIGQUIT 来保证执行重要任务的程序不会被意外的中止。

/**
 * 信号的处理
 * Michael Jiang
 * Wed Oct 30 23:31:39 CST 2019
 */

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

int main(int argc, char const *argv[])
{
    signal(SIGINT,SIG_IGN); //告诉进程将SIGINT信号忽略
    printf("aaaaaaaaaa\n");
    sleep(10);
    printf("bbbbbbbbbb\n");
    signal(SIGINT,SIG_DFL); //恢复对SIGINT信号的处理
    return 0;
}

执行结果

$ gcc signal_test.c && ./a.out
aaaaaaaaaa
^C
^C
^C
^C
^C
^C
^C
^C
bbbbbbbbbb

可以看出在程序打印完aaaaaaaaaa后我使用CTRL+C进行终止结果进程均未响应我的终止信号。

 

再看信号复位问题:

在Linux 中,当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。下面的程序演示了这一点:

/**
 * 信号的处理之捕获中捕获
 * Michael Jiang
 * Thu Oct 31 09:29:01 CST 2019
 */

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


int func(int sig);

int main(int argc, char const *argv[])
{
    signal(SIGINT,func); //告诉进程将SIGINT信号交给func函数处理

    printf("aaaaaaaaaa\n");
    sleep(10);
    printf("bbbbbbbbbb\n");

    return 0;
}

int func(int sig)
{
    printf("开始逮!sig=%d\n",sig);
    sleep(10);
    printf("逮到了!sig=%d\n",sig);
    return 0;
}

 

$ ./a.out
aaaaaaaaaa
^C开始逮!sig=2
^C
^C
^C
逮到了!sig=2
开始逮!sig=2
逮到了!sig=2
bbbbbbbbbb

分析:程序开始执行当按下第一个^C时进入处理信号函数,接着又连续按了多个^C等第一个^C的处理执行完成后程序返回执行第二个^C的处理函数然后程序就结束了???

对于第三个和第四个^C均未处理,可以得到在处理过程中只能接受一个排队信号!!!

 

最后介绍一下alarm()

1.系统调用alarm()
alarm()是一个简单而有用的系统调用,它可以建立一个进程的报警时钟,在时钟定时器到时的时候,用信号向程序报告。alarm()系统调用在Linux 系统函数库unistd.h 中的函数
声明如下:

unsigned int alarm(unsigned int seconds);
函数唯一的参数是seconds,其以秒为单位给出了定时器的时间。当时间到达的时候,
就向系统发送一个SIGARLM 信号。例如:
alarm(60);
这一调用实现在60 秒后发一个SIGALRM 信号。alarm 不会象sleep 那样暂停调用进程的执行,它能立即返回,并使进程继续执行,直至指定的延迟时间到达发出SIGALRM信号。事实上,一个由alarm()调用设置好的报警时钟,在通过exec()调用后,仍将继续有效。但是,它在fork()调用后中,在子进程中失效。如果要使设置的报警时钟失效,只需要调用参数为零的alarm():
alarm(0)

alarm()调用也不能积累。如果调用alarm 两次,则第二次调用就取代第一次调用。但是,alarm 的返回值柜橱了前一次设定的报警时钟的剩余时间。
当需要对某项工作设置时间限制时,可以使用alarm()调用来实现。其基本方法为:先调用alarm()按时间限制值设置报警时钟,然后进程作某一工作。如果进程在规定时间以内完成这一工作,就再调用alarm(0)使报警时钟失效。如果在规定时间内未能完成这一工作,进程就会被报警时钟的SIGALRM 信号中断,然后对它进行校正。

下面通过一个简单的限时答题程序来演示一下alarm的使用。

/**
 * 限时答题程序
 * Michael Jiang
 * Thu Oct 31 10:04:21 CST 2019
 */


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

#define BELL '\007'
#define TIMEOUT 5

int score = 0;  //得分

// alarm信号处理函数
int catch(int sig)
{
    putchar(BELL);
    printf("\n你答题超时了!\n得分:%d\n",score);
    exit(0);
}

int main(int argc, char const *argv[])
{
    printf("紧张刺激的限时答题程序\n你将回答五道加法题,每道题限时五秒!\n");

    // 先绑定一下信号处理方法
    signal(SIGALRM,catch);

    int cnt = 5;
    int ans = -1;
    int lop,rop;
    while (cnt--)
    {
        srand((int)time(NULL)); 
        lop = rand() % 10;

        rop = rand() % 10;

        printf("%d+%d=",lop,rop);
        alarm(TIMEOUT);
        scanf("%d",&ans);
        if(ans == lop + rop){
            score++;
        }
    }
    
    printf("恭喜完成所有题目,最终得分:%d\n",score);
    
    return 0;
}

当我们每道题都在5s内给出答案就不会触发alarm信号

$ ./a.out
紧张刺激的限时答题程序
你将回答五道加法题,每道题限时五秒!
2+6=8
0+2=2
4+9=13
5+3=8
2+8=10
恭喜完成所有题目,最终得分:5

但是如果某道题超时的话就会直接结束游戏

$ ./a.out
紧张刺激的限时答题程序
你将回答五道加法题,每道题限时五秒!
2+3=
你答题超时了!
得分:0