Linuxc学习笔记-进程间的通信

本文详细介绍了Linux系统中的进程间通信方式,包括信号、管道、共享内存、消息队列和信号量的概念与使用。重点讲解了管道的半双工与全双工特性,以及XSI进程通信中的共享内存、消息队列的创建与管理,强调了信号量在资源同步中的关键作用。
摘要由CSDN通过智能技术生成

进程间通信
基本概念:
什么是进程通信:
是指两个或多个进程之间交互数据的过程,是因为进程之间是相互独立的,
为了协同工作的需要必须要交互数据
进程间通信的分类:
简单的进程间通信:信号、文件、环境变量、命令行参数
传统的进程间的通信:管道文件
XSI进程间通信: 共享内存、消息队列、信号量
网络进程间通信: 套接字Socket

传统进程间通信—管道(FIFO):
管道是UNIX系统中最古老的进程间的通信,古老意味着所有系统都支持,早期的管道文件都是半双工,
现有的一些系统的管道是全双工的
管道是一种特殊的文件,它的数据在文件中是流动的,读取之后就会消失,如果文件中没有任何数据,读取时
就会阻塞

有名管道:基于有文件名的管道文件的通信
    编程模型:
        进程A               进程B
        创建管道            
        打开管道文件        打开管道文件
        写数据              读数据
        关闭管道            关闭管道
        删除管道

    创建管道文件:
        1、mkfifo filename 
        2、函数
        #include <sys/types.h>
        #include <sys/stat.h>
        int mkfifo(const char *pathname, mode_t mode);
        功能:创建有名的管道文件
        path:管道文件的路径
    
    匿名管道:
        注意:匿名管道只适合通过fork创建的父子进程之间使用
        #include <unistd.h>
        int pipe(int pipefd[2]);
        功能:创建一个匿名管道文件,返回管道文件的读取权限fd和写权限fd
        pipefd:返回用于存储管道文件读写fd的数组,输出型参数
            pipefd:返回用于存储管道文件读写的fd的数组,输出型参数
                pipefd[0] 用于读
                pipefd[1] 用于写
        
        编程模型:
            父进程          子进程
            获取一对fd      共享一对fd
            关闭读          关闭写
            写数据          读数据
            关闭写          关闭读

XSI进程通信:
X/Open公司制定用于进程间通信的系统接口
XSI进程间通信技术都需要借助系统内核,需要创建内核对象,内核对象会以整数形式返回给
用户态,相当于文件描述符,也叫做IPC标志符
文件的创建打开都需要借助文件名,同样的,IPC内核对象创建需要借助IPC键值(整数),必须
要确保IPC键值是独一无二的
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:计算出一个独一无二的ipc键值
pathname:项目路径,不是依靠字符串计算,而是依靠路径的位置以及项目编号计算的,所以不能
提供假的路径,否则可能会产生同样的ipc键值
proj_id:项目编号
返回值:计算出来的ipc键值

共享内存:
基本特点:
两个或者多个进程之间共享一块由内核负责维护的内存,该内存可以与多个进程的虚拟内存进行映射
优点:不需要复制信息,是一种最快的IPC通信机制
缺点:需要考虑同步访问的问题,一般借助信号来解决

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:创建/获取共享内存
key:由进程提供的一个独一无二的IPC键值
size:共享内存的大小,获取时该参数无意义,一般给0
shmflag:
    IPC_CREAT:创建共享内存
    IPC_EXCL:共享内存如果已存在,则返回错误
    获取时直接给0
    mode_flags 创建共享内存时需要提供权限 IPC_CREAT|0644
    返回值:IPC标识符,错误时返回-1

include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:让虚拟内存与共享内存进行映射
shmid:IPC标识符 
shmaddr:想要映射的虚拟内存首地址,为NULL时系统会自动操作
shmflag:
    SHM_RDONLY:以只读方式映射共享内存
    SHM_RND:   只有当shmaddr不为NULL时有效,表示shmaddr参数向下取内存页的整倍数,作为映射地址
    如果都不需要,则写0
返回值:与共享内存映射后的虚拟内存的首地址,失败返回(void*)-1,或者0xFFFFFFFF

int shmdt(const void *shmaddr);
功能:取消映射 
shmaddr:映射过的映射内存首地址

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:删除/控制共享内存
shmid:IPC标识符
cmd:
    IPC_STAT    获取共享内存的属性,则buf为输出型参数
    IPC_SET     设置共享内存的属性,则buf为输入型参数
    IPC_RMID    删除共享内存,      则直接给NULL

buf:
    struct shmid_ds {
        struct ipc_perm shm_perm;    //所有者的相关信息
        size_t          shm_segsz;   //共享内存字节数
        time_t          shm_atime;   //最后映射的时间
        time_t          shm_dtime;   //最后取消映射的时间
        time_t          shm_ctime;   //最后改变的时间
        pid_t           shm_cpid;    //创建者的进程号
        pid_t           shm_lpid;    //最后映射、取消映射者的进程号
        shmatt_t        shm_nattch;  //当前映射的次数
        ...
    };

    struct ipc_perm {
           key_t          __key;    //创建共享内存的键值
           uid_t          uid;      // 当前使用共享内存的用户ID
           gid_t          gid;      //当前使用共享内存的组ID
           uid_t          cuid;     //创建共享内存的用户ID
           gid_t          cgid;     //创建共享内存的组ID
           unsigned short mode;     //共享内存的权限
           unsigned short __seq;    //共享内存的序列号
    };

