6. 进程间通信

2019-03-28  本文已影响0人  郑行_aover

参考链接:
1. 进程间通信及使用场景
2. 进程间通信机制IPC
3. 看图理解进程间通信IPC==重点
4. 进程间通信和同步


1. 介绍

在linux下有多中进程间的通信方法:

2. 无名管道(PIPE)

应用

int fd[2];
//定义
int* write_fd = &fd[1];
int* read_fd = &fd[0];
//创建
ret = pipe(fd);
//读写
write(*write_fd,string,size);
read(*read_fd,buffer,sizeof(buffer));
//关系
close(*write_fd);
close(*read_fd);

管道的操作是阻塞性质的。

管道读写注意事项

3 .有名管道(FIFO)

FIFO有时被称为命名管道,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的祖先进程。但是,通过FIFO,不相关的进程之间也能交换数据。

FIFO用途

#include <sys/stat.h>
int mkfifo(const char* path, mode_t mode);
int mkfifoat(int fd, const char* path, mode_t mode);

说明

mkfifoat与mkfifo相似,像之前其他at系列函数一样,有3种情形:*

当我们使用mkfifo或者mkfifoat函数创建FIFO时,要用open打开,确是,正常的I/O函数(如close、read、write、unlink)都需要FIFO。当open一个FIFO时,非阻塞标志(O_NONBLOCK)会产生如下影响:

实例

mkfifo fifo1
prog3 < fifo1 &
prog1 < (输入文件) | tee fifo1 | prog2

执行流程如下:

image.png

但是这种类型的FIFO设计有问题,服务器如何回应各个客户进程呢?

  1. 一种解决方法是,每个客户进程都在其请求中包含它的进程ID,然后服务器进程为每个客户进程创建一个FIFO,所使用的路径名是以客户进程的进程ID为基础的。
  2. 例如,服务进程可以用名字/tmp/serv1.XXXXX创建FIFO,其中XXXXX被替换成客户进程的进程ID,如下图所示: image.png

4. 消息队列

linux进程间通信-消息队列
优点:消息队列与管道、FIFO相比,具有更大的灵活性

缺点

4.1 有关消息队列的相关函数
key = ftok(path_ptr,'a');
ipc_id = ipc(MSGGET,(int)key,flags,0,NULL,0);

4.2 消息队列基础理论

消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构体struct msg_queue
来描述。队列头中包含了该消息队列的大量信息,包括消息队列的键值、用户ID、组ID、消息队列中的消息数目等。

struct kern_ipc_perm{
//内核中记录消息队列的全局数据结构 msg_ids能够访问到该结构
    key_t key; //该键值则唯一对应一个消息队列
    key_t uid;
    key_t gid;
    key_t cuid;
    key_t cgid;
    mode_t mode;
    unsigned long seq;
}

读写操作
消息读写操作非常简单,每个消息队列都有如下的数据结构:

struct msgbuf{
    long mtype;
    char mtext[1];
}

获得或者设置消息队列的属性

完整示例代码



#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/msg.h>
#include <unistd.h>

void msg_stat(int ,struct msqid_ds);
int main(int argc, char const *argv[])
{
    int gflags,sflags,rflags;
    key_t key;
    int msgid;
    int reval;
    struct msgbuf
    {
        int mtype;
        char mtext[1];
    }msg_sbuf;

    struct msgbuf
    {
        int mtype;
        char mtext[10];
    }msg_rbuf;

    struct msqid_ds msg_ginfo,msg_sinfo;
    char* msgpath = "/unix/msgqueue";
    key = ftok(msgpath,'a');
    gflags = IPC_CREAT | IPC_EXCL;

    msgid = msgget(key,gflags | 00666);
    if(msgid == -1)
    {
        printf("msg queue creart error\n");
        return;
    }

    //创建消息队列后,输出消息队列的默认属性
    msg_stat(msgid,msg_ginfo);
    sflags = IPC_NOWAIT;
    msg_sbuf.mtype = 10;
    msg_sbuf.mtext[0] = 'a';
    //send msg
    reval = msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);
    if(reval == -1)
    {
        printf("messsge send error\n");
    }
    //发送消息后,输出消息队列属性
    msg_stat(msgid,msg_ginfo);
    rflags = IPC_NOWAIT | MSG_NOERROR;
    reval = msgrcv(msgid,&msg_rbuf,4,10,rflags);
    if(reval == -1){
        printf("read msg error\n");
    }else{
        printf("read from msg queue %d bytes\n",reval );
    }
    //从消息队列中读出消息后,输出消息队列的属性
    msg_stat(msgid,msg_ginfo);
    msg_sinfo.msg_perm.uid = 8;
    msg_sinfo.msg_perm.gid =8;
    msg_sinfo.msg_gbytes = 16388;
    // 此处验证超级用户可以更改消息队列的默认 msg_qbytes
    // 注意这里设置的值大于默认值
    reval = msgctl(msgid,IPC_SET,&msg_sinfo);
    if(reval == -1){
        printf("msg set info error\n");
        return;
    }
    msg_stat(msgid,msg_ginfo);
    // 验证设置消息队列属性
    reval = msgctl(msgid,IPC_RMID,NULL); //删除消息队列?
    if(reval == -1){
        printf("unlink msg queue error\n");
        return;
    }
}

void msg_stat(int msgid,struct msqid_ds msg_info)
{
    int reval;
    sleep(1);
    reval = msgctl(msgid,IPC_STAT,&msg_info);
    if(reval == -1){
        printf("get msg info error\n");
        return;
    }
    printf("\n");
    printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes );
    printf("number of message in queue is :%d\n", msgid_info.msg_qnum );
    printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes );
    //每个消息队列的容量都有限制 MSGMNB,值的大小因系统而异。在创建新的消息队列时,msg_qbytes的默认值就是MSGMNB。
    printf("pid of last msgsnd is %d\n", msg_info.msg_ispid );
    printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid );
    printf("last msgsnd time is  %s\n",ctime(&(msg_info.msg_stime)) );
    printf("last msgrcv time is  %s\n",ctime(&(msg_info.msg_rtime)) );
    printf("last change time is  %s\n",ctime(&(msg_info.msg_ctime)) );

    printf("msg uid is %d\n", msg_info.msg_perm.uid );
    printf("msg gid is %d\n", msg_info.msg_perm.gid);
}

5. 信号量

[高质量嵌入式编程 274页]

信号量主要提供对进程间共享资源访问控制机制。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时进程也可以修改该标志。用于访问控制及进程同步。

使用信号灯

问题

实例
【参看 高质量嵌入式linux c编程 278页】

6. 共享内存

6.1 mmap

使用普通文件提供内存映射

fd = open(name,flag,mode);
if(fd<0)
ptr = mmap(NULL,len,PROT_READ |PROT_WRITE,MAP_SHARED,fd,0);
6.2 父子进程之间通过匿名映射实现共享
typedef struct {
  char name[4];
  int age;
}people;

people* p_map;
p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,-1,0);

为了更安全的通信,通常共享内存需要和信号灯等同步机制共同使用。

上一篇下一篇

猜你喜欢

热点阅读