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; // 当前播放的子进程IDint 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; } }
}