上一节,我们讨论了信号的基本概念,信号的机制,与信号相关的事件与状态以及信号的四要素。那么有关信号还有一些基础知识需要学习,我们一起来看一下吧。主要包括信号的产生,信号集相关的函数,信号的捕捉,信号的传参等。
1 信号的产生
终端按键产生信号
Ctrl + c → 2) SIGINT(终止/中断) "INT"----Interrupt
Ctrl + z → 20) SIGTSTP(暂停/停止) "T" ----Terminal 终端。
Ctrl + \ → 3) SIGQUIT(退出)
硬件异常产生信号
除0操作 → 8) SIGFPE (浮点数例外) "F" -----float 浮点数。
非法访问内存 → 11) SIGSEGV (段错误)
总线错误 → 7) SIGBUS
kill函数/命令产生信号
kill命令产生信号:kill -SIGKILLpid
kill函数:给指定进程发送指定信号(不一定杀死)
int kill(pid_t pid, int sig); 成功:0;失败:-1 (ID非法,信号非法,普通用户杀init进程等权级问题),设置errno
sig:不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。
pid > 0: 发送信号给指定的进程。
pid = 0: 发送信号给 与调用kill函数进程属于同一进程组的所有进程。
pid < 0: 取|pid|发给对应进程组。
pid = -1:发送给进程有权限发送的系统中所有进程。
进程组:每个进程都属于一个进程组,进程组是一个或多个进程集合,他们相互关联,共同完成一个实体任务,每个进程组都有一个进程组长,默认进程组ID与进程组长ID相同。
权限保护:super用户(root)可以发送信号给任意用户,普通用户是不能向系统用户发送信号的。 kill -9 (root用户的pid) 是不可以的。同样,普通用户也不能向其他普通用户发送信号,终止其进程。只能向自己创建的进程发送信号。普通用户基本规则是:发送者实际或有效用户ID == 接收者实际或有效用户ID
循环创建5个子进程,任一子进程用kill函数终止其父进程。 【kill.c】
raise和abort函数
raise函数:给当前进程发送指定信号(自己给自己发) raise(signo) ==kill(getpid(), signo);
intraise(int sig); 成功:0,失败非0值
abort 函数:给自己发送异常终止信号 6) SIGABRT 信号,终止并产生core文件
voidabort(void); 该函数无返回
软件条件产生信号
alarm函数
设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止。
每个进程都有且只有唯一个定时器。
unsigned intalarm(unsigned int seconds); 返回0或剩余的秒数,无失败。
常用:取消定时器alarm(0),返回旧闹钟余下秒数。
例:alarm(5) → 3sec → alarm(4) → 5sec → alarm(5) → alarm(0)
定时,与进程状态无关(自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵尸...无论进程处于何种状态,alarm都计时。
练习:编写程序,测试你使用的计算机1秒钟能数多少个数。 使用time命令查看程序执行的时间。
程序运行的瓶颈在于IO,优化程序,首选优化IO。
实际执行时间 = 系统时间 + 用户时间 + 等待时间
#include #include int main(void) { int i; alarm(1); for(i = 0; ; i++) printf("%d\n", i); return 0; } |
setitimer函数
设置定时器(闹钟)。可代替alarm函数。精度微秒us,可以实现周期定时。
int setitimer(int which, const structitimerval *new_value, struct itimerval *old_value); 成功:0;失败:-1,设置errno
参数:which:指定定时方式
①自然定时:ITIMER_REAL → 14 )SIGLARM 计算自然时间②虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM 只计算进程占用cpu的时间
③运行时计时(用户+内核):ITIMER_PROF → 27)SIGPROF 计算占用cpu及执行系统调用的时间
使用setitimer函数实现alarm函数,重复计算机1秒数数程序。
#include #include #include /* * struct itimerval { struct timeval{ it_value.tv_sec; it_value.tv_usec; } it_interval; struct timeval { it_value.tv_sec; it_value.tv_usec; } it_value; } it, oldit; */ unsigned int my_alarm(unsigned int sec) { struct itimerval it, oldit; int ret; it.it_value.tv_sec = sec; it.it_value.tv_usec = 0; it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 0; ret = setitimer(ITIMER_REAL, &it, &oldit); if (ret == -1) { perror("setitimer"); exit(1); } return oldit.it_value.tv_sec; } int main(void) { int i; my_alarm(1); //alarm(sec); for(i = 0; ; i++) printf("%d\n", i); return 0; } |
拓展,结合man page编写程序,测试it_interval、it_value这两个参数的作用。
提示: it_interval:用来设定两次定时任务之间间隔的时间。 it_value:定时的时长