Jeff的linux环境编程第五天:管道、共享内存、消息队列及信号量(重中之重!)

基本概念:

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

传统进程间通-管道

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

有名管道:基于有文件名的管道文件的通信。

编程模型:
    进程A       进程B
    创建管道    
    打开管道    打开管道
    写数据      读数据
    关闭管道    关闭管道
    删除管道
    创建管道文件:
        1、命令:mkfifo file
        2、函数:
        int mkfifo(const char *pathname, mode_t mode);
        功能:创建管道文件
        pathname:管道文件路径
        mode:管道文件权限
        
        3、标准库函数:
        FILE *popen(const char *command, const char *type);
        功能:打开或创建管道
        command:可执行文件名
        type:
            r 文件指针链接到可执行文件的标准输出
            w 文件指针链接到可执行文件的标准输入 
        注意:适合获读取命令的执行结果。
        int pclose(FILE *stream);
        功能:关闭管道文件专用函数,不能与fclose混用

匿名管道:

    注意:该函数只适合通过fork创建父进程之间使用。
    int pipe(int pipefd[2]);
    功能:创建一个匿名管道文件,返回管道文件的读权限fd和写权限fd
    pipefd:用于存储管道文件fd的数组
    pipefd[0] 用于读 pipefd[1] 用于写

XSI进程间通信:

X/open公司制定用于进程通信的系统接口。
XSI进程间通信都需要借助系统内核,需要创建内核对象,内核对象会以整数形式返回给用户态,相当于文件描述符,也叫IPC标识符。

编程模型:

    父进程          子进程
    获取一对fd
    创建进程        拷贝一对fd
    关闭读          关闭写
    写数据          读数据
    关闭写          关闭读
文件的创建打开需要借助文件名,IPC内核对象创建则需要借助IPC键值(整数),必须要确保IPC错误独一无二。

key_t ftok(const char *pathname, int proj_id);
功能:计算出一个独一无二的IPC键值
pathname:项目路径,不是依靠字符串计算的,而是依靠路径的位置,如果提供假的路径,可能会生成同样的IPC键值。
proj_id:项目编号
返回值:计算出的IPC键值

共享内存:

基本特点:

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

int shmget(key_t key, size_t size, int shmflg);
功能:创建/获取共享内存
key:由进程提供一个独一无二的IPC键值
size:共享内存的大小,获取内存时此参数无意义一般设置为0。
shmflg:
    IPC_CREAT:创建共享内存
    IPC_EXCL:共享内存已经时返回错误
    mode_flags:创建共享内存时需要提供权限
返回值:IPC标识符,错误返回-1。

void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:虚拟内存与共享内存映射
shmid:IPC标识符
shmaddr:想映射的虚拟内存地址,为NULL时系统自动操作。
shmflg:
    SHM_RND:只有shmaddr参数不为NULL有效,表示对shmaddr参数向下取内存页的整数倍,作为映射地址。
    SHM_RDONLY:以只读方式映射共享内存
返回值:与共享内存映射后的虚拟内存地址,失败返回0xFFFFFFFF。

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

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:删除/控制共享内存
shmid:IPC标识符
cmd:
    IPC_STAT 获取共享内存属性,则buf输出型参数
    IPC_SET 设置共享内存属性,则buf是输入型参数
    IPC_RMID 删除共享内存
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;   创建共享内存的IPC键值
        uid_t          uid;     当前使用共享内存的用户ID
        gid_t          gid;     当前使用共享内存的用户组ID
        uid_t          cuid;    创建共享内存的用户ID
        gid_t          cgid;    创建共享内存的用户组ID
        unsigned short mode;    共享内存的权限
        unsigned short __seq;   共享内存序列号
    };

编程模型:

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

消息队列:

基本特点:是由内核负责维护管理的数据链表,通过消息类型收发数据。

编程模型:

    进程A           进程B
    创建消息队列    获取消息队列
    发送消息        接收消息
    接收消息        发送消息
    删除消息队列
int msgget(key_t key, int msgflg);
功能:创建/获取消息队列
key:IPC键值
msgflg:
    IPC_CREAT 创建消息队列
    IPC_EXCL 如果消息队列已经存在则返回错误
    mode_flags:创建消息队列时需要提供权限
返回值:成功返回IPC标识符,错误返回-1。

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列发送数据
msqid:IPC标识符
msgp:要发送的消息的首地址
    struct msgbuf {
        long mtype;     // 消息类型
        char mtext[5];  // 数据
    };
msgsz:数据的字节数,不包含消息类型。
msgflg:
    阻塞一般写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)的消息,如果有多个则读取最小的。
msgflg:
    IPC_NOWAIT 消息类型不符合时不阻塞,立即返回。
    MSG_EXCEPT 如果msgtyp>0,则读取第一个消息类型不是msgtyp的消息。
    MSG_NOERROR 如果不包含些标志,消息实际长度>msgsz,则返回错误不读取数据,若包含此标志,则读取msgsz个字节。
返回值:成功读取到的字节数

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除/获取消息的属性
msqid:IPC标识符
cmd:
    IPC_STAT 获取消息队列的属性
    IPC_SET 设置消息队列的属性
    IPC_RMID 删除消息队列
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
    };

信号量:

基本特点:由内核共享的一个"全局变量",用于记录共享资源的数量,限制进程对共享资源的访问。

    1、若变量的值大于0,说明可以使用资源,需要将变量-1,然后再用。
    2、若变量的值等于0,说明没有资源可以使用,此时进程进入休眠,直到变量大于0,进程被唤醒,执行步骤1。
    3、当资源使用完毕,变量的值+1,正在休眠的进程可以被唤醒。 
int semget(key_t key, int nsems, int semflg);
功能:创建/获取信号量
key:IPC键值
nsems:信号量的数量,一般写1即可。
semflg:
    IPC_CREAT 创建
    IPC_EXCL 排斥
    mode_flags:权限
返回值:IPC标识符,错误返回-1。

int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:对信号量进程加减操作
sops:
    struct sembuf{
        unsigned short sem_num;  信号量的数量
        short          sem_op;   
            1 信号是加1
            -1 如果信号量的值不能 则阻塞
            0 等待信号量的值为零
        short          sem_flg;  
            IPC_NOWAIT 不阻塞
            SEM_UNDO 如果进程终止没有还原信号量,系统将自动还原。
    }
nsops:表示sops指针指向多少个结构体

int semctl(int semid, int semnum, int cmd, union semun arg);
功能:删除/控制信号量
semid:IPC标识符
semnum:信号量的数量
cmd:
    IPC_STAT 获取信号量的属性
    IPC_SET 设置信号量的属性
    IPC_RMID 删除信号量
    GETALL 获取所有信号量的值
    GETNCNT 等待减信号的进程数量
    GETVAL 通过返回值获取某个信号量的值 semnum 就是下标
    GETZCNT 等待信号为0的进程数量
    SETALL 设置所有信号量的值
    SETVAL 设置某个信号量的值
arg:
    union semun {
        int              val;    用于设置信号量的值
        struct semid_ds *buf;    信号量属性
        unsigned short  *array;  批量获取/设置信号量的值
    };
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值