说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
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;
}