jackalwb 2020-06-05
本系列文章主要是学习记录Linux下进程间通信的方式。
常用的进程间通信方式:管道、FIFO、消息队列、信号量以及共享存储。
参考文档:《UNIX环境高级编程(第三版)》
参考视频:Linux进程通信  推荐看看,老师讲得很不错
Linux核心版本:2.6.32-431.el6.x86_64
注:本文档只是简单介绍IPC,更详细的内容请查看参考文档和相应视频。
本文介绍利用共享内存进行进程间的通信
struct shmid_ds {
    struct ipc_perm shm_perm;    /* Ownership and permissions */
    size_t          shm_segsz;   /* Size of segment (bytes) */
    time_t          shm_atime;   /* Last attach time */
    time_t          shm_dtime;   /* Last detach time */
    time_t          shm_ctime;   /* Last change time */
    pid_t           shm_cpid;    /* PID of creator */
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); 说明:创建共享内存 返回:成功返回内存中共享内存的的标识ID;失败返回-1; 参数key:用户指定的共享内存键值; 参数size:共享内存大小; 参数shmflg:IPC_CREAT、IPC_EXCL等权限组合。
#include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf); 说明:共享内存控制 参数shmid:共享内存ID; 参数buf:共享内存属性指针; 参数cmd:IPC_STAT:获取共享内存段属性;IPC_SET:设置共享内存段属性; IPC_RMID:删除共享内存段;SHM_LOCK:锁定共享内存段页面(页面映射到屋里内存不和外存进行换入换出操作); SHM_UNLOCK:解除共享内存段页面的锁定。
#include <sys/types.h> #include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg); 说明:共享内存映射 返回:成功返回共享内存映射到进程虚拟内存空间中的地址;失败返回-1; int shmdt(const void *shmaddr); 说明:共享内存解除映射; 返回:如果失败,则返回-1; 参数shmid:共享内存ID; 参数shmaddr:映射到进程虚拟内存空间的地址,建议设置为0,由操作系统分配; 参数shmflg:若shmaddr设置为0,则shmflg也设置为0。SHM_RND:随机;SHMLBA:地址为2的乘方;SHM_RDONLY:只读方式链接。 注:子进程不继承父进程创建的共享内存,大家是共享的,子进程继承父进程映射的地址。
(1)案例一:同步使用共享内存
父进程向共享内存中写入数据,子进程等待父进程写入数据并从中读取数据。对共享进程的同步使用管道实现。
管道的头文件:
#ifndef __TELL_H__ #define __TELL_H__ //管道初始化 extern void init(void); //利用管道进行等待 extern void wait_pipe(void); //利用管道进行通知 extern void notify_pipe(void); //销毁管道 extern void destroy_pipe(void); #endif
管道的C文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tell.h"
static int fd[2];
//管道初始化
void init(void)
{
    if (pipe(fd) < 0) {
        perror("pipe error");
    }
}
//利用管道进行等待
void wait_pipe(void)
{
    char c;
    //管道读写默认是阻塞性的
    if (read(fd[0], &c, 1) < 0) {
        perror("wait pipe error");
    }
}
//利用管道进行通知
void notify_pipe(void)
{
    char c = ‘c‘;
    if (write(fd[1], &c, 1) != 1) {
        perror("notify pipe error");
    }
}
//销毁管道
void destroy_pipe(void)
{
    close(fd[0]);
    close(fd[1]);    
}父子进程对共享进程的访问:
#include <sys/types.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int shmid; 
    if ((shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | IPC_EXCL | 0777)) < 0) {
        perror("shmget error");
        exit(1);
    }
    pid_t pid;
    init();  //初始化管道
    if ((pid = fork()) < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {  //父进程
        int *pi = (int *)shmat(shmid, 0, 0);
        if (pi == (int *)-1) {
            perror("shmat error");
            exit(1);
        }
        //往共享内存中写入数据(通过操作映射的地址即可)
        *pi = 100;
        *(pi + 1) = 20;
        //操作完毕,解除共享内存的映射
        shmdt(pi);
        //通知子进程读取数据
        notify_pipe();
        //销毁管道
        destroy_pipe();
        //等待子进程结束
        wait(0);
    } else {  //子进程
        //子进程阻塞,等待父进程先往内存中写入数据
        wait_pipe();
        //子进程从共享内存中读取数据
        //子进程进行共享内存的映射
        int *pi = (int *)shmat(shmid, 0, 0);
        if (pi == (int *)-1) {
            perror("shmat error");
            exit(1);
        }
        printf("start: %d, end: %d\n", *pi, *(pi+1));
        //读取完毕后解除映射
        shmdt(pi);
        //删除共享内存
        shmctl(shmid, IPC_RMID, NULL);
        //销毁管道
        destroy_pipe();
    }
    return 0;
}测试步骤:
1、编译:[ ipc]# gcc -o bin/cal_shm -Iinclude tell.c cal_shm.c
2、运行:
