UNIX/Linux系统命令(5)——进程间通信

一.基本概念
什么是进程间通信(IPC,Interprocess communication):是指两个或多个进程之间交换数据的过程叫进程间通信
进程之间为什么需要通信?
当需要多个进程协同工作高效率完成任务时,因为每个进程都是一个独立的个体(资源单位),进程之间就需要进行通信
进程间通信方式:
1.简单进程间通信:命令行参数,环境变量表,信号,文件
2.传统进程间通信:管道
3.XSI进程间通信:共享内存,消息队列,信号量
x-openx
si-信息接口
4.网络进程间通信:socket

二.传统的进程通信
管道是UNIX系统是最古老的进程间通信方式(基本不再使用),历史上的管道通常是半双工(只允许单向数据流动),现在的系统大都可以全双工,数据可以双向流动

    1.有名管道(创建实体文件)
       命令:mkfifo
       函数: int mkfifo(const char *pathname, mode_t mode);
               功能:创建管道文件
               pathname:文件路径
               mode:权限
               返回值:成功返回0,失败返回-1
               
               编程模型:
                进程A            				进程b
                创建管道(mkfifo)          		...   
                打开管道(open)          		打开管道
                读/写管道(read/write)/写数据
                关闭管道(close)          		关闭管道
                删除管道(unlink)          		...
    2.无名管道(用于用于fork()创建的父子进程通信)
        int pipe(int pipefd[2]);
        功能:创建无名管道
        pipefd:用来存储内核返回的文件描述符
             pipefd[0]    用来读操作
             pipefd[1]    用来写操作
         返回值:成功返回0,失败返回-1    

三.XSI进程间通信
X/open组织为UNIX系统设计的一套进程间通信机制,有共享内存,信息队列,信号量.
1.IPC标识
内核会为每个XSI的进程间通信维护一个IPC对象(XSI对象)
该对象通过一个非负整数来引用(类似于文件描述符).
与文件描述符不同的是,每用一个IPC对象标识符就持续+1,达到最大值时再从0开始
IPC标识需要程序员自己创建(类似于创建文件)
2.IPC键值
创建IPC键值的依据(类似创建文件的文件名),也是一个非负整数
1.自定义(不建议,可能会冲突)
2.自动生成(项目路径,项目编号)
key_t ftok(const char *pathname, int proj_id);
注意:项目路径一定要是有效路径,生成IPC键依靠的是路径而不是字符串
3.IPC对象的创建用到的宏
IPC_PRIVATE 创建IPC对象时永远创建成功
IPC_CREAT 对象存在则获取,不存在则创建
IPC_ECXL 如果对象已经存在,则创建失败
4.IPC对象销毁/控制用到的宏
IPC_STAT 获取IPC对象的属性
IPC_SET 设置IPC对象的属性
IPC_RMID 删除IPC对象
四.共享内存
在内核中开辟一块内存由IPC对象管理,进程A和进程B都用自己的虚拟内存与它映射,这样条目就共享了同一块内存,然后它们就可以通信了
特点:
1.不需要复制信息,是最快的一种进程间通信机制
2.需要考虑同步问题(必须借助其他的机制,如信号)

   编程模型:
     进程A                            	进程B
     生成IPC键值       ftok           	生成IPC键值       ftok 
     创建共享内存       shmget          获取共享内存       shmget
     映射共享内存       shmat           映射共享内存       shmat
     使用共享内存       *ptr            使用共享内存       *ptr
     取消映射        	shmdt           取消映射          shmdt
     删除共享内存		shmctl          ...
     
     int shmget(key_t key, size_t size, int shmflg);
     功能:创建/获取共享内存
     key:IPC键,由ftok函数生成
     size:共享内存的大小,最好是4096的整数倍,获取共享内存时,此值无效
     shmflg:
           0             获取共享内存
           IPC_CREAT     创建
           IPC_ECXL      如果存在,则创建失败
      返回值:成功返回共享内存标识(IPC标识),失败返回-1
           
     void *shmat(int shmid, const void *shmaddr, int shmflg);
     功能:映射共享内存
     shmid:共享内存标识符,shmget函数的返回值
     shmaddr:进程提供的虚拟地址,与内核中的内存映射,也可以是NULL(,内核会自动选择一个地址映射)
     shmflg:
           0             自动选择一个地址映射 
           SHM_RDONLY    只读权限
           SHM_RND       当shmaddr不为空时shmaddr向下取整页
      返回值:映射成功后的虚拟地址
           
     int shmdt(const void *shmaddr);
     功能:取消虚拟内存与共享内存的映射
     shmaddr:被映射过的虚拟地址
     
     int shmctl(int shmid, int cmd, struct shmid_ds *buf);
     功能:删除共享内存,获取/设置共享内存的属性
     shmid:共享内存标识符,shmget的返回值
     cmd:
        IPC_STAT      获取共享内存的属性
        IPC_SET       设置共享内存的属性
        IPC_RMID      删除共享内存
         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 */创建者进程ID
             pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */最后映射/取消映射的进程ID
             shmatt_t        shm_nattch;  /* No. of current attaches */映射的次数
             ...
         };
        struct ipc_perm {
             key_t          __key;    /* Key supplied to shmget(2) */IPC键值
             uid_t          uid;      /* Effective UID of owner */有效用户
             gid_t          gid;      /* Effective GID of owner */有效用户组
             uid_t          cuid;     /* Effective UID of creator */创建者的用户ID
             gid_t          cgid;     /* Effective GID of creator */创建者组ID
             unsigned short mode;     /* Permissions + SHM_DEST and SHM_LOCKED flags */权限
             unsigned short __seq;    /* Sequence number */IPC标识
         };

