函数简介篇——signal示例(附前后台切换)

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《Unix环境高级编程》

示例一:捕获用户信号

1️⃣说明:

  若捕获用户信号SIGUSR1、SIGUSR2则调用用户创建的信号处理函数sig_usr

2️⃣源文件:signal.c
#include<stdio.h>
#include<unistd.h>
#include<signal.h>

static void sig_usr(int signo)
{
        if(signo == SIGUSR1)
                printf("received SIGUSER1\n");
        else if(signo == SIGUSR2)
                printf("received SIGUSER2\n");
        else
                printf("received signal %d\n",signo);

}


int main(int argc,char *argv[])
{
        if(signal(SIGUSR1,sig_usr) == SIG_ERR)
                fprintf(stderr,"can't catch SIGUSER1");

        if(signal(SIGUSR2,sig_usr) == SIG_ERR)
                fprintf(stderr,"can't catch SIGUSER2");
        for(;;)
                pause();

        return 0;
}

3️⃣编译运行及结果:
$ gcc signal.c -o signal						编译生成signal可执行文件

$ ./signal & 									在后台启动进程执行signal程序
[1] 4789											jobID:1,PID:4789ps

$ kill -USR1 4789								发送SIGUSR1信号
received SIGUSER1

$  kill -USR2 4789								发送SIGUSR2信号
received SIGUSER2

$ kill 4789										发送SIGTERM信号(终止信号)
4️⃣相关说明:

  ①可执行程序后加上&
    表示该程序在后台执行。不加默认前台执行,被shell占据,常用需交互的进程。

5️⃣前后台切换相关说明

详细查看:Linux程序前台后台切换在linux后台运行脚本命令和程序的方法大全
  ①&字符:可执行文件后加& 表示后台运行。
  ② Ctrl+Z快捷键:将前台进程切换到后台并暂停状态。
  ③ fg命令fg [job_id]将后台进程调为前台。
  ④ bg命令bg [job_id]将前台暂停,变成后台继续执行
  ⑤ jobs命令:查看当前有多少在后台运行的命令
  ⑥ ps命令:查看进程,详细查看:shell ps命令
  ⑦ nohup命令:如果让程序始终在后台执行,即使关闭当前的终端也执行。.
  ⑧ kill命令:发送信号,详细查看shell kill命令
  ⑨disown命令:隔离终端挂断信号,事后使用;
  ⑩setsid命令:重新创建一个会话进程来执行任务;

6️⃣前后台切换运用实践
$ ps								   	①查看当前进程
    PID TTY          TIME CMD				PID:进程号,TTY:运行终端  TIME 运行CPU处理时间  CMD  进程命令
   3672 pts/1    00:00:00 bash
   4174 pts/1    00:00:00 ps
   
$ ./signal &							②后台运行signal程序
[1] 4199								jobID:1,PID:4199

$ jobs									③查看后台运行程序
[1]+  Running                 ./signal &

$ fg 1									④切换为前台程序
./signal
【光标闪烁等待输入】

^Z										⑤Ctrl+Z:将前台进程切换到后台并暂停状态。
[1]+  Stopped                 ./signal

$ jobs									⑥再次查看后台运行程序
[1]+  Stopped                 ./signal

$ bg 1									⑦将后台暂停,变成后台继续执行
[1]+ ./signal &

$ jobs									⑧再次查看后台运行程序
[1]+  Running                 ./signal &
 
$  ps									⑨查看进程ID
    PID TTY          TIME CMD
   3672 pts/1    00:00:00 bash
   4199 pts/1    00:00:00 signal
   4332 pts/1    00:00:00 ps
  
$ kill -9 4199                          ⑩杀死该进程

$ nohup ./signal &						①在系统后台不挂断地运行命令,退出终端不会影响程序的运行
[1] 4710
$ nohup: ignoring input and appending output to 'nohup.out'

【Ctrl+Shitf+T在打开一个终端】
$ ps -a
    PID TTY          TIME CMD
   1632 tty2     00:00:21 Xorg
   1683 tty2     00:00:00 gnome-session-b
   4710 pts/0    00:00:00 signal
   4719 pts/2    00:00:00 ps

示例二:打印 *