编程模型:
    进程A                   进程B
    创建共享内存                获取共享内存
    映射共享内存                映射共享内存
    写数据并通知进程其他进程     接收到通知后读取数据
    接收到通知后读数据          写数据并通知进程其他进程
    取消映射                    取消映射
    删除共享内存

消息队列:
基本特点:
由内核维护管理的数据链表,是通过消息的类型收发数据
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:创建/获取消息队列
key:IPC键值
magflag:
IPC_CREAT 创建消息队列
IPC_EXCL 如果消息队列已存在则返回错误
mode: 当创建消息队列时需要提供权限
返回值:成功返回IPC标识符,失败返回-1

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列发送数据
msqid:IPC标识符
msgp:要发送的消息的首地址
    struct msgbuf 
    {
       long mtype;       //消息类型
       char mtext[1];    //数据
       
    };
msgsz:数据的字节数,是不包括消息类型的
msgflag:
    阻塞一般给0
    IPC_NOWAIT  当消息队列满时,不等待立即返回

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列中读取数据
msqid:IPC标识符
msgp:存储数据结构体首地址
msgsz:数据结构体的字节数
msgtyp:消息的类型,是按照类型的值来读取消息,而不是按照顺序
    >0  读取消息队列中第一条等于msgtyp的消息
    =0  读取消息队列中的第一条消息
    <0  读取消息类型小于abs(msgtyp)的消息,如果有多个满足则读取值最小的
msgflag:
    IPC_NOWAIT  消息类型不符合时不阻塞,立即返回
    IPC_EXCEPT  如果msgtyp>0,则读取第一个类型消息类型不是msgtyp的消息
    MSG_NOERROR 如果不包含此标记,则消息的实际长度>msgsiz,则会返回错误,
    并且返回失败,如果包含此标记,则最多读取msgsiz个字节,确保一定成功
返回值:成功读取到的字节数

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除/控制消息队列
msgqid:IPC标识符
cmd:
    IPC_STAT    获取消息队列的属性,则buf为输出型参数
    IPC_SET     设置消息队列属性,则buf为输入型参数
    IPC_RMID    删除消息队列      则直接给NULL
buf:
    struct msqid_ds 
    {
        struct ipc_perm msg_perm;     //属主消息
        time_t          msg_stime;    //最后发送时间
        time_t          msg_rtime;    //最后接收时间
        time_t          msg_ctime;    //最后修改时间
        unsigned long   __msg_cbytes; //当前消息队列的字节数                                         
        msgqnum_t       msg_qnum;     //当前消息的数量
        msglen_t        msg_qbytes;   //消息的最大字节数                                         
        pid_t           msg_lspid;    //最后发送者的pid
        pid_t           msg_lrpid;    //最后接收者的pid
    };

编程模型:
    进程A               进程B
    创建消息队列         获取消息队列
    发送消息             接收信息
    接收信息             发送信息
    删除消息队列

信号量:
基础概念:
由内核维护并共享一个“全局变量”,用于记录共享资源的数量,限制进程对共享资源的访问,
信号量是一种数据操作锁,本身是不具备数据交换功能,而是通过控制其它的通信资源来实现
进程间协调通信
1、若该变量的值大于0时,说明可以使用资源,说明可以使用资源,需要将信号量-1,再使用资源
2、若信号量的值等于0时,说明没有资源可以使用,此时进程会进入休眠,直到信号量的值大于0,
进程就会自动唤醒,执行步骤1
3、当资源使用完毕,需要将信号量的值+1,其他的正在休眠的进程就可以被唤醒

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:创建/获取信号量
key:IPC键值
nsems:信号量的数量,一般给1即可
semflg:
    IPC_CREAT   创建信号量
    IPC_EXCL    如果信号量已存在则返回错误
    mode:       当创建信号量时需要提供权限
返回值:IPC标识符,错误返回-1

int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:对信号量进行加减操作
semid:IPC标识符,代表了要操作的信号量
sops:
    struct
    {
        unsigned short sem_num;  //信号量的下标 从0开始
        short          sem_op;
                        //1     信号量+1
                        //-1    如果信号量的值不能-1,则阻塞
                        //0     等待信号量的值为0
        short          sem_flg;
                        //IPC_NOWAIT  不阻塞
                        //SEM_UNDO  如果进程终止没有还原信号量,系统会自动还原
    }
nsops:代表sops指针执行多少个结构体,一般写1

int semctl(int semid, int semnum, int cmd, ...);
功能:删除/控制信号量
semid:IPC标识符
semnum:第几个信号量,下标从0开始
cmd:
    IPC_STAT    获取信号量的属性,则buf为输出型参数
    IPC_SET     设置信号量属性,则buf为输入型参数
    IPC_RMID    删除信号量      则buf直接给NULL
    GETALL      获取所有信号量的值
    GETCNT      获取等待减信号量的进程数量
    GETVAL      通过返回值获取某个信号量的值
    GETZCNT     获取等待信号量为0的进程数量
    SETALL      设置所有信号量的值
    SETVAL      设置某个信号量的值

    union semun 
    {
        int              val;    //用于设置信号量的值
        struct semid_ds *buf;    //获取、设置信号量的属性
        unsigned short  *array;  //批量获取、设置信号量的值
    };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值