【IO进程】进程间通信

【1】进程间通信概述


每一个进程虽然独立,但也需要让不同的进程实现数据的传输、还有信号通知

通信方式:
传统的进程间通信:
    无名管道、有名管道  -->  数据传输
    信号                -》  异步通知

系统5(System V)通信方式:

    共享内存、消息队列          --》  数据传输
    信号量集                   --》  同步和互斥

网络套接字通信
    不同的电脑不同的进程之间实现通信

不同的进程间如何实现通信?
    在操作系统的所运行的那段空间(内核空间3-4g),开辟一款缓冲区

【2】无名管道


 是操作系统在内核空间所开辟的一块内存区域。内存区域有一定的大小,大小为64K

 无名管道:就是在对当前文件系统找不到对应的名字,对应的操作函数会给你返回两个描述符,一个代表管道的读操作,一个代表管道的写操作 

 特点:只能在父子进程或者兄弟进程或者子孙进程之间实现通信,数据传输方向的单向,是一个半双工的通信方式。


 int  pipe(int pipdfd[2]);
  功能:在内核空间开间一个内存区域用来实现进程间通信
  参数:
    pipefd[0]   代表管道的读端,也就是用来从pipefd[0]读取数据
    pipefd[1]   代表管道的写端,也就是用来从pipefd[1]写入数据
  返回值:
        成功  0
        失败  -1

  当管道中没有数据的时候,执行读操作,read函数会产生阻塞
  如果管道有 数据则依照读取的数据个数进行read操作     

  往管道中写数据,如果写满管道64k。则write会产生阻塞,只有读操作读取数据
  管道的剩余空间大于4k时,write才能继续写数据     

【3】有名管道


  有名管道:可以在文件系统下找到某个文件,对文件对打开,执行相应的读写操作,内部实现与无名管道相同,文件中仅保存inode编号,指向内存中的区域。

    int mkfifo(const char *pathname, mode_t mode);    

       功能:创建有名管道,用来实现不同的进程间通信
       参数:
          pathname:  指定管道文件的名字,文件类型是p,以后通过打开管道文件
                  往有名管道中写入数据、或者读出数据
          mode:      文件权限(mode & ~umask)
       返回值:
            成功  0
            失败  -1
  有名管道与无名管道的使用方式相同,只需使用open系统调用获取文件描述符即可。

【4】信号


实现进程跟进程之间的通知,依据信号通知,具体执行什么操作,决定于信号的默认操作
但是信号有三种相应(操作)方式:
  (1)默认,执行系统设定的操作方式
  (2)忽略,进程接收到信号之后,不做任何操作
  (3)自定义操作:需要自己在进程中定义一个信号处理函数,执行对应信号操作

    kill -l
常用默认信号的操作方式:
 2) SIGINT     ctrl+c         结束进程
 3) SIGQUIT    ctrl+\         结束进程
 9) SIGKILL                   结束进程  
10) SIGUSR1
12) SIGUSR2   用户自定义信号   结束进程   
13) SIGPIPE                   管道破裂,结束进程
14) SIGALRM                   闹钟信号,结束进程
17) SIGCHLD                   子进程状态改变,会向父进程发送这个信号
18) SIGCONT                   让暂停的进程恢复运行
20) SIGTSTP                   暂停进程
19) SIGSTOP                   暂停进程


【注意】9 和 19这两个信号的默认操作方式不能被改变




kill(pid_t pid, int sig) -->  kill(1000, SIGKILL)


int raise(int sig);

功能:  都是用来发送信号
    pid     进程的pid号
    sig     信号种类中的一种
成功     0


unsigned int seconds alarm(unsigned int seconds)
功能:  用来设置闹钟,本身不具有阻塞作用
参数  senonds   设置闹钟秒数
返回值:
    之前没有设置闹钟,返回值是0
    之前有设置过闹钟,则返回之前闹钟剩余的时间值

    typedef void (*sighandler_t)(int); --> sighandler_t 

    typedef别名重定义,sighandler_t表示函数指针类型

   sighandler_t signal(int signum, sighandler_t handler);

功能:信号处理函数,首先需要向内核注册信号signum,注册完之后,signal本身不具有阻塞作用,会继续往后执行
    handler   用户自定操作,也就是用户需要定义一个函数   void handler(int),这是函数的类型,形参表示的是内核向你这个进程发送的信号是什么?


    void  handler(int signo)
    {
        if(signo == SIGKILL)
        {
            处理操作,实现功能,需要用户自己定义
        }
        if(signo == SIGINT)
        {
            处理操作
        }
    }

【5】System V 进程间通信对象

共享内存、消息队列、信号量集


也是在内核当中去创建,创建好之后,一直保留系统当中,
如果不用操作内核对象了,需要手动的删除

a、【删除】内核对象命令
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ]

                  举例:ipcs  -m  id      --》查看共享内存

b、在程序中【查看】内核对象   

int system(const char *command);
参数  :
       conmand  shell命令
       举例:system(“ls -l”)

内核对象操作流程: 

1、创建key值
2、创建内核对象
3、操作内核对象,具体操作,依据内核对象
4、删除内核对象

key: 让进城找到内核对象。(共享内存、消息队列、信号量集)
  IPC_PRIVATE   当前进程创建,只允许当前进程使用   0x00000000
  ftok          当一个进程创建好之后,其他的进程可以通过这个key值,找到内核对象


#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
   功能:创建一个key值,让不同的进程找到内核对象
   参数:
      pathname  文件名(必须存在,并且可以访问),取得是文件的inode节点号
      proj_id   整形变量,值得范围是(1~255)
   返回值:
        成功   key
        失败   -1

