linux编程(进程间通信)

IPC   进程间通信  interprocess communicate
三大类:
1、古老的通信方式
无名管道  有名管道  信号
2、IPC对象通信 system v    BSD     suse fedora   kernel.org
消息队列
共享内存
信号量集 
3、socket通信
 网络通信

古老的通信方式中信号是唯一的异步通信
所有的通信方式中共享内存是唯一的最高效

管道==》无名管道、有名管道
无名管道 ===》pipe ==》只能给有亲缘关系进程通信
有名管道 ===》fifo ==》可以给任意单机进程通信

管道的特性:
1、管道是 半双工的工作模式
2、所有的管道都是特殊的文件不支持定位操作。lseek->> fd  fseek ->>FILE* 
3、管道是特殊文件,读写使用文件IO。fgets,fread,fgetc,
open,read,write,close;

1,读端存在,一直向管道中去写,超过64k,写会阻塞。
2.写端是存在的,读管道,如果管道为空的话,读会阻塞。
3.管道破裂,,读端关闭,写管道。
4. read 0 ,写端关闭,如果管道没有内容,read 0 ;
   使用框架:创建管道 ==》读写管道 ==》关闭管道

验证读阻塞:

验证写阻塞:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
    
    int pipefd[2]={0};
    int ret = pipe(pipefd);
    if(-1 == ret)
    {
        perror("pipe");
        exit(1);
    }


    pid_t pid = fork();
    if(pid>0)
    {
        char buf[1024]={0};
        close(pipefd[0]);
        memset(buf,'a',1024);
        int i = 0 ;
        for(i=0;i<65;i++)
        {
            write(pipefd[1],buf,sizeof(buf));
            printf("i is %d\n",i);
        }
    }
    else if(0 == pid)
    {
        close(pipefd[1]);
        char buf[100]={0};
        while(1)sleep(1);
    }
    else 
    {
        perror("fork");
        exit(1);
    }
    return 0;
}

验证管道破裂:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
    
    int pipefd[2]={0};
    int ret = pipe(pipefd);
    if(-1 == ret)
    {
        perror("pipe");
        exit(1);
    }


    pid_t pid = fork();
    if(pid>0)
    {
        char buf[]="hello,world";
        close(pipefd[0]);
        sleep(3);
        // 管道破裂 gdb  跟踪
        write(pipefd[1],buf,strlen(buf));
        printf("aaaa\n");
    }
    else if(0 == pid)
    {
        close(pipefd[1]);
        close(pipefd[0]);
    }
    else 
    {
        perror("fork");
        exit(1);
    }
    return 0;
}

1、无名管道 ===》管道的特例 ===>pipe函数
特性:
1.1  亲缘关系进程使用
1.2  有固定的读写端
    流程:
创建并打开管道: pipe函数
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建并打开一个无名管道
参数:pipefd[0] ==>无名管道的固定读端
  pipefd[1] ==>无名管道的固定写端
返回值:成功 0
失败 -1;

注意事项:
1、无名管道的架设应该在fork之前进行。
无名管道的读写:===》文件IO的读写方式。
读: read()
写: write()
关闭管道: close();

无名管道实现文件复制:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
    
    int pipefd[2]={0};
    int ret = pipe(pipefd);
    if(-1 == ret)
    {
        perror("pipe");
        exit(1);
    }


    pid_t pid = fork();
    if(pid>0)
    {
        close(pipefd[0]);
       int fd =  open("/home/linux/1.png",O_RDONLY);
       if(-1 == fd)
       {
            perror("open");
            exit(1);
       }
       while(1)
       {
            char buf[4096]={0};
            int rd_ret = read(fd,buf,sizeof(buf));
            if(rd_ret<=0)
            {
                break;
            }
            write(pipefd[1],buf,rd_ret);
       
       }
       close(fd);
       close(pipefd[1]);
    }
    else if(0 == pid)
    {
        close(pipefd[1]);
        int fd = open("2.png",O_WRONLY|O_CREAT|O_TRUNC,0666);
        if(-1==fd)
        {
            perror("open");
            exit(1);
        }
        while(1)
        {
            char buf[1024]={0};
            int rd_ret = read(pipefd[0],buf,sizeof(buf));
            if(rd_ret<=0)
            {
                break;
            }
            write(fd,buf,rd_ret);
        }
        close(fd);
        close(pipefd[0]);
    }
    else 
    {
        perror("fork");
        exit(1);
    }
    return 0;
}

有名管道===》fifo ==》有文件名称的管道。
  文件系统中可见

框架:
创建有名管道 ==》打开有名管道 ==》读写管道
==》关闭管道  ==》卸载有名管道

1、创建:mkfifo
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
功能:在指定的pathname路径+名称下创建一个权限为
      mode的有名管道文件。
参数:pathname要创建的有名管道路径+名称
  mode  8进制文件权限。
返回值:成功 0
失败  -1;

2、打开有名管道 open
注意:该函数使用的时候要注意打开方式,
因为管道是半双工模式,所有打开方式直接决定
当前进程的读写方式。
一般只有如下方式:
int fd-read = open("./fifo",O_RDONLY); ==>fd 是固定读端
int fd-write = open("./fifo",O_WRONLY); ==>fd 是固定写端
不能是 O_RDWR 方式打开文件。
不能有 O_CREAT 选项,因为创建管道有指定的mkfifo函数

3、管道的读写: 文件IO
读: read(fd-read,buff,sizeof(buff));
写: write(fd-write,buff,sizeof(buff));

