进程间通讯(IPC)

目录

pipe匿名管道

有名管道

MMAP文件共享映射

关于mmap权限异常

映射内存的位置

关于映射大小问题

mmap偏移映射(处理大文件)


可以利用这种技术完成多个进程间的数据传递,消息收发。

有以下几种方式:匿名管道(pipe),有名管道(fifo),Posix消息队列,SystemV消息队列,MMAP内存共享映射,Signal信号,Socket套接字技术。

绝大多数的进程间通讯技术都是利用内核层实现的(内核层共享内存)。

pipe匿名管道

特点:1.流通性(传输介质)2.方向性3.暂存能力

创建管道,pipe(fds)穿的参数是数组的首地址,int fds[2],创建成功后管道缓冲区是4k环形队列,老版本是64k。创建成功后,系统将读写管道的两个描述符传出到fds中,让用户可以读写使用管道。

所以一般来说fds[0]=3,fds[1]=4,因为0,1,2都被占了。

匿名管道有亲缘限制,只有亲缘进程间可以利用完成进程通信。

管道使用前要确定通信方式,管道要单工使用,让进程关闭无用描述符。(单工:定义通信方向,不可改变,非读即写)。(可调节单工:在一个时刻非读即写,不同时刻可切换)(全双工:可同时读写)。

每一个指向管道描述符都是一个引用计数,当管道引用计数为0,系统会自行释放管道空间。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
    int fds[2];
    pipe(fds);
    pid_t pid;
    pid=fork();
    if(pid>0)
    {
        close(fds[0]);
        printf("father pid %d\n",getpid());
        write(fds[1],"hahahahhahhahha",strlen("hahahahhahhahha"));
        wait(NULL);
        close(fds[1]);
    }else if(pid==0){
        close(fds[1]);
        char buf[1024];
        bzero(buf,sizeof(buf));
        read(fds[0],buf,sizeof(buf));
        printf("child read %s\n",buf);
        close(fds[0]);
        exit(0);
    }else{
        printf("fail\n");
    }
    return 0;
}

管道为空,写端未写数据,读端读取管道,读阻塞。

管道为满,读端未读数据,写端写管道,写阻塞。

写端关闭,如果管道有数据,读端读取完管道剩余数据后再次读返回0,管道为空直接返回0.

管道读端关闭,写端尝试向管道写数据,系统会向写端进程发送SIGPIPE,杀死写端进程。

问题:

编写一个服务端客户模型,可以进行连接和基本的数据收发,客户端异常退出,服务器也异常退出,为什么?和pipe一样。

send(int sockfd,buffer,len,MSG_NOSIGNAL)//如果系统给服务端(写端)发送SIGPIPE信号,可以利用MSG_NOSIGNAL忽略信号。

缺点:亲缘限制,只能亲缘进程可以使用其完成通信。默认情况下管道使用无格式字节流传输,需要用户自行封装。

管道这种进程间通信方式效率好。在所有unix和linux系统,管道都是可以使用的,但是其它系统不一定。

有名管道

mkfifo 测试管道//创建管道文件

管道文件没有存储能力,无法编辑。创建管道文件后,系统会在内核层创建一个管道缓冲区,管道文件被删除,立即清理释放管道缓冲区。

命名管道使用时也是单工方式。匿名管道的四种情况适用于有名管道。

访问命名管道,必须满足两种权限,读写,才可以成功的打开和使用管道,如果只有一种访问权限,会阻塞等待另一种。

如下代码open阻塞了。

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
    int rfd=open("havenamepipe",O_RDONLY);
    char buf[1024];
    bzero(buf,sizeof(buf));
    read(rfd,buf,sizeof(buf));
    close(rfd);
    printf("%s\n",buf);
    return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
q
include<sys/types.h>
#define MEE "hello"
int main()
{

    int wfd=open("havenamepipe",O_WRONLY);
    write(wfd,MEE,strlen(MEE));
    printf("write %d\n",getpid());
    close(wfd);
    return 0;
}

单进程只要满足两种访问权限(O_RDWR),可以直接打开和使用管道。

unlink函数可以删管道,文件。

有名管道使用时的特殊情况:

1.权限要求,满足两种访问权限才可以打开使用管道,否则要阻塞等待另一种。

2.如果在一个进程中有多个读序列,阻塞只会对第一个读序列生效,其他都会设置非阻塞。(多线程为进程读取数据,一个线程阻塞即可,其他线程设置非阻塞立即返回,执行其他任务,避免阻塞开销)。

3.原子使用管道(传输速度较慢,但是数据完整性比较好)和非原子使用管道(传输效率高,无法保证数据完整性)。

原子访问(一次写的大小小于等于管道大小):系统会检测管道余量,如果写端的写大小大于管道余量,系统会挂起写端进程,等管道余量满足写需求大小,再唤醒写端继续写。原子模式可以保证,读写完整,一次收发一个消息包。

非原子访问(每次写的大小大于管道大小):非原子访问情况下,系统不会控制写端,只要管道缓冲区有余量,写端可以立即写入数据,传输效率高,但是读端读取数据后要校验完整性,避免数据包的异常。

