linux高级编程(IPC进程间通信)

#IT专业入门,高考假期预习指南#

IPC:进程间通信

1、古老的通信方式
        无名管道  有名管道  信号

2、IPC对象通信
        消息队列(用的相对少,这里不讨论)
        共享内存
        信号量集 

3、socket通信
        网络通信
        线程信号,posix()  sem_init(信号量)

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

管道:

管道-->无名管道、有名管道

    无名管道 --->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 ;

有读没有写,读阻塞

有写没有读,管道破裂

使用框架:
    创建管道 ==》读写管道 ==》关闭管道

无名管道

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();

问题:

1.父子进程是否都有fd[0] fd[1]?     都有
   如果在单一进程中写fd[1]能否直接从fd[0]中读到?
   可以,写fd[1]可以从fd[0]读(操作系统操作好了,其实管道只有一个实体,只是读写操作分成了fd[0]与fd[1])

----->>>

2.管道的数据存储方式是什么样的(管道数据是保存在内核管理的内存缓冲区中)
   数据是否一直保留?(不会一直保留,在管道被关闭时释放)

栈:

局部变量:栈用于存储函数的局部变量、返回地址、函数参数等。
生命周期:栈上的数据通常在函数调用结束时销毁。
大小限制:栈的大小通常是有限的,并且由操作系统或程序设置。

管道:

内核管理:管道的缓冲区是由操作系统内核管理的,不直接与进程的栈相关。
生命周期:管道缓冲区的生命周期与管道本身相同,通常在管道被关闭时释放。
大小限制:管道缓冲区的大小通常是固定的,并且与特定的操作系统实现有关。

堆:

大小:堆的大小不是固定的,它可以根据程序的需要动态增长或缩小。
生命周期:堆内存的生命周期由分配和释放操作决定,与函数调用的生命周期无关。
访问方式:堆内存的访问是随机的,没有固定的顺序。
多线程共享:在多线程程序中,堆内存是共享的,所有线程都可以访问堆上的数据。

3、管道的数据容量是多少,有没有上限值。
    操作系统的建议值: 512* 8 = 4k(4kb是1页的大小,管道大小不能小于1页)
    代码测试实际值:   65536byte= 64k(16页,管道的大小必须为4k的整数倍,少了会向上补全)

4、管道的同步效果如何验证?读写同步验证。
    读端关闭能不能写? 不可以 ===>SIGPIPE 异常终止(管道破裂) 
    写端关闭能不能读? 可以,取决于pipe有没有内容,===>read返回值为0(如果写端是开启的,read将会被阻塞)

    结论:读写端必须同时存在,才能进行(系统内部的读写阻塞)
              管道的读写。

5、固定的读写端是否就不能互换?
    能否写fd[0] 能否读fd[1]?   不可以,是固定读写端。

有名管道

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

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

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

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));(写端看情况可以strlen)

问题:

1、是否需要同步,以及同步的位置。
        读端关闭 是否可以写,不能写什么原因。
        写端关闭 是否可以读。

        结论:有名管道执行过程过必须有读写端同时存在。
              如果有一端没有打开,则默认在open函数部分阻塞。

2、有名管道是否能在fork之后的亲缘关系进程中使用。
        结论: 可以在有亲缘关系的进程间使用。
        注意: 启动的次序可能会导致其中一个稍有阻塞。

信号通信

  kill      -xx     xxxx(终端下命令)(使用kill -l可以看到所有的信号列表)(ps aux命令查看进程号)
    发送进程  信号    接收进程号
    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;

int raise(int sig)== kill(getpid(),int sig);
    功能:给进程自己发送sig信号    

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

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

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

接收端

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

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

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

-->signal函数第一个参数为信号号数,第二个参数为函数指针(返回值void*,参数为int)

signal函数外面套着的返回值,因为其返回值是一个函数指针(返回值void*,参数为int)

在系统中使用了typedef来定义

-------------------------------------------------------------------------------------------------------------------------

typedef void (*sighandler_t)(int);
     ===》void (*xx)(int); == void fun(int);
     ===》xx是 void fun(int) 类型函数的函数指针
     ===》typedef void(*xx)(int)   sighandler_t; ///错误写法
          typedef int   myint;

===>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     表示自定义处理(自定义函数,也就是函数指针void (*fun)(int))

注:

编写信号注册函数,并测试所有的32个系统信号
        是否能全部被忽略? 如果不能,则找出信号编号。

    结论: 9  19 不能被忽略也不能被自定义(停止和暂停)

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

        }

    2、在所有的信号中有如下两个特列:
        10 SIGUSR1
        12 SIGUSR2
        
        专门预留给程序员使用的未定义信号。

        

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值