• 《嵌入式linux应用程序开发标准教程》笔记——8.进程间通信


    , 

    8.1 概述

      linux里使用较多的进程间通信方式:

    • 管道,pipe和fifo,管道pipe没有实体文件,只能用于具有亲缘关系的进程间通信;有名管道 named pipe,也叫fifo,还允许无亲缘关系进程间通信;
    • 信号,signal,软件模拟中断的机制,很多信号是系统处理的;
    • 消息队列,messge queue,消息的链表。
    • 共享内存,shared memory,容量大,用的比较多,需要额外的同步机制,如互斥锁和信号量
    • 信号量,semaphore,主要用于进程间的同步和互斥
    • 套接字,socket,主要用于网络通信

    8.2 管道

      8.2.1 概述及注意事项

         例如 ps -ef | grep ntp, ps命令的输出,先进入管道(在内核中),grep命令从管道中获取输入数据。

        

       注意事项:

    • 只能用于具有亲缘关系的进程间通信
    • 半双工,读和写端口分开
    • 能用read/write等调用,类似文件,但只存在于内核中
    • 进程创建管道,会创建fds[0]和fds[1],0固定用于读,1固定用于写

    • 子进程继承父进程的管道文件描述符,关闭多余的描述符以后,可以构建父子进程间的通信,兄弟进程也同理。

    •  只有读端存在时,写入才有意义,否则写入端会收到SIGPIPE信号,进程会被终止
    •  缓冲区有空闲,则写入,否则阻塞

      8.2.2 系统调用

    #include <unistd.h>

    int pipe( int fd[2] );
    参数:
      fd[2],管道的文件描述符,0读1写
    返回值:
      成功:0
      出错:-1

      

      8.2.3 实例

    #include <stdio.h> // printf
    #include <stdlib.h> // exit
    #include <unistd.h>
    #include <sys/types.h> // pid_t
    #include <fcntl.h>

    #define PIPE_FD_RD 0
    #define PIPE_FD_WR 1

    int main(int args, char *argv[])
    {
      pid_t pid_fork;
      char buf[32]="pipe data. ";
      int fd[2];

      if( pipe(fd) < 0 )
      {
        printf("pipe() err,exit. ");
        exit(-1);
      }

      pid_fork = fork();
      if( pid_fork < 0 )
      {
        printf("fork err. ");
      }
      else if( pid_fork > 0 ) // father write
      {
        close(fd[PIPE_FD_RD]);
        while(1)
        {
          printf("Father write data. ");
          write(fd[PIPE_FD_WR],buf,strlen(buf)+1);
          sleep(1);
        }
      }
      else // child read
      {
        close(fd[PIPE_FD_WR]);
        memset(buf,0,sizeof(buf));
        while(1)
        {
          if( read(fd[PIPE_FD_RD],buf,32 ) > 0 )
          {
            printf("child read data.");
            printf("%s ",buf);
          }
        }
      }

      exit(0);
    }

      8.2.4 标准流管道 popen/pclose

      标准流把执行另一个程序的流程标准化,简化编程,具体操作如下:

    • 创建一个管道
    • fork一个子进程
    • 在父子进程中关闭不需要的文件描述符
    • 执行exec函数族调用
    • 执行函数中所指定的命令(另一个程序)

      优缺点:

    • 优点:大大减少代码量
    • 缺点:不灵活,只能用标准流读写,不能用read、write这类不带缓冲的IO函数
    #include <stdio.h>

    FILE * popen( const char * command, const char * type );
    参数:
      command:以NULL结尾的字符串,包含shell命令, 会被送到shell中执行。
      type:“r”,读命令的结果,即文件指针连接到命令的标准输出;
         “w”,设置命令的输入,即文件指针连接到命令的标准输入;
    返回值:
      成功:文件流指针
      出错:-1

    int pclose(FILE * stream);
    参数:
      stream:要关闭的文件流
    返回值:
      成功:返回由popen所执行进程的退出码
      出错:-1

    用popen执行ps -ef

    /* 8-2,popen,pclose */

    #include <stdio.h> // printf,popen/pclose
    #include <stdlib.h> // exit
    #include <unistd.h>
    #include <sys/types.h> // pid_t
    #include <fcntl.h>


    int main(int args, char *argv[])
    {
      FILE * pfile;
      char buf[1024];

      printf("start. ");
      pfile = popen("ps -ef","r");
      if(pfile == -1)
      {
        printf("popen return err. ");
        exit(-1);
      }
      while( fgets(buf,1024,pfile) !=NULL)  // 注意fgets的参数及返回值
      {
        printf("%s ",buf);
      }  

      rtn = pclose(pfile);            // 获取ps -ef执行的退出码,有若干宏定义可以检测错误,类似wait函数用到的宏定义
      printf("pclose() return 0x%x. ",rtn);

      exit(0);
    }

      8.2.5 FIFO

    • 有名管道,即FIFO,与普通文件差不多,在文件系统中有实体文件,可以用于互不相关的两个进程实现彼此通信。
    • 不能用lseek()
    • mkfifo()创建有名管道后,可以用open、read、write等函数操作
    • 阻塞问题
      • 读,阻塞打开,若当前FIFO内没有数据,则读进程一直阻塞到有数据写入;
      • 读,非阻塞打开,不管有没有数据,都返回,没有数据时返回0
      • 写,堵塞打开,写操作一直阻塞到可以写入(FIFO有空间了)
      • 写,非阻塞打开,若不能写入全部数据,则写进程进行部分写入或者调用失败
    #include <sys/types>
    #include <sys/state.h>

    int mkfifo( const char * filename, mode_t mode );  // 创建FIFO
    参数: 
      filename,要创建的管道名
      mode,与open的第三个参数mode一样,表示该文件的权限
    返回值:
      成功,0
      出错,-1

    FIFO相关错误总结(errno):
      EACCESS, 参数filename的目录无可执行权限
      EEXIST,参数filename指定的文件已存在
      ENAMETOOLONG,参数filename的路径名称过长
      ENOENT,参数filename包含的目录不存在
      ENOSPC,文件系统的剩余空间不足
      ENOTDIR,目录存在,但不是真正的目录
      EROFS,文件存在于只读文件系统中

      8.2.1 FIFO实例

    /* 8-3,fifo */

    #include <stdio.h> // printf,popen/pclose
    #include <stdlib.h> // exit
    #include <unistd.h>
    #include <sys/types.h> // pid_t
    #include <sys/stat.h>
    #include <fcntl.h>

    void write_fifo( int args, char *argv[] )
    {
      int fd;

      if( args < 2 )
      {
        printf("help: app string_writing_to_fifo. ");
        exit(-1);
      }

      // 创建fifo
      if( access("myfifo",F_OK) < 0)
      {
        if( mkfifo("myfifo",0666)<0 )
        {
          printf("mkfifo error. ");
          exit(-1);
        }
      }

      // open
      if( (fd=open("myfifo",O_WRONLY)) < 0 )
      {
        printf("open fifo err. ");
        exit(-1);
      }

      // write
      if( write(fd,argv[1],128) < 0 )
        printf("write err. ");
    }


    void read_fifo( void )
    {
      int fd;
      char buf[128];

      // open
      if( (fd=open("myfifo",O_RDONLY)) < 0 )
      {
        printf("open fifo err. ");
        exit(-1);
      }

      // read
      if( read(fd,buf,128) < 0 )
        printf("read err. ");
      else
        printf("read from fifo: %s. ",buf);
    }

    int main(int args, char *argv[])
    {

      //write_fifo(args,argv);     // write进程时使用此函数
      read_fifo();            //  read进程是使用此函数
      exit(0);
    }

    $ ./write "hello world"  // 终端1,阻塞,终端2执行read以后接触阻塞,估计是fifo没有读进程时,写不进去

    $ ./read           // 终端2,获取fifo中的数据
    read from fifo: hello world.

    8.3 信号

    8.3.1 信号概述

    • 用软件对中断机制的一种模拟
    • 进程未处于执行态怎么办?  内核暂时保存信号,待进程进入执行态以后再提交信号
    • 可靠信号和不可靠信号
      • 可靠信号支持排队
      • 不可靠信号不支持排队,一个信号已经注册,又来了一个新的相同信号,操作系统直接扔掉,不排队

    • 信号的处理方式
      • 忽略,两个信号不可忽略,SIGKILL/SIGSTOP
      • 捕捉,自定义信号处理函数,信号发生时,执行响应的处理函数
      • 缺省,系统默认动作,大部分是终止进程
    • linux的常用信号:
      • SIGHUP,终止, 终端连接结束时发出
      • SIGINT,终止, Ctrl+C 时发出,终端发送此信号给每个前台进程;
      • SIGQUIT,终止, Ctrl+ 时发出;
      • SIGILL,终止, 进程企图执行非法指令
      • SIGFPE,终止, 致命算数运算错误,例如浮点运算错误、溢出、除数为0等
      • SIGKILL,终止, 结束进程,不能被阻塞、处理或忽略
      • SIGALRM,终止, 定时器到时
      • SIGSTOP,暂停,暂停进程,不能被阻塞、处理或忽略
      • SIGTSTP,停止,交互停止进程,Ctrl+Z
      • SIGCHLD,忽略,子进程改变状态,父进程收到此信号
      • SIGABORT,进程异常终止

    8.3.2 kill()和raise()

    • kill()可以向进程或进程组发送信号
    • 除了SIGKILL外,还可以发送其他信号;
    • raise()进程给自身发送信号;
    #include <signal.h>
    #include <sys/types.h>

    int kill( pid_t pid, int sig );
    参数:
      pid:正数,信号发往的进程号
         0,信号被发往所有与当前进程同一个进程组的进程
         -1,信号发送给所有进程表中的进程
         <-1,信号发送给进程组号为-pid的所有进程
      sig,信号
    返回值:
      成功,0
      出错,-1

    int raise( int sig );
    参数:
      sig,信号
    返回值:
      成功,0
      出错,-1

    /* 8-4,kill,raise */

    #include <stdio.h> // printf,popen/pclose
    #include <stdlib.h> // exit
    #include <unistd.h>
    #include <sys/types.h> // pid_t
    #include <fcntl.h>
    #include <signal.h>


    int main(int args, char *argv[])
    {
      pid_t pid;

      pid = fork();
      if( pid < 0 )
      {
        printf("fork err. ");
      }
      else if( pid>0 ) // 父进程
      {
        sleep(3);
        printf("kill child %d. ",pid);
        kill(pid,SIGKILL);
        printf("father exit. ");
      }
      else // 子进程
      {
        printf("child %d waiting... ",getpid());
        //raise(SIGSTOP);
        while(1)  // 等待被kill
        {
          printf("child alive. ");
          sleep(1);
        }
      }

      exit(0);
    }

    $ ./example

    child 6442 waiting...
    child alive.
    child alive.
    child alive.
    kill child 6442.
    father exit.

    8.3.3 alarm()和pause()

    • alarm()函数是闹钟函数,在进程中设置一个定时器,到时后向进程发送SIGALARM信号;

      【注意】一个进程只能有1个闹钟时间,如果调用alarm()前已经设置过闹钟,则新的闹钟时间取代旧的。

    • pause()将调用进程挂起,直至捕捉到信号为止,常用函数
    #include <unistd.h>

    unsigned int alarm( unsigned int seconds );
    参数:
      seconds,指定定时器秒数,到时后向该进程发送SIGALARM信号
    返回值:
      成功,如果之前调用过alarm()函数,则返回上一个闹钟的剩余时间;否则返回0;
      出错,-1

    int pause( void )
    返回值:-1,并且把errno设置为EINTR.
    【注意】只有执行1个信号处理函数并从其返回时, pause才返回
      

    /* 8-5,alarm/pause */

    #include <stdio.h> // printf,popen/pclose
    #include <stdlib.h> // exit
    #include <unistd.h>
    #include <sys/types.h> // pid_t
    #include <fcntl.h>
    #include <signal.h>


    int main(int args, char *argv[])
    {
      printf("start. finish after 5 seconds. ");
      alarm(5);
      pause();

      printf("finish. "); // 不会执行
      fflush((FILE*)STDOUT_FILENO);

      exit(0);
    }

    $ ./example
    start. finish after 5 seconds.   // 5秒以后结束了
    闹钟

    8.3.3 信号的处理

    • 通过接口注册信号处理函数,信号发生时,就会调用注册的信号处理函数了。
    • signal()简单,sigaction()健壮,推荐使用sigaction()函数。
    #include <signal.h>

    void ( *signal( int signum, void (*handler)(int) ) )(int);
    参数:
      signum,信号
      handler,SIG_IGN,忽略该信号
           SIG_DFL,系统默认方式
           自定义的信号处理函数指针
    返回值:
      成功,以前的信号处理配置
      出错,-1,NULL

    【注意】signal函数形式复杂了,写成如下形式便于理解(就是返回值形式复杂了些):
      typedef void sign( int );
      sign * signal( int , handler *);
      返回值为sign类型,“无返回值并且带一个整型参数的函数”,就是信号的原始配置函数。
      void (*handler)(int),信号处理函数,类似注册一个回调函数,形参int是具体的信号值


    int sigaction( int signum, const struct sigaction * act, struct sigaction * oldact );
    参数:
      signum,信号,除了SIGKILL和SIGSTOP
      act,指定对特定信号的处理。
      oldact,保存原来对相应信号的处理
    返回值:
      成功,0
      出错,-1

    【sigaction结构体】
    struct sigaction
    {
      void (*sa_handler)(int signo);    // 自定义信号处理函数指针/SIG_DEL/SIG_IGN
      sigset_t sa_mask;            // 信号集,指定在信号处理程序执行过程中哪些信号应当被屏蔽
      int sa_flags;              // 标志位,包含若干对信号进行处理的各个选择项
      void (*sa_restore)(void);       //
    }

    sa_flags:
    SA_NODEFER/SA_NOMASK,当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动屏蔽此信号
    SA_NOCLDSTOP,进程忽略子进程产生的任何SIGSTOP/SIGTSTP/SIGTTI/SIGTTOU信号
    SA_RESTART,令重启的系统调用起作用
    SA_ONESHOT/SA_RESETHAND,自定义信号只执行1次,执行完毕后恢复信号的系统默认动作

    /* 8-6,signal */

    #include <stdio.h> // printf,popen/pclose
    #include <stdlib.h> // exit
    #include <unistd.h>
    #include <sys/types.h> // pid_t
    #include <fcntl.h>
    #include <signal.h>


    void fun_sigal( int sig_no )
    {
      if( sig_no == SIGINT )
        printf("Signal SIGINT ");
      else if( sig_no == SIGQUIT )
        printf("Signal SIGQUIT ");
      else
        printf("Other SIGQUIT ");
    }

    int main(int args, char *argv[])
    {
      printf("start. ");

      signal(SIGINT,fun_sigal);
      signal(SIGQUIT,fun_sigal);

      pause(); // 从信号处理函数返回
      pause(); // 再等待一次
      printf("finish. ");

      exit(0);
    }

    $ ./example
    start.
    ^CSignal SIGINT
    ^CSignal SIGINT    // 相同信号,多次发生的情况
    finish.

    $ ./example
    start.
    ^CSignal SIGINT
    ^Signal SIGQUIT
    finish.

    /* 8-7,sigaction */
    
    #include <stdio.h>    // printf,popen/pclose
    #include <stdlib.h>    // exit
    #include <unistd.h>
    #include <sys/types.h>    // pid_t
    #include <fcntl.h>
    #include <signal.h>
    
    
    void fun_sigal( int sig_no )
    {
        if( sig_no == SIGINT )
            printf("Signal SIGINT
    ");
        else if( sig_no == SIGQUIT )
            printf("Signal SIGQUIT
    ");
        else
            printf("Other SIGQUIT
    ");
    }
    
    int main(int args, char *argv[])
    {
        struct sigaction st_sigact;
    
        printf("start. 
    ");
        
        st_sigact.sa_handler = fun_sigal;
        st_sigact.sa_flags=0;
        sigemptyset(&st_sigact.sa_mask);
    
        sigaction(SIGINT,&st_sigact,NULL);
        sigaction(SIGQUIT,&st_sigact,NULL);
    
        pause();    // 从信号处理函数返回
        pause();    // 再等待一次
        printf("finish. 
    ");
    
        exit(0);
    }

    【执行效果与signal例程相同】

    8.3.4 信号集

      后续补充吧。

    8.4 信号量

    8.4.1 信号量概述

    • 不同进程有可能争抢共享资源
    • 信号量用来解决进程之间的同步和互斥,信号量包括
      • 信号量变量,代表当前可用的该资源的数量,>0表示资源可用, <=0表示无可用资源;一般信号量只有0/1,叫二维信号量。
      • 在该信号量下等待资源的进程等待队列
      • 对信号量的两个原子操作,PV
        • P操作,占用资源,-1,如果没有可用资源,则被阻塞,直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该资源
        • V操作,释放资源,+1,如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程

    创建信号量,semget(),不同进程用相同键值获取同一个信号量
    初始化,semctl(),SETVAL
    PV操作,semop()
    删除,semctl(), IPC_RMID

    8.4.2 函数

    #include <sys/types>
    #include <sys/ipc.h>
    #include <sys/sem.h>

    int semget( key_t key, int nsems, int semflg );    // 获取或创建信号量
    参数:  
      key,信号量的键值,多个进程通过同一个键值访问同一个信号量;IPC_PRIVATE用于创建本进程的私有信号量;
      nsems,需创建的信号量数目,通常取值为1
      semflg,类似open函数的flg+mode的集合,其中权限与open相同,也可用八进制表示;IPC_CREAT,创建,已存在也不出错;IPC_EXCL,如果创建时已经存在,则返回错误;
    返回值:
      成功,信号量标识符,类似文件描述符
      出错,-1

    int semctl( int semid, int semnum, int cmd, union semun arg );
    参数:
      semid,信号量标识符
      semnum,信号量编号,一般为0,使用信号量集时才有用
      cmd,指定各种操作,常用如下:
        IPC_STAT,获取信号量的状态结构体semid_ds,并存放在第四个参数arg的buf中。semid_ds是系统中描述信号量的数据结构;
        IPC_SET,将信号量的值设置为arg的val
        IPC_GET,返回信号量的当前值
        IPC_RMID,删除信号量
      arg,union semun结构,在有些系统里需要自己定义,linux里需要自己定义
        union semun
        {
          int val;
          struct semid_ds * buf;
          unsigned short * array;
        }
    返回值:
      成功:IPC_STAT/IPC_SETVAL/IPC_RMID时,返回0
         IPC_GETVAL时返回信号量当前值
      出错:-1

    int semop( int semid, struct sembuf * sops, size_t nsops );
    参数:
      semid,信号量标识符
      sops,指向信号量操作结构体数组
        struct sembuf
        {
          short sem_num;  // 信号量编号,使用单个信号量时,通常取值为0
          short sem_op;   // -1为P操作,+1为V操作
          short sem_flg;  // 通常设置为SEM_UNDO,这样进程没有释放信号量就退出时,系统自动释放该信号量
        }
      nsops,该数组的操作个数,通常取1
    返回值:
      成功,0
      出错,-1

    8.4.3 例程

    /* 8-8,sem */
    
    #include <stdio.h>    // printf,popen/pclose
    #include <stdlib.h>    // exit
    #include <unistd.h>
    #include <sys/types.h>    // pid_t
    #include <fcntl.h>
    #include <sys/sem.h>
    #include <sys/ipc.h>
    
    union semun
    {
        int val;
        struct semid_ds * buf;
        unsigned short * array;
    };
    
    void sem_init( int semid, int val )
    {
        union semun arg;
    
        arg.val = val;
        semctl( semid, 0, IPC_SET, arg);
    }
    
    void sem_p( int semid)
    {
        struct sembuf st_sem_buf;
        
        st_sem_buf.sem_num = 0;
        st_sem_buf.sem_op = -1;
        st_sem_buf.sem_flg = SEM_UNDO;
        semop( semid, &st_sem_buf ,1 );
    }
    
    void sem_v( int semid )
    {
        struct sembuf st_sem_buf;
        
        st_sem_buf.sem_num = 0;
        st_sem_buf.sem_op = 1;
        st_sem_buf.sem_flg = SEM_UNDO;
        semop( semid, &st_sem_buf ,1 );
    }
    
    void sem_del( int semid )
    {
        union semun arg;
    
        semctl( semid, 0, IPC_RMID, arg);    
    }
    
    
    int main(int args, char *argv[])
    {
        pid_t pid;    
        key_t key;
        int semid;
    
        printf("start. 
    ");
        
        // creat sem
        key = ftok(".",0);
        if( (semid=semget(key,1,0666|IPC_CREAT)) < 0 )
        {
            printf("semget err.
    ");
            exit(-1);
        }
        
        sem_init(semid,0);        
    
        pid = fork();
        if( pid > 0 )    // 父进程
        {
            sleep(5);
            printf("father process.
    ");
            sem_v(semid);
        }    
        else if( pid == 0 )
        {
            sem_p(semid);
            printf("child process.
    ");
            sem_v(semid);
        }
        else
        {
            printf("fork err.
    ");
            exit(-1);
        }
    
        printf("finish. 
    ");
    
        exit(0);
    }

    $ ./example
    start.           // 5秒后后面打印信息出现
    father process.    
    finish.
    child process.
    finish.

    【注意】此例用sem实现父子进程顺序执行,属于同步的例子。 sem也可以实现互斥操作。

    8.5 共享内存

    8.5.1 概述

    • 内核有专门的内存区用于进程间共享,进程可以将其映射到自己的私有地址空间后访问。
    • 数据读写高效,不需要额外复制,但是需要额外的同步和互斥机制
    • 【注意】  linux命令ipcs可以查看共享内存、消息队列的各种进程间通信机制的情况

    8.5.2 函数

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>

    int shmget( key_t key, int size, int shmflg );  // 从内核中获取共享内存
    参数:
      key,共享内存的键值,与信号量函数中的键值相同
      size,共享内存大小
      shmflg,同open的权限位,可用8进制表示
    返回值:
      成功:共享内存段标识符
      出错,-1

    int shmat( int shmid, const void * shmaddr, int shmflg );  // 映射从内核中获取的共享内存,映射已后进程就可以使用了
    参数:
      shmid,共享内存标识符
      shmaddr,将共享内存映射到指定地址,若为0,则系统自动分配
      shmflg,SHM_RDONLY,只读;默认0,可读写
    返回值:
      成功,被映射的段地址
      出错,-1

    int shmdt( const void * shmaddr );
    参数:
      shmaddr,被映射的共享内存段地址
    返回值:
      成功,0
      出错,-1

    8.5.3 例程

    /* 8-9,shm */
    
    #include <stdio.h>    // printf,popen/pclose
    #include <stdlib.h>    // exit
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>    // pid_t
    #include <fcntl.h>
    //#include <sys/sem.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    
    int main(int args, char *argv[])
    {
        pid_t pid;    
        int shmid;
        char * shm_addr;
        char buf[128];
        char flg[6]="write";
    
        printf("start. 
    ");
        
        // creat sem
        if( (shmid=shmget(IPC_PRIVATE,128,0666)) < 0 )
        {
            printf("semget err.
    ");
            exit(-1);
        }
        else
            printf("semget ok,shmid %d
    ",shmid);
    
        system("ipcs -m");
        
        pid = fork();
        if( pid > 0 )    // 父进程
        {
            if( (shm_addr=(char*)shmat(shmid,0,0) ) == ((void*)-1) )    
            {    
                printf("shmat err in father process,rtn 0x%x
    ",shm_addr);
                exit(-1);
            }
            else
                printf("shmat addr 0x%x in father process.
    ",shm_addr);
    
            sleep(1);
            printf("input something in father.
    ");
            fgets(buf,128,stdin);    
            strncpy(shm_addr,flg,strlen(flg));
            strncpy(shm_addr+strlen(flg),buf,strlen(buf));        
    
            printf("father finish.write data:%s 
    ",shm_addr);
        }    
        else if( pid == 0 )
        {
            printf("child pid %d
    ",getpid());
            if( (shm_addr=(char*)shmat(shmid,0,0) ) == ((void*)-1) )    
            {    
                printf("shmat err in child process,rtn 0x%x
    ",shm_addr);
                exit(-1);
            }
            else
                printf("shmat addr 0x%x in child process.
    ",shm_addr);    
    
            printf("
    child read shm:%s 
    ",shm_addr);
            while(strncmp(flg,shm_addr,strlen(flg)) != 0)
            {
                sleep(1);
                printf("
    child read shm:%s 
    ",shm_addr);
            }
            printf("rev from father process:%s
    ",shm_addr+strlen(flg));
    
            shmdt(shm_addr);
            printf("child finish. 
    ");
        }
        else
        {
            printf("fork err.
    ");
            exit(-1);
        }
    
        exit(0);
    }

    【运行结果】

    shmat addr 0xb3cad000 in father process.
    child pid 10449
    shmat addr 0xb3cad000 in child process.

    
    

    child read shm:
    input something in father

    
    

    child read shm:
    12
    father finish.write data:write12

    $
    child read shm:write12

    rev from father process:12

    child finish.



    8.6 消息队列

    8.6.1 概述

    • 消息队列是消息的列表,用户可以添加和读取消息
    • 比FIFO功能多一些,用户可以随机查询消息
    • 消息存在于内核中,由“队列ID”标识
    • 有点类似通信类接口,以包为单位

    8.6.2 函数

    • msgget(),创建或打开消息队列
    • msgsnd(), 添加消息队列
    • msgrcv(),读取消息队列,可以指定取走某一条消息
    • msgctl(), 控制消息队列
    #include <sys/types>
    #include <sys/ipc.h>
    #include <sys/shm.h>

    int msgget( key_t key, int msgflg );  
    参数:
      key, 消息队列键值,多进程共享时使用,特殊键值IPC_PRIVATE表示当前进程的私有消息队列
      msgflg, 权限标志位
    返回值:
      成功,消息队列ID
      出错,-1

    int msgsnd( int msqid, const void * msgp, size_t msgsz, int msgflg );
    参数:
      msqid,消息队列ID
      msgp,指向消息结构的指针,该结构通常为
        struct msgbuf
        {
          long mtype;    // 消息类型,同一个消息队列,可以有多种消息类型,接收时可以按类型接收,这点比较方便
          char mtext[1];  // 消息正文
        }
        【注意】该结构为模板,实际使用时需要自己根据数据量进行定义
      msgsz,消息正文的字节数,不包括消息类型
      msgflg,IPC_NOWAIT,不阻塞;0,阻塞直到发送成功为止
    返回值:
      成功,0
      出错,-1

    int msgrcv( int msqid, void * msgp, size_t msgsz, long int msgtyp, int msgflg );
    参数:
      msqid,消息队列ID
      msgsz,消息缓冲区,同msgsnd的msgp
      msgtyp, 0,接收消息队列中的第一个消息,不管类型了
           大于0,接收第一个类型为msgtyp的消息
           小于0,接收第一个类型“不小于msgtyp绝对值”且“类型值最小”的消息
      msgflg,MSG_NOERROR,若返回的消息比msgsz字节多,则消息就会截短到msgsz字节,且不通知消息发送进程
          IPC_NOWAIT,不阻塞
         0,阻塞,直到收到一条消息为止
    返回值:
      成功,0
      出错,-1

    int msgctl( int msqid, int cmd, struct msqid_ds * buf );
    参数:
      msqid,消息队列的队列ID
      cmd,IPC_STAT,读取消息队列的数据结构msqid_ds,并将其保存在buf中
         IPC_SET,设置消息队列数据结构msqid_ds中的ipc_perm域(IPC操作权限描述结构),该值取自buf
         IPC_RMID,删除消息队列
      buf,描述消息队列的msqid_ds结构类型变量
    返回值:
      成功,0
      出错,-1

    8.6.3 例程

  • 相关阅读:
    fixture中使用request参数
    python 使用values() 获取字典中所有的值
    php连接mssql数据库的方法以及错误设置
    C#使用tabcontrol控件可操作多窗体嵌入以及双击窗口名称可关闭
    ie下js调试超级爽的工具IE WebDeveloper V2 (附注册码)
    php curl 下载文件
    www.qian14.cn 终于解决了这个问题
    二级缓存伪优化揭秘(转)
    向访客和爬虫显示不同的内容
    我的新博客地址!
  • 原文地址:https://www.cnblogs.com/liuwanpeng/p/6631833.html
Copyright © 2020-2023  润新知