五.消息队列
信息队列就是由内核负责管理的一个管道,可以按顺序发送信息包(消息类型+消息内容),可以全双工工作


   int msgget(key_t key, int msgflg);
     功能:创建/获取消息队列
     key:IPC键值,由ftok函数自动生成
     msgflg:
           0 获取消息队列
           IPC_CREAT 创建消息队列
           IPC_EXCL  如果存在则创建失败
           返回值:消息队列标识
           
           
     int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
     功能:向消息队列发送消息
     mspid:消息队列标识,msgget函数的返回值
     msgp:结构指针
         struct msgbuf {
               long mtype;       /* message type, must be > 0 */消息类型
               char mtext[n];    /* message data */消息内容
           };
     msgsz:消息的长度,不包括消息类型sizeof(msgbuf)-sizeof(mtype)
     msgflg:
           0  阻塞,当消息队列满时,等待
IPC_NOWAIT 1  不阻塞,当消息队列满时,不等待
          
          
     ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
     功能:从信息队列按类型获取消息
     mspid:消息队列标识,msgget函数的返回值
     msgp:结构指针
         struct msgbuf {
               long mtype;       /* message type, must be > 0 */消息类型
               char mtext[n];    /* message data */消息内容
           };
     msgsz:要接收的消息的长度,可以长一点
     msgtyp:要接收的消息类型
            0 接收任意类型的消息(接收队列中第一个消息)
           >0 只接收msgtyp类型的消息
           <0 接收消息队列中小于等于msgtyp绝对值的消息,取小的那个
     msgflg:
          0  阻塞,消息队列中是否有对应类型的消息,没有则等待
          1  不阻塞,消息队列中是否有对应类型的消息,没有则等待
    
       MSG_NOERROR
          1.消息类型正确,而消息的实际长度大于msgsz,则不接收消息并返回-1
          2.如果msgflg带MSG_NOERROR标志,则把多余的消息截取成功接收
     IPC_NOWAIT:如果消息队列没有要接收的消息,则不等待,返回-1
     MSG_EXCEPT:接收消息队列中的第一个消息类型不是mstyp的消息,编译时添加-D_GNU_SOURCE参数
     返回值:成功接收到的消息字节数
     
     
     int msgctl(int msqid, int cmd, struct msqid_ds *buf);
     功能:删除消息队列,或设置消息队列
     mspid:消息队列标识,msgget函数的返回值
     cmd:
          IPC_STAT      获取消息队列的属性
          IPC_SET       设置消息队列的属性
          IPC_RMID      删除消息队列
       struct msqid_ds {
               struct ipc_perm msg_perm;     /* Ownership and permissions */权限
               time_t          msg_stime;    /* Time of last msgsnd(2) */最后一条消息的发送时间
               time_t          msg_rtime;    /* Time of last msgrcv(2) */最后一次接收消息时间
               time_t          msg_ctime;    /* Time of last change */最后一次修改时间
               unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */消息队列中的字节数
               msgqnum_t       msg_qnum;     /* Current number of messages in queue */消息队列中消息的个数
               msglen_t        msg_qbytes;   /* Maximum number of bytes allowed in queue */消息队列中容纳的最大字节数
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */最后一次发送消息的进程
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */最后一次接收消息的进程
           };

      返回值:成功返回0,失败返回-1