1️⃣说明:

  每秒钟打印出一个*,若使用Ctrl+C发送SIGINT信号时,第一条:忽略终止信号,第二条:打印!

2️⃣源程序
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

static void int_handler(int s)
{
        write(1,"!",1);
}

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

#if 0                                   
                                          //启用该语句时,Ctrl+C中断信号不起作用
        signal(SIGINT,SIG_IGN);           //当接收到SIGINT信号时,将其忽略掉

#elif 1
                                          //启用该语句时。Ctrl+C中断信号会输出!,且信号会打断阻塞的系统调用(加速结束)
        signal(SIGINT,int_handler);       //若程序结束前,收到SIGINT信号时执行int_handler该行为

#endif
        for(i = 0 ; i < 10; i++)
        {
                write(1,"*",1);
                sleep(1);
        }
        return 0;
}

示例三:5秒钟不停计数

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

static volatile int loop = 1;

static void alarm_handler(int s)
{
        loop = 0;
}


int main(int agrc,char *argv[])
{
        int64_t count = 0;
        alarm(5);
        signal(SIGALRM,alarm_handler);

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


示例四:打开文件并将文件内容输出,以每秒钟输出10字节数据(流量控制)[漏桶实现]

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

#define CPS 10                          //每秒钟传输字符的个数
#define BUFSIZE CPS                     //缓冲区大小

static volatile int loop = 0;

static void alrm_handler(int s)
{
        alarm(1);
        loop = 1;
}

int main(int argc,char *argv[])
{
        int sfd,dfd = 1;
        char buf[BUFSIZE];
        int len,ret,pos;

        /* 判断传入参数是否有误 */
        if(argc < 2)
        {
                fprintf(stderr,"Usage...\n");
                exit(1);
        }

        /* 注册信号 */
        signal(SIGALRM,alrm_handler);
        alarm(1);


        /* 打开传入参数的文件 */
        do
        {
                sfd = open(argv[1],O_RDONLY);
                if(sfd < 0)
                {
                        if(errno != EINTR)
                        {
                                perror("open()");
                                exit(1);
                        }
                }
        }while(sfd < 0);

        /* 不断读取文件内容并将内容进行输出 */
        while(1)
        {
                while(!loop)
                        pause();                //去除盲等效果
                loop = 0;

                while( (len = read(sfd,buf,BUFSIZE)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        perror("read()");
                        break;
                }
                if(len == 0)
                        break;

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

        /* 关闭文件 */
        close(sfd);
        return 0;
}


示例五:打开文件并将文件内容输出,以每秒钟输出10字节数据(流量控制)[令牌桶实现]

  与实例三相比修改的极少,目的是为了应对更加实际的情况,数据一般是单词一次性大量输入而不是逐次小量输入,故修改为令牌桶实现能更好的利用一次性大量输入,作为缓冲后在根据实际需求慢慢输出,而不像漏桶实现可能导致浪费资源的现象。

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

#define CPS 10                          //每秒钟传输字符的个数
#define BUFSIZE CPS                     //缓冲区大小
#define BURST 100

static volatile sig_atomic_t token = 0;//sig_atomic_t 保证对该变量赋值一定是原子的

static void alrm_handler(int s)
{
        alarm(1);
        token++;                //有权限则自增(有机会则保存到令牌桶中)
        if(token > BURST)       //设置最大的值
        {
                token = BURST;
        }
}

int main(int argc,char *argv[])
{
        int sfd,dfd = 1;
        char buf[BUFSIZE];
        int len,ret,pos;

        /* 判断传入参数是否有误 */
        if(argc < 2)
        {
                fprintf(stderr,"Usage...\n");
                exit(1);
        }

        /* 注册信号 */
        signal(SIGALRM,alrm_handler);
        alarm(1);


        /* 打开传入参数的文件 */
        do
        {
                sfd = open(argv[1],O_RDONLY);
                if(sfd < 0)
                {
                        if(errno != EINTR)
                        {
                                perror("open()");
                                exit(1);
                        }
                }
        }while(sfd < 0);

        /* 不断读取文件内容并将内容进行输出 */
        while(1)
        {
                /* 若token<=0则盲等,>0则token-- */
                while(token <= 0)
                        pause();                //去除盲等效果
                token--;

                while( (len = read(sfd,buf,BUFSIZE)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        perror("read()");
                        break;
                }
                if(len == 0)
                        break;

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

        /* 关闭文件 */
        close(sfd);
        return 0;
}

示例六:打开文件并将文件内容输出,以每秒钟输出10字节数据(流量控制)[令牌桶实现封装为通用函数]

main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include "mytbf.h"

#define CPS 10                          //每秒钟传输字符的个数
#define BUFSIZE 1024                    //缓冲区大小
#define BURST 100                       //令牌桶最大数


int main(int argc,char *argv[])
{
        int sfd,dfd = 1;
        char buf[BUFSIZE];
        int len,ret,pos;
        mytbf_t *tbf;
        int size;

        /* 判断传入参数是否有误 */
        if(argc < 2)
        {
                fprintf(stderr,"Usage...\n");
                exit(1);
        }

        /* 令牌桶初始化 */
        tbf = mytbf_init(CPS,BURST);
        if( tbf == NULL)
        {
                fprintf(stderr,"mytbf_init() faile!");
                exit(1);
        }

        /* 打开传入参数的文件 */
        do
        {
                sfd = open(argv[1],O_RDONLY);
                if(sfd < 0)
                {
                        if(errno != EINTR)
                        {
                                perror("open()");
                                exit(1);
                        }
                }
        }while(sfd < 0);

        /* 不断读取文件内容并将内容进行输出 */
        while(1)
        {
                /* 取令牌桶 */
                size = mytbf_fetchtoken(tbf,BUFSIZE);
                if(size < 0)
                {
                        fprintf(stderr,"mytbf_fetchtoken():%s\n",strerror(-size));
                        exit(1);
                }

                while( (len = read(sfd,buf,size)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        perror("read()");
                        break;
                }
                if(len == 0)
                        break;

                /* 归还令牌 */
                if(size - len >0)
                {
                        mytbf_returntoken(tbf,size - len);
                }

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

        /* 关闭文件 */
        close(sfd);

        mytbf_destroy(tbf);
        return 0;
}

mytbf.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include "mytbf.h"

typedef void (*sighandler_t)(int);
static struct mytbf_st *job[MYTBF_MAX]; //数组的格式定义令牌桶结构体
static int inited = 0;
static sighandler_t alrm_handler_save;

/* 令牌桶结构体(私有结构体) */
struct mytbf_st
{
        int cps;                //流速
        int burst;              //上限
        int token;              //令牌数
        int pos;
};

/*
 * @function: 获取空闲位置
 * @param :   无    
 *
 * @return: 
 *      成功返回int型的pos,
 *      失败返回-1
 */
static int get_free_pos(void)
{
        int i ;

        for(i = 0; i < MYTBF_MAX; i++)
        {
                if(job[i] == NULL)
                        return i;
        }
        return -1;
}

/*
 * @function:       回调函数函数指针
 * @param s:       
 *
 * @return:         无
 */

static void alrm_handler(int s)
{
        int i;

        alarm(1);

        for(i = 0; i < MYTBF_MAX; i++)
        {
                if(job[i] != NULL)
                {
                        job[i]->token += job[i]->cps;
                        if(job[i]->token >job[i]->burst )
                                job[i]->token = job[i]->burst;
                }
        }
}

/*
 * @function:  模块卸载函数
 * @param :    无   
 *
 * @return:    无
 */
static void module_unload(void)
{
        int i;

        signal(SIGALRM,alrm_handler_save);      //恢复SIGALRM信号
        alarm(0);                               //关闭alarm()函数
        for(i = 0; i < MYTBF_MAX; i++)          //释放数组空间
                free(job[i]);
}


/*
 * @function:  模块加载函数
 * @param :    无   
 *
 * @return:    无
 */
static void module_load(void)
{
        alrm_handler_save = signal(SIGALRM,alrm_handler);
        alarm(1);
        atexit(module_unload);                  //钩子函数
}

/*
 * @function: 获取两数中的最小值
 * @param a:  数值a       
 * @param b:  数值b
 *
 * @return:   数值最小值
 */
static int min(int a,int b)
{
        if(a < b)
                return a;
        return b;
}
/************************  对外接口函数  *************************/

/*
 * @function:       初始化函数
 * @param cps:       
 * @param burst:
 *
 * @return: mytbf_t 结构体指针
 */
mytbf_t *mytbf_init(int cps,int burst)
{
        struct mytbf_st *me;
        int pos;

        /*
         * STEP 1:模块加载
         *      完成对信号的注册与设置(仅实现一次)
         */
        if(!inited)
        {
                module_load();
                inited = 1;
        }

        /*
         * STEP 2:获取空闲位置数值
         *      若小于0则没找到空闲位置
         * */
        pos = get_free_pos();
        if( pos < 0)
                return NULL;

        /*
         * STEP 3:开辟结构体空间大小并为其赋值
         */
        me = malloc(sizeof(*me));
        if(me == NULL)
                return NULL;
        me->token = 0;
        me->cps = cps;
        me->burst = burst;
        me->pos = pos;

        job[pos] = me;
        return me;
}


/*
 * @function:     取令牌函数
 * @param ptr:    令牌桶结构体指针   
 * @param size:   想要token的数值大小(一般取令牌桶最大值)
 *
 * @return :      返回本次令牌数
 */
int mytbf_fetchtoken(mytbf_t *ptr,int size)
{
        struct mytbf_st *me = ptr;
        int n;

        if(size <= 0)
                return -EINVAL;         //表示错误为-

        /* 若令牌小于0则进程处于休眠状态 */
        while(me->token <= 0)
                pause();

        /* 获取现存令牌数和size中的最小值,并更新 */
        n = min(me->token,size);
        me->token -= n;

        return n;
}

/*
 * @function:     归还令牌函数
 * @param ptr:    令牌桶结构体指针   
 * @param size:   归还令牌的数(目前令牌数-实际令牌数)
 *
 * @return:       返回归还的令牌数
 */
int mytbf_returntoken(mytbf_t *ptr,int size)
{
        struct mytbf_st *me = ptr;

        if(size <= 0)
                return -EINVAL;

        me->token += size;
        if(me->token > me->burst)
                me->token = me->burst;

        return size;
}

/*
 * @function: 销毁函数
 * @param ptr:    令牌桶结构体指针   
 *
 * @return:
 */
int mytbf_destroy(mytbf_t *ptr)
{
        struct mytbf_st *me = ptr;

        job[me->pos] = NULL;
        free(me);

        return 0;
}


mytbf.h
#ifndef __MYTBF_H_
#define __MYTBF_H_


#define MYTBF_MAX 1024                           //令牌桶的最大值
        
typedef void mytbf_t;

mytbf_t *mytbf_init(int cps,int burst);         //初始化

int mytbf_fetchtoken(mytbf_t *,int);            //取令牌

int mytbf_returntoken(mytbf_t *,int);           //还令牌

int mytbf_destroy(mytbf_t *);                   //销毁

#endif /* __MYTBF_H_ */

Makefile
all:mytbf
mytbf: main.o mytbf.o
        gcc $^ -o $@
clean:
        rm -rf *.o mytbf

示例七

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

/*信号处理函数*/
static void sig_handle(int signo)
{
	if(signo == SIGSTOP)							//SIGSTOP信号
	{
		printf("接收到信号SIGSTOP\n");
	}
	else if(signo == SIGKILL)						//SIGKILL信号
	{
		printf("接收到信号SIGKILL\n");
	}
	else 											//其他信号
	{
		printf("接收到信号:%d\n",signo);
	}
	return;
}

int main (int agrc,char *argv[])
{
	sighandler_t ret;

	ret = signal(SIGSTOP, sig_handle);	 			//挂接SIGSTOP信号处理函数

	if(ret == SIG_ERR)				/*挂接失败*/
	{
		printf("为SIGSTOP挂接信号处理函数失败\n");
		return -1;
	}

	ret = signal(SIGKILL, sig_handle);				//挂接SIGKILL信号处理函数
	if(ret == SIG_ERR)				/*挂接失败*/
	{
		printf("为SIGKILL挂接信号处理函数失败\n");
		return-1;
	}
	for(;;)											//等待程序退出	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值