Linux环境编程08

本文详细介绍了进程间通信的几种关键方法,如传统管道、XSI进程间的共享内存、消息队列和信号量。以银行系统为例,展示了如何使用这些技术进行账户操作,包括开户、销户、登录、转账等。着重讲解了如何利用vfork+消息队列进行子进程间高效协作。
摘要由CSDN通过智能技术生成

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

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

    有名管道:基于有文件名的管道文件的通信
        编程模型:
            进程A               进程B
            创建管道
            打开管道            打开管道
            写数据              读数据
            关闭管道            关闭管道
            删除管道
        创建管道文件:
            1、mkfifo filename
            2、函数 
            int mkfifo(const char *pathname, mode_t mode);
            功能:创建有名管道文件
            pathname:管道文件路径
            mode:管道文件权限
    
    匿名管道:
        注意:匿名管道只适合fork创建的父子进程之间使用
        int pipe(int pipefd[2]);
        功能:创建一个匿名管道文件,返回管道文件的读权限fd和写权限fd
        pipefd:返回用于存储管道文件读写fd的数组,输出型参数
            pipefd[0] 用于读
            pipefd[1] 用于写
        编程模型: 
            父进程              子进程
            获取一对fd          共享了一对fd
            关闭读              关闭写
            写数据              读数据
            关闭写              关闭读    

XSI进程间通信:
    X/Open公司制定用于进程间通信的系统接口
    XSI进程间通信技术都需要借助系统内核,需要创建内核对象,内核对象会以整数形式返回给用户态,相当于文件描述符,也叫做IPC标识符
    文件的创建打开需要借助文件名,同样的,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    共享内存如果已存在,则返回错误
            获取时直接给0
            mode_flags  创建共享内存时需要提供权限 IPC_CREAT|0644
        返回值:IPC标识符,错误时返回-1

        void *shmat(int shmid, const void *shmaddr, int shmflg);
        功能:让虚拟内存和共享内存进行映射
        shmid:IPC标识符
        shmaddr:想要映射的虚拟内存首地址,为NULL时系统会自动操作
        shmflg:
            SHM_RDONLY:以只读方式映射共享内存
            SHM_RND:只有当shmaddr参数不为NULL时有效,表示对shmaddr参数向下取内存页的整数倍,作为映射地址
            如果都不需要,则写0

        返回值:与共享内存映射后的虚拟内存的首地址,失败返回(void*)-1 或者 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给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;    // 创建共享内存的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 
        创建共享内存            获取共享内存 
        映射共享内存            映射共享内存 
        写数据并通知其它进程     接收到了通知后读数据
        接收到了通知后读数据     写数据并通知其它进程
        取消映射                取消映射
        删除共享内存 

信息队列:
    基本特点:
        由内核维护管理的数据链表,是通过消息类型收发数据
    int msgget(key_t key, int msgflg);
    功能:创建/获取消息队列
    key:IPC键值 
    msgflg:
        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[n];    // 数据
        };
    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    获取消息队列的属性 则buf为输出型参数
        IPC_SET     设置消息队列的属性 则buf为输入型参数
        IPC_RMID    删除消息队列       则buf给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,其它的正在休眠的进程就可以被唤醒

    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 sembuf{
            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    获取信号量的属性 
        IPC_SET     设置信号量的属性 
        IPC_RMID    删除信号量       
        GETALL      获取所有信号量的值
        GETNCNT     获取等待减信号量的进程数量
        GETVAL      通过返回值获取某个信号量的值
        GETZCNT     获取等待信号量为0的进程数量
        SETALL      设置所有的信号量的值
        SETVAL      设置某个信号量的值

        union semun {
            int              val;    // 用于设置信号量的值
            struct semid_ds *buf;    // 获取、设置信号量的属性
            unsigned short  *array;  // 批量获取、设置信号量的值
        };

        
基于进程间通信的银行系统

主要分两个大块:(C_S模型)

客户端(Client):
    1、进入时的功能:开户、销户、登录、解锁
                开户:输入姓名、身份证号、设置密码,如果开户成功,则在服务器端保存一个账号信息的文件(一个账号一个文件,文件名可以用账号)
                销户:输入账号、密码、服务器询问是否确认销户,如果确认则服务器删除该账户的文件
                登录:输入账号、密码,如果三次错误锁定账号
                解锁:输入账号、身份证号,解锁
    2、登录成功:存钱、取钱、转账、查询、修改密码
            存钱:输入金额,存入
            取钱:输入金额,取出
            转账:输入目标账号和转账金额
            查询:不需要输入数据
            修改密码:原密码和新密码

服务端(Server):
    开启服务各项功能的子进程即可:
        各项的子进程按照信息类型来接收消息

    推荐使用的技术:
        vfork + 消息队列 + exec系列函数

账号结构体:
{
    账号
    身份证号
    密码
    金额
    是否锁定
}

CTOS消息结构体:
{
    消息类型 
    账号结构体
    进程号
}

STOC消息结构体:
{
    消息类型 
    提示信息
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值