我是靠谱客的博主 犹豫果汁,最近开发中收集的这篇文章主要介绍Linux - 信号No.8 练习,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 常用信号链接
    • No.1 信号
    • No.2 信号的处理过程
    • No.3 高级信号处理:sigaction
    • No.4 简单信号发送:kill
    • No.5 可以多个信号使用一个函数
    • No.6 定时信号:SIGALRM
      • alarm函数 只能触发一次
      • setitimer 函数,可以多次触发
    • No.7 信号屏蔽
  • No.8 练习

常用信号链接

=========================================

No.1 信号

  • 本质是一个整数

  • 用户模式下 信号是用来模拟硬件中断的

  • 硬件中断:物理层面的中断

  • 软件中断:模拟硬件中断

  • 信号的产生:

      信号发送:
      内核		硬件		进程
      类似:
      qt: 信号与槽
      	中断:设置一个提示,来信号了提示一下
      windows:消息
      	轮询:一直等待消息,直到信息来了
    

No.2 信号的处理过程

	进程A正在运行
	进程B发送了一个SIGINT信号
	进程A收到 SIGINT 信号,默认处理(结束进程)
#include <signal.h>

typedef void (*sighandler_t)(int); //声明void的类型并且参数是int的函数指针类型	sighandler_t	

//signum为信号类型	handler为sighandler_t类型的一个指针
sighandler_t signal(int signum,sighandler_t handler);
  • Ctrl + c 2 SIGINT

  • Ctrl + 9 SIGQUIT

      LINUX提供的信号:64个
      		+   不可靠信号(非实时)		unix提供的			1-31
      		+   可靠信号(实时)			后来扩充				32-64
      		+	标准:操作系统提供
      		+	自定义:用户自定义
    
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void testSignal(int sign)
{
    printf("受到了一个%d信号!n",sign);

}
int main()
{
    signal(SIGINT,testSignal);
    int n=0;
    while(1)
    {
        printf("<< 你好啊啊啊啊~t%dn",n++);
        sleep(2);
    }
    return 0;
}

No.3 高级信号处理:sigaction

不仅仅可以接受信号做信号处理,还能接收信号的同时接受一些信息

int sigaction(
		int signum,
		const struct sigaction *act,
        struct sigaction *oldact);
struct sigaction {
        void (*sa_handler)(int);		//信号处理函数 signal
        void (*sa_sigaction)(int, siginfo_t *, void *);			//
        sigset_t sa_mask;				//信号集,用于信号屏蔽
        int sa_flags;							//方式	SA_SIGINFO 使用第二个函数的时候要给赋值
        void (*sa_restorer)(void);	//是否保存
       };
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void hander(int sign)
{
    printf("基本信息处理函数 sig: %d信号!n",sign);

}
void handAct(int n,siginfo_t* siginfo,void* arg)
{
    printf("高级信息处理函数!n");
    printf("n:%d,msg:%dn",n,siginfo->si_int);
}
int main()
{
    struct sigaction act = {0};
    struct sigaction oldAct = {0};
    act.sa_handler = hander;
    act.sa_sigaction = handAct;
    act.sa_flags = SA_SIGINFO;



    sigaction(SIGINT,&act,&oldAct);

    printf("pid:%dn",getpid());

    int n=0;
    while(1)
    {
        printf("<< 你好啊啊啊啊~t%dn",n++);
        sleep(2);
    }
    return 0;
}

在这里插入图片描述

No.4 简单信号发送:kill

可以在另外一个终端上使用kill命令发送信号kill -s 信号 进程id同时也可以使用kill命令进行信号的发送
在这里插入图片描述

#include <sys/types.h>
#include <signal.h>
       
int kill(pid_t pid, int sig);

示例代码

```cpp

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

int main(int argc,char* argv[])
{
int pid = atoi(argv[2]);
int sigNum = atoi(argv[1]);
kill(pid,sigNum);
printf(“<< 发送%d信号给%d进程!n”,sigNum,pid);
return 0;
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/b08d8c064af14120b6dd1dd44e42c8cc.png)
## No.5 高级信号发送:sigqueue
发送的时候可以带点信息
	

```cpp
 #include <signal.h>