【6】共享内存


    共享内存:是在内核空间中的一块区域,需要设定,大小也有用户自己定义,以字节为单位进行分配,并且是连续的地址空间,在所有通信当中,效率最高,可以直接方位内存空间的地址

    #include <sys/shm.h>

    int shmget(key_t key, size_t size, int shmflg);
    功能:  创建共享内存
    参数:
          key   让不同的进程找到内核对象
          size   共享内存的大小是多少? 以字节为单位
          shmflg  
                IPC_CREAT   表示要创建共享内存
                IPC_EXCL    如果已经创建,则会判断是否重复创建
                mode_flags  指定9位有效权限位    0666
                举例:      IPC_CREAT  |   IPC_EXCL   |   0666 
    返回值:
            成功  shmid,用来对内和对象(共享内存)操作的变量
            失败  -1


    void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:   将内核空间的内存地址映射到调用的用户空间
    参数: 
        shmid   通过shmget创建或者打开的共享内存
        shmaddr  NULL  表示系统自动选择合适的未使用的是给给用户空间
        shmflg  
                0   表示可读可写
                SHM_RDONLY  表示只读
   返回值:
        成功   映射后的地址,需要进行强制类型转换
        失败   (void *)-1



   int shmdt(const void *shmaddr);
   功能:解除映射
   参数:  
        shmaddr   映射后的地址(指针变量)
   返回值:
        成功 0 
        失败 -1

   int shmctl(int shmid, int cmd, struct shmid_ds *buf);
   功能:共享内存控制函数
   参数:
         shmid    表示共享内存
         cmd   
                                  IPC_RMID      IPC_STAT

         buf    信息结构体         NULL         struct shmid_ds(定义变量取地址)   
   返回值:
         成功  0
         失败  -1

   【注意】     
   在访问共享内存时,有可能出现多个进程共同访问的现象,造成数据的混乱或者说是不一致,需要使用信号量或者互斥锁来保护共享资源      

【7】信号量集


信号量集:信号量的集合,代表一类资源,每一类资源都有数量,需要用户自己设定
创建信号量的数目,需要用户事先指定,并初始化设置每一个信号量的数量值同样也需要进行P(申请)V(释放)操作。


int semget(key_t key, int nsems, int semflg);
功能:创建信号量集合,里面可以包含多个信号量,具体数量自己指定
参数:
      key   为了方便其他进程找到信号量集合
      nsems 指定信号量集合中信号量的数目是多少个?
            1     集合有一个信号量    0
            2     集合有两个信号量    0  1
      semflg  
            IPC_CREAT   创建信号量
            IPC_EXCL    防止重复创建
            0666        权限
返回值:
      成功   操作标识符(操作信号量用的)
      失败   -1  


int semctl(int semid, int semnum, int cmd, ...);
功能:信号量的控制操作函数
参数:
      semid      代表你要操作的信号量集合
      sennum     表示你要操作第几个信号量 
      cmd  
           SETVAL   用来设置每一个信号量的数量是多少个?
           IPC_RMID   用来删除信号量集合
      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) */
      };





int semop(int semid, struct sembuf *sops, unsigned nsops);      
功能:信号量的pv操作

参数:
     semid  操作那个信号量集合
     sops   如何对信号量进行操作 
            unsigned short sem_num; 对第几个信号量进行操作/* semaphore number */
            short sem_op;    执行什么操作P(正数)  V(负数)/* semaphore operation */
            short sem_flg;   设置阻塞还是非阻塞方式  0阻塞  IPC_NOWAIT非阻塞/* operation flags */

     nsops  表示连续操作几个信号量
返回值:
      成功  0            
      失败  -1

【8】消息队列


(1)消息队列机制

    特点:
        1、先进先出 
        2、按照类型发送、读取消息
    使用ipcs -q查看系统中的消息

(2)如何创建消息队列

    int msgget(key_t key, int msgflg);
    功能:创建或者打开消息队列
    参数:
        key:让不同的进程找到同一个消息队列
        msgflg: 
            IPC_CREAT 创建
            IPC_EXCL  防止重复创建
            0666      权限

        msqid:消息队列标识符
(3)发送消息/接收消息
    结构体1:
        struct msgbuf 
        {
               long mtype;       /* message type, must be > 0 */消息类型
               char mtext[1];    /* message data */消息正文
        };


    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    功能:发送消息到消息队列
    参数:
        msgid: 消息队列标示符  (用于操作消息队列发送或者接收消息)
        msgp:首先定义一个消息队列的结构体,类型如上所示结构体1
        msgsz:消息队列正文(text)的大小
        msgflg:设置为阻塞的方式0  非阻塞的方式 IPC_NOWAIT

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                  int msgflg);
    功能:从队列接收消息
    参数:
        msgid: 消息队列标示符  (用于操作消息队列发送或者接收消息)
        msgp:首先定义一个消息队列的结构体,类型如上所示结构体1
        msgsz:消息队列正文(text)的大小
        msgtyp:消息的类型(必须是大于0的整数)
        msgflg:设置为阻塞的方式0  非阻塞的方式 IPC_NOWAIT

(4)删除消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:操作消息队列
参数:
    msgid: 消息队列标示符  (用于操作消息队列发送或者接收消息)
    cmd:操作消息队列的命令   
        IPC_STAT   获取消息队列属性信息的命令,需要定义一个struct msqid_ds结构体
        IPC_RMID   删除消息队列的命令    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值