6 信号与管道 1
目录
6 信号与管道 1
信号
信号的概念
信号的使用
信号的发送
通过函数来实现信号的发送
信号改造函数(重点)
给自己发送信号函数
定时闹钟函数
暂停进程的函数
例题:
代码一:
代码二:
代码分析
-- linux系统下的进程间通信
- 一共六种
- 信号 管道 消息队列 共享内存 信号量集 套节字
信号
信号的概念
-- 信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件)。
-- 信号是硬件中断的软件模拟(软中断) 。
-- 每一个信号用一个整型常量宏表示,以 SIG 开头,比如 SIGCHLD、SIGINT 等, 它们在系统头文件<signal.h> 中定义, 也可以通过在终端键入kill -l查看信号列表,或者键入 man 7
-- 1~31号信号是从unix上面继承过来的 不可靠信号 没有排队机制
-- 34~64是后面补充的信号 可靠信号
-- 信号大部分都是让进程死亡的
信号的使用
-- 主要去搞清楚 一些信号是如何产生的以及如何使用
- SIGHUP:从终端上发出的结束信号; 1
- SIGINT:来自键盘的中断信号(Ctrl-C); 2
- SIGQUIT:来自键盘的退出信号(Ctrl-\); 3
- SIGFPE: 浮点异常信号(例如浮点运算溢出);
- SIGKILL:该信号结束接收信号的进程; 9
- SIGALRM:进程的定时器到期时,发送该信号; 14
- SIGTERM: kill 命令发出的信号;
- SIGCHLD:标识子进程停止或结束的信号; 17
-- 子进程给父进程主动发送的信号(有以下三种情况父进程会向子进程发送信号)
-- 1、子进程死亡
-- 2、子进程由运行转为暂停
-- 3、子进程由暂停转为继续运行 - SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号 19
- SIGCONT: 18信号 继续运行信号
信号的发送
-- 在终端上发送信号
- kill -num pid
- 发送第num个信号到pid这个进程上去
例如:
通过函数来实现信号的发送
-- 函数头文件
- #include <sys/types.h>
- #include <signal.h>
-- 函数原型
- int kill(pid_t pid, int sig);
-- 函数的作用:
- 给指定的进程发送一个信号
-- 函数的参数:
- pid:要给哪一个进程发送信号
- sig:发送的信号
-- 函数的返回值:
- 成功 0
- 失败 -1
信号改造函数(重点)
-- 可以让信号原本的效果消失, 转而去执行相应的函数
-- 函数头文件
- #include <signal.h>
-- 函数原型
- sighandler_t = void (*) (int)
- typedef void (*sighandler_t)(int);
- sighandler_t signal(int signum, sighandler_t handler);
-- 函数的作用:
- 将第一个参数中所填写的信号进行改造
- 让他原本的效果消失
- 接收到这个信号我们的进程会去运行 第二个参数所填写函数
-- 函数的参数:
- signum:填写要进行改造的信号
- 9信号、18 继续、 19 暂停不可以被改造和忽略
- handler:函数指针指向void (*)(int )类型的函数
- 当signal函数运行完之后
- 再次接收到signum信号 会调用handler所指向的函数
- handler如果参数选SIG_DFL,信号的改变就会恢复默认操作
- SIG_IGN 对该信号进行忽略
-- 函数的返回值:
- 成功返回 第二个参数的地址
- 失败返回 SIG_ERR
给自己发送信号函数
--- 函数头文件
- #include <signal.h>
-- 函数原型
- int raise(int sig)
-- 函数的作用:
- 给当前掉用该函数的进程发送一个信号
-- 函数的参数:
- sig:要给自己发送的信号
-- 函数的返回值:
- 成功 0
- 失败 非 0
定时闹钟函数
-- 函数头文件
- #include <unistd.h>
-- 函数原型
- unsigned int alarm(unsigned int seconds);
-- 函数的作用
- 定一个闹钟 计时结束之后会给当前进程发送一个闹钟信号
-- 函数的参数:
- seconds:要计时的秒数
- 给0表示取消之前定的闹钟
-- 函数的返回值:
- 如果前面定过闹钟返回 上一个闹钟的剩余秒数
- 如果前面没有定义过闹钟,返回 0
暂停进程的函数
-- 函数头文件
- #include <unistd.h>
-- 函数原型
- int pause(void); -- 函数的作用:
- 当程序运行到 pause 函数的时候 会暂停
- 当捕获到一个信号的时候 会解除暂停
-- 函数的返回值
- 解除暂停返回 -1
例题:
-- 实现无界面的 MP3 播放器
-- 能实现 上下切歌、播放、暂停、自动播放功能
-- 需要依靠改造 17 信号来实现
-- 17 信号的发出条件 (子进程的状态发生了改变)
- 1 子进程死亡 会 发出 17 信号给父进程
- 2 由运行转为暂停的时候也会发送 17 信号
- 3 由暂停转为继续的时候也会发送 17 信号
代码一:
#include "stdio.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include "string.h"
#include "unistd.h"
#include <stdlib.h>
#include "signal.h"
char * name[20] ={0};
int count = 0;
int i = 0;
pid_t pid;
//子进程被暂停和继续也会发送17信号
//子进程死亡的时候发送的17信号
//需要判断子进程是否存活
void func(int a)
{
printf("接收到17信号\n");
//wait(NULL);会阻塞
pid_t p = waitpid(pid,NULL,WNOHANG);
if(p == 0)//子进程没有死亡
return ;
i++;//偏移下标
if(i == count)//判断是否超了
i = 0;
else if(i < 0)
i = count -1;
pid = vfork();
if(pid == 0)
{
execlp("mpg123","mpg123",name[i],NULL);
}
}
int main(int argc,char*argv[])
{
//1 获取歌曲的绝对路径 目录操作
DIR * dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir()");
return -1;
}
//2 循环读取获取目录下的所有文件
while(1)
{
struct dirent * file = readdir(dir);
if(file == NULL)
break;//循环的结束条件
//查找所有文件后缀为.mp3的文件
if(strstr(file->d_name,".mp3"))
{
//1 把歌的绝对路径存起来
name[count] = malloc(200);
sprintf(name[count],"%s/%s",argv[1],file->d_name);
count++;
}
}
for(int i =0;i<count;i++)
printf("%s\n",name[i]);
while(1)//失效
{
pid = vfork();
if(pid == 0)
{
execlp("mpg123","mpg123",name[i],NULL);
}
else if(pid>0)
{//从键盘输入要进行的操作
char a = 0;
signal(17,func);//信号改造函数
//当接收到17信号 会自动的调用func函数
//子进程给父进程发送17信号 父进程就会调用func函数
while(1)
{
scanf("%c",&a);//阻塞 等待键盘输入
getchar();
//根据不同的值进行不同的操作
switch(a)
{ //暂停
case 'p':
kill(pid,19);//发送暂停信号
printf("暂停\n");
break;
case 'c':
kill(pid,18);//发送继续信号
printf("继续\n");
break;
case 'b':
kill(pid,9);
i=i-2;
printf("上一曲\n");
break;
case 'n':
//突然发现只要子进程死亡 会自动调用func函数
//去播放下一个音乐
kill(pid,9);//给指定的进程发送信号
printf("下一曲\n");
break;
}
}
}
}
return 0;
}
代码二:
#include "stdio.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include "string.h"
#include "unistd.h"
#include <stdlib.h>
#include <signal.h>
char * name[20] ={0};
int count = 0;
int a=0;
pid_t pid = -1; // 当前播放的子进程ID
int i=0;
pid_t d =0;
void handleInput() ;
void playSong(int i) ;
void func(int a) ;
int main(int argc,char*argv[])
{
if(argc==1)
{
printf("请填写参数\n");
return -1;
}
DIR * dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir()");
return -1;
}
while(1)
{
struct dirent * file = readdir(dir);
if(file == NULL)
break;
if(strstr(file->d_name,".mp3"))
{
name[count] = malloc(300);
sprintf(name[count],"%s/%s",argv[1],file->d_name);
count++;
}
}
closedir(dir);
for(int i =0;i<count;i++)
printf("%s\n",name[i]);
signal(17,func);
playSong(i);
handleInput();
for (int i = 0; i < count; i++) {
free(name[i]);
}
return 0;
}
void playSong(int i)
{
if (pid != -1) {
kill(pid, 9);
wait(NULL);
}
pid = fork();
if (pid == 0) {
execlp("mpg123", "mpg123", name[i], NULL);
exit(1);
}
}
void func(int a) {
if (a == 17) {
printf("子进程状态发生改变!\n");
d = waitpid(-1,&a,WNOHANG);
if(d >0){
i++;
if (i >= count) {
i = 0;
}
playSong(i);
}
}
}
void handleInput() {
char n;
while (1) {
printf("请输入命令(p: 暂停, s: 继续, n: 下一首, l: 上一首): \n");
scanf(" %c", &n);
switch (n) {
case 'p':
if (pid != -1) {
kill(pid, 19); // 暂停子进程
}
break;
case 's':
if (pid != -1) {
kill(pid, 18); // 继续子进程
}
break;
case 'n':
if (pid != -1) {
kill(pid, 9); // 终止当前进程
wait(NULL);
}
i++;
if (i >= count) {
i = 0; // 循环到第一首歌
}
playSong(i);
break;
case 'l':
if (pid != -1) {
kill(pid, 9); // 终止当前进程
wait(NULL);
}
i--;
if (i < 0) {
i = count - 1; // 循环到最后一首歌
}
playSong(i);
break;
default:
printf("无效命令\n");
break;
}
}
}