int sigqueue(
	pid_t pid, 		//要发送的进程id
	int sig, 		//信号
	const union sigval value//一个联合);
union sigval {
     int   sival_int;	//发送黑进程的信息
     void *sival_ptr;};	//一个地址


示例:

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

int main(int argc,char* argv[])//sigNum pid sival_int
{
    int pid = atoi(argv[2]);
    int sigNum = atoi(argv[1]);

    union sigval uVal={0};
    uVal.sival_int = atoi(argv[3]);

    sigqueue(pid,sigNum,uVal);
    
    //kill(pid,sigNum);
    printf("<< 发送%d信号给%d进程! --附带信息%dn",
            sigNum,pid,uVal.sival_int);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

No.5 可以多个信号使用一个函数

修改终端大小时触发的信号:SIGWINCH

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void testSignal(int sign)
{
    switch (sign)
    {
    case 2:
        printf("受到了一个%d信号!n",sign);
        break;
    case 28:
        printf("窗口改变了 %d!n",sign);
        break;
    default:
        printf("%d信号!n",sign);
        break;
    }

}
int main()
{
    signal(99,testSignal);
    signal(SIGWINCH,testSignal);
    signal(SIGINT,testSignal);
    int n=0;
    printf("PID:%dn",getpid());
    while(1)
    {
        printf("<< 你好啊啊啊啊~t%dn",n++);
        sleep(2);
    }
    return 0;
}

通SIGINT信号使用方法都相同,就是终端大小改变的时候触发

No.6 定时信号:SIGALRM

alarm函数 只能触发一次

setitimer 函数,可以多次触发

int setitimer(
	int which,
	const struct itimerval *new_value,
    struct itimerval *old_value);
struct itimerval {
    struct timeval it_interval; /* 周期性计时器的间隔 */
    struct timeval it_value;    /* 下次到期前的时间 */};

struct timeval {
    time_t      tv_sec;         /* 秒*/
    suseconds_t tv_usec;        /* 微妙*/ };

setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。

which:间歇计时器类型,有三种选择

ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。

返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EFAULT:value或ovalue是不有效的指针
EINVAL:其值不是ITIMER_REAL,ITIMER_VIRTUAL 或 ITIMER_PROF之一

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
void testSignal(int sign)
{
    switch (sign)
    {
    case 2:
        printf("受到了一个%d信号!n",sign);
        break;
    case 28:
        printf("窗口改变了 %d!n",sign);
        break;
    case 14:
        printf("信号%d来了!n",sign);
        break;
    default:
        printf("%d信号!n",sign);
        break;
    }

}
int main()
{
    signal(14,testSignal);
    signal(SIGWINCH,testSignal);
    signal(SIGINT,testSignal);
    int n=0;
    struct itimerval tv = {0};
    tv.it_value.tv_sec = 3;//第一次发送间隔时间
    tv.it_interval.tv_sec = 5;//间隔时间

    setitimer(ITIMER_REAL,&tv,NULL);
    //alarm(3);
    printf("PID:%dn",getpid());
    while(1)
    {
        printf("<< 你好啊啊啊啊~t%dn",n++);
        sleep(1);
    }
    return 0;
}

No.7 信号屏蔽

  • 信号集:多个信号的集合 sigset_t类型
  • 有一系列的函数
#include <signal.h>
int sigemptyset(sigset_t *set);//清空这个信号集合
int sigfillset(sigset_t *set);//填充
int sigaddset(sigset_t *set, int signum);//添加
int sigdelset(sigset_t *set, int signum);//删除
int sigismember(const sigset_t *set, int signum);//判断某一个信号是不是信号集里面的
  • 在os 内核中有一个信号缓冲区,维护一个信号队列,如果产生了信号就入队,注册了信号处理的话,从队列中取出信号进行处理;如果屏蔽某个信号,那么会不从队列中取出处理。直到解除屏蔽再行处理。屏蔽结束后相同信号只响应一次.
int sigprocmask(
		int how,//是要屏蔽还是解除屏蔽 
		/*
		SIG_BLOCK:屏蔽 
		SIG_UNBLOCK:解除屏蔽
		*/ 
		const sigset_t *set,//信号集
		sigset_t *oldset);//信号

示例代码:

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
void testSignal(int sign)
{
    switch (sign)
    {
    case 2:
        printf("受到了一个%d信号!n",sign);
        break;
    case 28:
        printf("窗口改变了 %d!n",sign);
        break;
    case 14:
        printf("信号%d来了!n",sign);
        break;
    default:
        printf("%d信号!n",sign);
        break;
    }

}
int main()
{
    signal(14,testSignal);
    signal(SIGWINCH,testSignal);
    signal(SIGINT,testSignal);
    int n=0;

    sigset_t set,oldset;
    sigemptyset(&set);
    // sigemptyset(&oldset);
    sigaddset(&set,SIGINT);
    

    printf("PID:%dn",getpid());
    //前十秒钟不加信号屏蔽
    while(1)
    {
        printf("<< 这是前十秒钟,未屏蔽~t%dn",n++);
        sleep(1);
        if(n==10) break;
    }

    int r = 0;
    if(1==sigismember(&set,SIGINT))
    {
        printf("屏蔽当前信号集!n");
        r = sigprocmask(SIG_BLOCK,&set,&oldset);
        if(r==0) printf("屏蔽成功:%m!n");
        else printf("屏蔽失败:%m!n");
    }
    //屏蔽十秒钟
    while(1)
    {
        printf("<< 这是中间的十秒钟,屏蔽~t%dn",n++);
        sleep(1);
        if(n==20) break;
    }

    if(1==sigismember(&set,SIGINT))
    {
        printf("解除当前信号集!n");
        r = sigprocmask(SIG_UNBLOCK,&set,&oldset);
        if(r==0) printf("解除屏蔽成功:%m!n");
        else printf("解除屏蔽失败:%m!n");
    }
    //十秒钟之后解除屏蔽
    while(1)
    {
        printf("<< 这是最后的十秒钟,解除屏蔽~t%dn",n++);
        sleep(1);
        if(n==30) break;
    }

    return 0;
}

No.8 练习

  • 用管道传输文件的练习 传输完后要结束进程
    用信号来结束进程。

A

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
void header(int sigNum)
{
    if(sigNum==SIGINT)
    {
        printf("接收到关闭信号!n");
        exit(-1);
    }
}

int main(int argc,char* argv[])
{
    signal(SIGINT,header);

    if(argc!=2) return 0;

    //1. 创建管道文件(mkfifo)	
    int r = mkfifo("test.pipe",0777);
    if(0==r) printf("创建管道文件:%mn");
    else printf("创建管道文件失败:%mn"),exit(-1);	
	//2. 打开管道文件					
	int fd = open("test.pipe",O_RDWR);
    if(-1==fd) printf("打开管道文件失败:%mn"),exit(-1);
    else printf("打开管道文件成功:%mn");	
	//3. 使用管道文件(写)	

    //获取B进程的pid
    int Aid = getpid();
    int Bid = 0;
   
    read(fd,&Bid,sizeof Bid);
    write(fd,&(Aid),sizeof Aid);
    printf("A pid:%dt  B pid:%dn",Aid,Bid);

    //3.1 获取要传输的文件信息		
    struct stat st ; 
    stat(argv[1],&st);
    //3.2 传送文件名长度
    int Size = strlen(argv[1]);
    write(fd,&Size,4);
    //3.3 传送文件名
    write(fd,argv[1],strlen(argv[1]));
    printf("%s %ldn",argv[1],st.st_size);
    //3.4 传送文件大小
    Size = st.st_size;
    write(fd,&Size,sizeof Size);
    //3.5 打开文件然后传送文件
    int fw = open(argv[1],O_RDONLY);
    r=0;
    char c;
    while(1)
    {
        r = read(fw,&c,1);
        if(r!=1)
        {
            printf("拷贝完成!n");
            break;
        }
        write(fd,&c,1);
    }
    



	//4. 关闭								
	close(fd);
    close(fw);
	//5. 删除管道文件
	unlink("test.pipe");
    kill(Bid,SIGINT);
    while(1);

    return 0;
}

B

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
int Aid = 0;
void header(int sigNum)
{
    if(sigNum==2)
    {
        kill(Aid,2);
         printf("接收到关闭信号!n");
        exit(-1);
    }
}

int main()
{
    signal(SIGINT,header);
	//1. 打开管道文件
    int fd = open("test.pipe",O_RDWR);
    if(-1==fd) printf("打开管道文件失败:%mn"),exit(-1);
    else printf("打开管道文件成功:%mn");	
	//2. 使用管道文件(读)

    //获取A管道的pid以及发送自己的pid
    int Bid = getpid();
    
    write(fd,&(Bid),sizeof Bid);
    read(fd,&Aid,sizeof Aid);
    printf("A pid:%dt  B pid:%dn",Aid,Bid);

    //2.1 获取文件名长度
    int r = 0;
    read(fd,&r,4);
    char* name = (char*)malloc((r+1));
    //2.2 获取文件名
    r = read(fd,name,r);
    name[r+1] = 0;
    printf("%sn",name);
    //2.3 获取文件内容长度
    int Size;
    read(fd,&Size,4);
    printf("size:%dn",Size);
    //2.4 创建文件夹Test
    system("mkdir Test");
    //2.5 链接文件目录
    char demo[1024];
    sprintf(demo,"./Test/%s",name);
    printf("%sn",demo);
    //2.6 创建文件并且进行接受
    int fw = open(demo,O_WRONLY|O_CREAT,0666);
    int n=0;
    char c;
    while(n<=Size)
    {   
        read(fd,&c,1);
        n++;
        write(fw,&c,1);
    }		
	//3. 关闭
	close(fd);
    close(fw);
    while (1);
  
    return 0;
}

最后

以上就是犹豫果汁为你收集整理的Linux - 信号No.8 练习的全部内容,希望文章能够帮你解决Linux - 信号No.8 练习所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部