4、关闭管道:
close(fd);

5、卸载管道:remove();
int unlink(const char *pathname);
功能:将指定的pathname管道文件卸载,同时
  从文件系统中删除。
参数: ptahtname 要卸载的有名管道 
返回值:成功 0
失败  -1;
 

写入管道

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    int ret = mkfifo("myfifo",0666);    
    if(-1 == ret)
    {
        //如果是管道文件已存在错误,让程序继续运行
        if(EEXIST== errno)
        {

        }else 
        {
            perror("mkfifo");
            exit(1);
        }
    }
    //open 会阻塞,等到另一端读段打开,解除阻塞
    int fd = open("myfifo",O_WRONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }

    char buf[256]="hello,fifo,test";
    write(fd,buf,strlen(buf));
    close(fd);

    return 0;
}

读管道

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    int ret = mkfifo("myfifo",0666);    
    if(-1 == ret)
    {
        //如果是管道文件已存在错误,让程序继续运行
        if(EEXIST== errno)
        {
        
        }else 
        {
            perror("mkfifo");
            exit(1);
        }
    }

    int fd = open("myfifo",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }

    char buf[256]={0};
    read(fd,buf,sizeof(buf));
    printf("fifo %s\n",buf);
    close(fd);
    //remove("myfifo");

    return 0;
}

进程间通信 ===》信号通信
应用:异步通信。 中断,,
1~64;32应用编程。
如何响应:
     Term   Default action is to terminate the process.

      Ign    Default action is to ignore the signal.
   wait
   

       Core   Default action is to  terminate  the  process  and  dump  core  (see
              core(5)).
gdb a.out -c core
       Stop   Default action is to stop the process.

       Cont   Default  action  is  to  continue  the  process  if  it is currently
              stopped.


kill      -xx     xxxx
发送进程  信号    接收进程
kill -9 1000
a.out  9 1000
1、发送端
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
功能:通过该函数可以给pid进程发送信号为sig的系统信号。
参数:pid 要接收信号的进程pid
  sig 当前程序要发送的信号编号 《=== kill  -l
返回值:成功 0
失败  -1;

示例

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    printf("input pid");
    char buf[10]={0};
    fgets(buf,sizeof(buf),stdin);// 1000\n
    pid_t pid = atoi(buf);
    int ret = kill(pid,9);
    if(-1 == ret)
    {
        perror("kill");
        exit(1);
    }
    return 0;
}

unsigned int alarm(unsigned int seconds);SIGALAM
功能:定时由系统给当前进程发送信号
  也称为闹钟函数

  闹钟只有一个,定时只有一次有效,
  但是必须根据代码逻辑是否执行判断。

示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    alarm(5);
    while(1)
    {
        printf("i'm working\n");
        sleep(1);
    }
    return 0;
}

int pause(void);
功能:进程暂停,不再继续执行,除非
  收到其他信号。

示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int i = 0 ;
    while(1)
    {
        printf("i'm working\n");
        sleep(1);
        i++;
        if(5 == i)
        {
        
            pause();
        }
    }
    return 0;
}

接收端
每个进程都会对信号作出默认响应,但不是唯一响应。
一般如下三种处理方式:
1、默认处理
2、忽略处理 9,19
3、自定义处理 9,19 捕获

以上三种方式的处理需要在如下函数上实现。

信号注册函数原型:
 
 void ( *signal(int signum, void (*handler)(int)) ) (int);


 typedef void (*sighandler_t)(int);
 ===》void (*xx)(int); == void fun(int);
 ===》xx是 void fun(int) 类型函数的函数指针

 ===>sighandler_t signal(int signum, sighandler_t handler);
 ===> signal(int sig, sighandler_t fun);
 ===> signal(int sig, xxx fun);
 ===>fun 有三个宏表示:SIG_DFL 表示默认处理
   SIG_IGN 表示忽略处理
   fun     表示自定义处理

示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
int flag = 0;
void handle(int num)
{
    flag = 1;    
}
int main(int argc, char *argv[])
{
    signal(SIGALRM,handle);
    alarm(5);
    while(1)
    {
        if(0 == flag)
        {
        
            printf("i'm working\n");
        }
        else
        {
            
            printf("i'm reseting\n");
        }
        sleep(1);
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void myhandle(int num)
{
}

int main(int argc, char *argv[])
{
    int i = 0 ;
    signal(SIGCONT,myhandle);
    while(1)
    {
        printf("i'm working pid:%d\n",getpid());
        sleep(1);
        i++;
        if(5 == i)
        {
            pause();
        }
    }
    return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void handle1(int num)
{
    static int i = 0;
    printf("老爸叫你\n");
    i++;
    if(3 == i )
    {
        signal(SIGUSR1,SIG_IGN);
    }

}
void handle2(int num)
{
    static int i = 0 ;
    printf("老妈叫你\n");
    i++;
    if(4== i)
    {
        signal(SIGUSR2,SIG_DFL);
    }
}
int main(int argc, char *argv[])
{
    signal(SIGUSR1,handle1);
    signal(SIGUSR2,handle2);
    while(1)    
    {
        printf("i'm playing pid :%d\n",getpid());
        sleep(1);
    }
    return 0;
}

自定义信号处理:
1、必须事先定义自定义函数,必须是如下格式:
void fun(int sig)  sig 接收到的信息编号
{

}

2、在所有的信号中有如下两个特列:
10 SIGUSR1
12 SIGUSR2

专门预留给程序员使用的未定义信号。

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值