进程间通讯

进程间通讯缘由
由于每个进程都是拥有一个独立的虚拟地址空间的,促使进程独立,致使了进程之间须要协做。
进程间通讯分为ide

  • 数据传输-----管道,消息队列
  • 数据共享-----共享内存
  • 进程控制-----信号量

首先先谈谈管道和共享内存函数

管道---匿名管道,命名管道操作系统

本质:管道其实就是一块内存,是内核当中的缓冲区code

  • 匿名管道---没有标识
    特性:
  1. 具备亲缘关系的进程间通讯
  2. 半双工,数据只能有一个流向
    进程间通讯
  3. 提供字节流服务
  4. 自带同步与互斥功能
  5. 生命周期随进程,进程终止,生命周期消失
  6. 若是管道为空,则读阻塞
  7. 若是管道为满,则写阻塞
  8. 若是管道的读端被关闭,则写端往管道当中写数据的时候,会形成管道破裂,而且致使进程收到SIGPIPE信号,从而进程终止
  9. 若是管道的写端被关闭掉了,则读端读完管道的数据以后,read不会陷入阻塞状态,而是返回。执行代码剩下的正常流程
  10. 管道大小PIPE_SIZE:64K,PIPE_BUG:4K
  11. 若是写入的数据大于PIPE_BUG的话,则不保证写入数据的原子性

    原子性:当前操做不被打断,换句话说,在管道的读写操做是不能够被打断的
    临界资源:同一时间,当前资源只能被一个进程所访问
    互斥:同一时间,保证只能有一个进程访问临界资源
    同步:保证临界资源访问的合理性blog

建立一个管道(亲缘关系的进程中使用)
int pipe(int pipefd[2]);
pipefd[0]:从pipefd[0]文件描述符中去读
pipefd[1]:从pipefd[1]文件描述符中去写
返回值:0(成功) -1(失败)接口

int fd[2];
    int ret = pipe(fd);
    if(ret != 0)
    {
        perror("pipe");//建立错误
        return 0;
    }

    pid_t pid = fork();
    if(pid < 0)
    {
        return 0;//建立失败
    }
    else if(pid == 0)//建立成功
    {
        //child
        sleep(5);
        close(fd[0]);
        write(fd[1], "hehe", 4);
    }
    else
    {
        //father
        close(fd[1]);
        char buf[1024] = {0};
        read(fd[0], buf, sizeof(buf));
        printf("buf = %s\n", buf);
    }

命名管道---有标识的管道
特性:生命周期

  1. 具备标识符,能够知足不一样进程进程进程间通讯
  2. 其余的特性和匿名管道类似
    建立命名管道的方式
    1.使用命令去出建立
    mkfifo+[命名管道文件名称] 建立出来的文件类型是p
    2.使用函数去建立命名管道文件
    mkfifo(char* pathname,mode_t mode)

共享内存
原理:
建立共享内存的时候,首先在物理内存当中建立一块内存,各个进程都经过页表结构将该段内存映射到本身的虚拟内存地址空间上的共享区,各个进程经过映射的地址来进行通讯
特性:
1.共享内存是最快的进程间通讯方式
2.共享内存是不带有同步和互斥功能的
3.写入数据是按照覆盖的方式进行的队列

建立共享内存进程

int shmget(key_t key,size_t size,int shmflg)
key:共享内存标识符
    size:共享内存的大小
    shmflg:1.IPC_CREAT:若是共享内存不存在,则建立共享内存,若是已经存在,则返回
                            2.IPC_CREAT | IPC_EXCL若是共享内存依然存在,则报错,若是不存在则建立
                            3.+权限:按位或权限,权限至关于文件的权限(使用8进制的数字)

返回值:
成功返回共享内存的操做句柄ip

将进程附加到共享内存上

void *shmat(int shmid,const void *shmaddr,int shmflg);
shmid:共享内存的操做句柄
    shmaddr:映射到共享区的地址,通常不选择(填NULL),由操做系统本身映射到虚拟地址空间
    shmflg:0:可读可写        IPC_RDONLY:只读

返回值:
返回映射到的那个地址

分离进程和共享内存

int shmdt(const void *shmaddr);

shmaddr:shmat返回的地址
返回值:
成功返回0,失败返回1
共享内存的销毁

int shmctl(int shmid,int cmd,struct shmid_ds *buf);
shmid:共享内存的操做句柄
    cmd:要使用什么操做
                    IPC_STAT:获取当前共享内存的状态,要搭配shmid_ds一块儿使用
                    IPC_RMID:删除共享内存,标记共享内存为删除状态
    buf:一个出参,结构体当中是共享内存的一些信息

返回值:
失败返回-1

共享内存的生命周期

生命周期跟随操做系统内核
    ipcs -m 查看共享内存
    ipcrm -m [shmid] 删除一个共享内存
若是删除一个有进程的共享内存,操做系统的作法是,先标记当前的共享内存为destroy的状态,而且将key设置为0x00000000,表示当前的共享内存不能再被其余进程所附加,同时会释放内存,也就致使了正在附加到该共享内存上的进程有崩溃的风险,通常禁止这样去操做。当附加的进程退出的时候,操做系统就会将共享内存清除掉

消息队列
消息队列本质上是内核当中的一个优先级队列,进程经过访问优先级队列来增长节点或者查看节点,来进行进程间通讯
特性:生命周期跟随进程,若是用户进程不删除消息队列资源,则资源一直在操做系统内核当中自带同步和互斥功能
接口:msgget/msgsnd/msgrcv/msgct

信号量-----计数器+PCB进程的等待队列
做用:实现进程的控制,也就是实现进程的同步和互斥

如何实现互斥?---PV操做
1.实现互斥的时候,信号量只有两个取值,也就是0或1,0表示当前资源不可用,1表示当前资源可用
2.当进程须要访问一个临界资源的时候,会先访问信号量,预计算信号量的值
先对当前信号进行预减1操做,判断当前信号是否小于0
若是是小于0,则表示信号量以前的值是等于0,则表示当前的临界资源不可用;将当前的进程放到PCB等待队列中去
若是是等于0,则表示信号量以前的值是等于1,则表示当前的临界资源可用;访问临界资源,而且信号量减1,标志着临界资源不能够再被访问了
3.若是访问完成了,则须要进行结束对临界资源的访问,对信号进行加1操做

减1操做:P操做
加1操做:V操做

怎么实现同步?---计数资源的个数前提:信号量的值不在是0或者1了,信号量大于0的时候,表示有多少个资源可使用。当信号量小于0的时候,小于0的数值的绝对值表示多少个进程在等待资源1.若是须要访问该资源的时候,对信号量进行-1,访问资源2.若是不访问该资源的时候,对信号量进行+13.若是当前信号量值是小于0,表示当前PCB等待队列当中还有进程在等待资源,当一个进程结束对该资源的访问的时候,应当唤醒PCB等待队列当中的一个进程,使之去获取资源4.若是当前的信号量值大于0,表示PCB等待队列当中是没有进程在等待的,也就是不牵扯说须要唤醒没某个进程去获取资源

相关文章
相关标签/搜索