1.进程间通信

1. 信号 -- 通知

2. 管道 -- 无名管道  有名管道

3. IPC通信  -- 进程间通信

共享内存,消息队列,信号量集

4. 套接字 -- 网络通信  网络聊天室

2.信号的概念

信号 -- 类似于通知,是一种软中断机制;

查看信号:kill -l

Linux 信号与管道_#include

前31个是不可靠信号;

后31个是可靠信号,支持排队;

2号信号 -- 杀死进程

3号信号 -- 杀死进程

9号信号 -- 专门用来杀死进程--不允许忽略或者改造

14号信号 -- 闹钟信号 -- 杀死进程

17号信号 -- 子进程只要状态发生变化,父进程就能够接收到17号信号;

18号信号 -- 恢复由19号信号暂停的进程

19 号信号 -- 暂停进程

信号的产生:

方式1:kill -sig pid

kill -2 pid

方式2:快捷方式:ctrl+c 

方式3:内核发送信号  (段错误)

信号的相关API

1》信号的发送函数

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid, int sig);

形参:pid -- 进程号

sig -- 信号

返回值:成功返回0,失败返回-1;


2》给自己发送信号

#include <signal.h>

int raise(int sig);

形参:信号


3》闹钟函数:alarm

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

功能:设置闹钟,在seconds产生闹钟信号

形参:seconds  -- 秒

返回值:在调用alarm之前,已经设置过闹钟返回上一次闹钟的剩余时间;


4》pause -- 阻塞性函数,直到有信号产生会解除阻塞;

#include <unistd.h>

int pause(void);


注意:alarm不建议和sleep一块用;

自己实现一个sleep功能;


5》信号处理函数

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

形参:signum -- 信号

handler  -- 对信号的处理方式

SIG_IGN : 对该信号忽略

SIG_DFL : 对该信号默认

函数名  : 捕获到signum信号之后,执行该函数名对应的函数


插入:

atexit -- 注册退出清理函数

#include <stdlib.h>

int atexit(void (*function)(void));

形参:函数指针;  传递的时候只能写函数名;

功能:在程序正常退出前执行形参中对应的函数

两个例题

1.结合信号中的函数,自己实现sleep的功能

#include<stdio.h>
#include <signal.h>
#include<unistd.h>
void fun(int sig);
int main()
{
    signal(14,fun);//对alarm闹钟进行改造,使其默认秒数结束杀死进程功能替换
    //为fun函数啥也不做,即实现了隔一秒一打印
    int i=0;
    while (1)
    {
        printf("%d\n",i);
        alarm(1);
        pause();//堵塞程序在此一秒
        i++;
    }s
    return 0;
}
void fun(int sig)
{

}
  1. 完成无界面的MP3播放器多进程+信号
#include<stdio.h>
#include<glob.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include<stdlib.h>
void Fun(int sig);
void print(void);
void Clean(void);
int cur=0;
pid_t pid=0;
glob_t song;
int main()
{
    atexit(Clean);//首先防止光标消失,输入终端不显示
    glob("/home/fuc/音乐/*.mp3",0,NULL,&song);//通过glob把该文件里的歌曲存放到song里
    pid=fork();//创建子进程
    if(pid==0)//子进程播放歌曲
    {
        execlp("mpg123","mpg123",song.gl_pathv[cur],NULL);
    }
    else if(pid>0)
    {
        int num=0;
        signal(17,Fun);//定义17信号
        print();
        while(1)
        {
            scanf("%d",&num);
            getchar();
            switch(num)
            {
                case 1:kill(pid,19);printf("音乐已暂停\n");break;
                case 2:kill(pid,18);printf("音乐已播放\n");break;
                case 3:
                cur++;
                if(cur==song.gl_pathc)
                cur=0;
                kill(pid,9);
                printf("下一首\n");
                break;
                case 4:
                cur--;
                if(cur==-1)//第0首歌的上一首歌为本歌单最后一首
                cur=song.gl_pathc-1;//本歌单最后一首
                kill(pid,9);
                printf("上一首\n");
                break;
                case 0:signal(17,SIG_IGN);kill(pid,9);printf("音乐已退出\n");return 0;
                //先恢复signal忽略17信号的作用,再退出防止父进程退出,子进程还在继续运行
            }
        }
        
    }
        return 0;
}
void print(void)
{
    printf("--1 暂停 -- --2 恢复 -- --3 下一首 -- --4 上一首 --\n");
}
void Clean(void)
{
    system("stty echo");//回显
    printf("\033[?025h");//光标
}
void Fun(int sig)
{
    int sta=0;
    pid_t res=waitpid(pid,&sta,WNOHANG);//此时waitpid为非阻塞,把子进程的结束状态赋值给sta
    if(res==pid)//排除暂停和恢复带来的干扰
    {
        if(WIFEXITED(sta))//当sta值为非0则说明子进程是自然结束的
        {
            cur++;//下一首
            if(cur==song.gl_pathc)
            {
                cur=0;
            }
        }
        pid=fork();//继续创建子进程进行播放
        if(pid==0)
        {
            execlp("mpg123","mpg123",song.gl_pathv[cur],NULL);
        }
    }
}