不要频繁变更传输模型。

MMAP文件共享映射

void* ptr=mmap(NULL,int Filesize,PROT_READ|PROT_WRITE|PROT_EXEC\PROT_NONE,映射方式,fd,offset);offset传页的大小或页的倍数,Filesize映射文件大小,用户返回的指针类型取决于文件的·数据内容,以及用户想以何种方式访问数据。

munmap(ptr,Filesize)//释放映射内存。

首先进程A调用mmap()创建出一个映射内存,然后加载文件中的内容到映射内存,有两种映射方式:1.私有映射(拷贝映射)2.共享映射(sync同步),然后进程B也mmap(),读取文件中内容。

mmap适合处理大数据文件,比read方式有更小的开销(拷贝开销)

通过映射方式将文件数据加载到进程内存,效率更高,开销更小。(映射文件必须有大小,如果是空文件要扩展文件大小)。

以下代码我们用16进制修改内容。

od -tcx 文件名,查看文件内容的16进制。

代码流程:首先先获得文件描述符fd,然后用lseek计算文件大小Filesize,然后mmap,关闭文件描述符,修改内容,释放映射内存。

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/mman.h>
int main()
{
    int fd=open("MMAPFile",O_RDWR);
    int Filesize=lseek(fd,0,SEEK_END);
    int* ptr=mmap(NULL,Filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(ptr==MAP_FAILED)
    {
        printf("on map error\n");
        exit(0);
    }
    close(fd);
    ptr[0]=0x34333231;
    munmap(ptr,Filesize);
    return 0;
}

映射文件是覆盖使用还是偏移缓存取决于读端如何读取数据。应将文件截断为特定大小(如结构体大小)。

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/mman.h>
typedef struct
{
    int id;
    char name[1024];
}M_ess;
int main()
{
    int fd=open("MMAPFile",O_RDWR);
    ftruncate(fd,sizeof(M_ess));
    M_ess* ptr=mmap(NULL,sizeof(M_ess),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    ptr->id=0;
    bzero(ptr->name,1024);
    close(fd);
    while(1){
    sleep(1);
    sprintf(ptr->name,"zzj %d",ptr->id);
    (ptr->id)++;
    }
    munmap(ptr,sizeof(M_ess));
    return 0;
}
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/mman.h>
typedef struct
{
    int id;
    char name[1024];
}M_ess;
int main()
{
    int fd=open("MMAPFile",O_RDWR);
    M_ess* ptr=mmap(NULL,sizeof(M_ess),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    close(fd);
    while(1){
        sleep(1);
    printf("%s\n",ptr->name);
    }
    munmap(ptr,sizeof(M_ess));
    return 0;
}

进程的互斥锁

pthread_mutexattr_t arr;初始化互斥锁属性

pthread_mutexattr_setpshared设置为进程间共享

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include<string.h>
typedef struct {
        char ans[1024];
        int shared_var;
        pthread_mutex_t mutex;
} SharedData;
void increment(SharedData* data) {
        int i;
        for (i = 0; i < 1000; i++) {
             pthread_mutex_lock(&data->mutex);
             data->shared_var++;
             pthread_mutex_unlock(&data->mutex);
                }
}
int main() {
        int fd = open("gongxiang", O_CREAT | O_RDWR, 0664);
        if (fd < 0) exit(0);
        ftruncate(fd, sizeof(SharedData));
        SharedData* data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (data == MAP_FAILED) {
            perror("mmap\n");
            exit(0);
        }
        data->shared_var = 0;
        bzero(data->ans,1024);
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&data->mutex, &attr);
        pthread_mutexattr_destroy(&attr);
        pid_t pid = fork();
        if (pid > 0) {
                     increment(data);
                     wait(NULL);
                     printf("最终的共享变量值是: %d\n", data->shared_var);
                     sprintf(data->ans,"%d\n",data->shared_var);
                     pthread_mutex_destroy(&data->mutex);
                     munmap(data, sizeof(SharedData));
        }
        else if (pid == 0) {
            increment(data);
            munmap(data,sizeof(SharedData));
                     exit(0);
        }
         else  exit(0);
         return 0;
}

有乱码的原因是文件里有结构体的锁不能直接显示出来不是字符串。

关于mmap权限异常

如果一个权限不足的用户,通过映射方式可以修改文件,那么权限变得不安全,映射权限取决于open打开文件的权限,要小于等于打开权限,否则映射失败,映射是否成功与用户的权限无关而是看open的权限。

映射内存的位置

映射内存在库里申请,不够向堆申请。

关于映射大小问题

当申请出映射内存后,mmap会获取文件长度,来开发内存访问权限,如果文件为0,那么没有任何访问权限,向此内存读写会出现总线错误,即访问越界问题。(段错误(非法操作内存)总线错误(内存越界引发异常)

mmap比read减少一次拷贝开销,mmap可以用于实现零拷贝计数,减少拷贝开销,sendfile更强。

mmap偏移映射(处理大文件)

int i=0;i=i*page;i++;

while(ptr=mmap(NULL,4096,PROT_READ,MAP_PRIVATE,fd,i));

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值