六.信号量
内核维护的计数量,用于管理多进程之间共享资源.
例如:有个变量n表示资源的数量,当有进程想要独占一个资源时,n的值要减1(可能减多个),如果n的值等于0(不够减),则进程阻塞,
直到n的值可以减再被唤醒,当资源使用完毕后n要加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:信号量的数量
       semflg:
             	0 			获取信号量
             IPC_CREAT		创建信号量(以存在则获取,不存在则创建)
             IPC_EXCL		如果已存在则创建失败
       返回值:信号量的标识      
       
       int semop(int semid, struct sembuf *sops, unsigned nsops);
       功能:操作信号量(对信号量进行加/减操作)
       semid:信号量的标识,semget的返回值
       sops:结构体数组
       nsops:数组长度
 	   struct sembuf{
 	       unsigned short sem_num;  /* semaphore number */         信号量的下标
           short          sem_op;   /* semaphore operation */      操作
           short          sem_flg;  /* operation flags */          标记
           } 
           sem_flg:
           IPC_NOWAIT  当信号量不够减的时候不阻塞
           SEM_UNDO    当进程结束时,信号量的值自动归还
   
       int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);
       功能:带时间限制的操作信号量
       struct timespec{
             			time_t tv_sec    ;long   tv_nsec   ;纳秒
       };
       
       int semctl(int semid, int semnum, int cmd, ...);
       功能:删除信号量,获取,设置信号量的属性,初始化信号量 
       cmd:
           IPC_STAT   获取信号量的属性
           IPC_SET 	  获取信号量的属性
           IPC_RMID	  删除信号量  
            struct ipc_perm {
               key_t          __key; /* Key supplied to semget(2) */
               uid_t          uid;   /* Effective UID of owner */
               gid_t          gid;   /* Effective GID of owner */
               uid_t          cuid;  /* Effective UID of creator */
               gid_t          cgid;  /* Effective GID of creator */
               unsigned short mode;  /* Permissions */
               unsigned short __seq; /* Sequence number */
           };


            struct  seminfo{
            
            } 
           IPC_INFO   获取信号量的信息
           SEM_INFO	  设置信号量的信息
           GETALL	  获取所有信号量的值
           GETNCNT 	  获取信号量的数量
           GETVAL	  获取某个信号量的值
           SETALL	  设置所有信号量的值
           SETVAL	  设置某个信号量的值
             union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           };

           
       编程模型:
       进程A                    		  进程B
       创建信号量			semget        获取信号量 
	   初始化信号量			semctl        ....../减信号量			semop         加减信号量
       删除信号量			semctl        ......
     注意:信号量是用来计数的,一定要与资源对应

七.IPC命令
显示IPC对象
ipcs -m
ipcs -q
ipcs -s
ipcs -a
删除IPC对象
ipcrm -m ID
ipcrm -q ID
ipcrm -s ID

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值