进程间通信

一.进程间通信

  1. 进程间通信的目的

    • **数据传输:**一个进程需要将它的数据发送给另一个进程(cat log.txt | grep hello)。
    • **资源共享:**多个进程之间共享同样的资源。
    • **通知事件:**一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
    • **进程控制:**有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
  2. 进程间通信的本质

    • 进程间通信的本质是让 不同的进程看到同一份资源(内存 , 文件,内核缓冲等)

      资源由谁(OS的哪些模块)提供 , 就有了不同的进程间通信方式!

      这里的模块可以是: (文件–管道) , (OS内核IPC提供- SystemV IPC) , (网络–套接字)

    • a.进程运行的时候是具有独立性的!(数据层面) , 因此进程之间要实现通信是非常困难的。
      b.进程间通信,一般一定要借助第三方(OS)资源。
      c.通信的本质就是”数据的拷贝“。

      在这里插入图片描述

    • 参考:CSDN:作者:GSX_M:进程间通信详解(http://t.csdnimg.cn/GWh4X)

  3. 进程间通信分类

    IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

二.无名管道

  1. 无名管道

    管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

  2. 特点:

    • 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。管道数据读被走就没了。
    • 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
    • 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
  3. 函数原型:

    #include <unistd.h>
    int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1
    
  4. 当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:

    在这里插入图片描述

    要关闭管道只需将这两个文件描述符关闭即可。

  5. 示例:

    单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:

    在这里插入图片描述

    若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main()
    {
            int fd[2];//fd[0]read,fd[1]write
            char buf[20] = {"hello world!"};
            char buf2[20] = {0};
            pid_t pid;
            if((pipe(fd)) == -1){
                    printf("pipe create failed!\n");
            }
            pid = fork();
            if(pid < 0){
                    printf("fork create failed!\n");
            }
            if(pid > 0){
                    printf("this is father process\n");
                    close(fd[0]);
                    //ssize_t write(int fd, const void *buf, size_t count);
                    write(fd[1],buf,20);
                    wait();
            }if(pid == 0){
                    printf("this is child process\n");
                    close(fd[1]);
                    //ssize_t read(int fd, void *buf, size_t count);
                    read(fd[0],buf2,20);
                    printf("read:%s\n",buf2);
                    exit(0);
            }
            return 0;
    }
    
    

    FIFO,也称为命名管道,它是一种文件类型。

三.命名管道

FIFO,也称为命名管道,它是一种文件类型。

  1. 特点

    • FIFO可以在无关的进程之间交换数据,与无名管道不同。
    • FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
  2. 原型

    #include <sys/stat.h>
    // 返回值:成功返回0,出错返回-1
    int mkfifo(const char *pathname, mode_t mode);
    

    其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。

    当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

    • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
    • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。
  3. 示例:

    • 创建fifo

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <stdio.h>
      #include <errno.h>
      //int mkfifo(const char *pathname, mode_t mode);
      int main()
      {
              if((mkfifo("./file1",0600))==-1 && errno !=EEXIST){
                      printf("mkfifo failed!\n");
                      perror("why:");
              }
              return 0;
      }
      
    • write函数

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <stdio.h>
      #include <errno.h>
      #include <fcntl.h>
      #include <string.h>
      #include <stdlib.h>
      
      int main()
      {
              int fd;
              int i = 5;
              char *str = "message from write";
      
              if((fd = open("./file1",O_WRONLY)) < 0){
                      printf("open fifo failed!\n");
                      exit(-1);
              }
              while(i){
                      write(fd,str,strlen(str));
                      sleep(1);
                      i--;
              }
              close(fd);
              return 0;
      }
      
    • read函数

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <stdio.h>
      #include <errno.h>
      #include <fcntl.h>
      #include <stdlib.h>
      
      int main()
      {
              int fd;
              char buf[30];
              int nread;
              if((fd = open("./file1",O_RDONLY)) < 0){
                      printf("open fifo failed!\n");
                      exit(-1);
              }
              while((nread = read(fd,buf,30)) > 0){
                      printf("read %d bytes from file1\n",nread);
                      printf("content:%s\n",buf);
              }
              close(fd);
              return 0;
      }
      

四.消息队列

消息队列(message queue),是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

  1. 特点

    • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
    • 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
    • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
  2. 函数原型

    #include <sys/msg.h>
    // 创建或打开消息队列:成功返回队列ID,失败返回-1
    int msgget(key_t key, int flag);
    // 添加消息:成功返回0,失败返回-1
    int msgsnd(int msqid, const void *ptr, size_t size, int flag);
    // 读取消息:成功返回消息数据的长度,失败返回-1
    int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
    // 控制消息队列:成功返回0,失败返回-1
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    

    在以下两种情况下,msgget将创建一个新的消息队列:

    • 如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
    • key参数为IPC_PRIVATE。

    函数msgrcv在读取消息队列时,type参数有下面几种情况:

    • type == 0,返回队列中的第一个消息;
    • type > 0,返回队列中消息类型为 type 的第一个消息;
    • type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。

    可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。

  3. 示例:

    msgrcv

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdlib.h>
    #include <string.h>
    struct msgbuf {//消息队列结构体
            long mtype;       /* message type, must be > 0 */
            char mtext[128];    /* message data */
    };
    int main()
    {
            int msgid;
            struct msgbuf msg_readbuf;
            struct msgbuf msg_sendbuf = {999,"888receive,return999!"};
            key_t key;
            key = ftok(".",'z');//ftok是获得key值函数
            printf("the key is %x\n",key);
            if((msgid = msgget(key,IPC_CREAT|0777)) == -1){
                    printf("create msg_que failed!\n");
                    exit(-1);
            }
            msgrcv(msgid,&msg_readbuf,sizeof(msg_readbuf.mtext),888,0);
            printf("888get msg:%s\n",msg_readbuf.mtext);
    
            msgsnd(msgid,&msg_sendbuf,strlen(msg_sendbuf.mtext),0);
            printf("999send success!\n");
            msgctl(msgid,IPC_RMID,NULL);//清除消息队列
            return 0;
    }
    
    

    msgsnd

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdlib.h>
    #include <string.h>
    struct msgbuf {
            long mtype;       /* message type, must be > 0 */
            char mtext[128];    /* message data */
    };
    int main()
    {
            int msgid;
            struct msgbuf msg_sendbuf = {888,"this is a message from 888!"};
            struct msgbuf msg_readbuf;
            key_t key;
            key = ftok(".",'z');
            printf("the key is %x\n",key);
            if((msgid = msgget(0x1235,IPC_CREAT|0777)) == -1){
                    printf("create msg_que failed!\n");
                    exit(-1);
            }
            msgsnd(msgid,&msg_sendbuf,strlen(msg_sendbuf.mtext),0);
            printf("888send success!\n");
            
            msgrcv(msgid,&msg_readbuf,sizeof(msg_readbuf.mtext),999,0);
            printf("999get msg:%s\n",msg_readbuf.mtext);
            msgctl(msgid,IPC_RMID,NULL);
            return 0;
    }
    
    

五.共享内存

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

  1. 特点

    • 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
    • 因为多个进程可以同时操作,所以需要进行同步。
    • 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
  2. 原型

    #include <sys/shm.h>
    // 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
    int shmget(key_t key, size_t size, int flag);
    // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
    void *shmat(int shm_id, const void *addr, int flag);
    // 断开与共享内存的连接:成功返回0,失败返回-1
    int shmdt(void *addr); 
    // 控制共享内存的相关信息:成功返回0,失败返回-1
    int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
    

    当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。

    当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。

    shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。

    shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。

  3. 示例

    shm_write

    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    //int shmget(key_t key, size_t size, int shmflg);
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //int shmdt(const void *shmaddr);
    
    int main()
    {
            int shmid;
            char *shmaddr;
            key_t key;
            key = ftok(".",1);
    
            if((shmid = shmget(key,1024*4,IPC_CREAT|0666)) == -1){//创建共享内存
                    printf("create shm failed!\n");
                    exit(-1);
            }
            shmaddr = shmat(shmid,0,0);//获取共享内存
            printf("shmat OK!\n");
            strcpy(shmaddr,"this is share memory!");
            sleep(5);
            shmdt(shmaddr);//卸载共享内存
            shmctl(shmid,IPC_RMID,0);//删除共享内存
            printf("quit\n");
            return 0;
    }
    

    shm_read

    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main()
    {
            int shmid;
            char *shmaddr;
            key_t key;
            key = ftok(".",1);
    
            if((shmid = shmget(key,1024*4,0)) == -1){
                    printf("shmid : %d\n",shmid);
                    printf("create shm failed!\n");
                    exit(-1);
            }
            shmaddr = shmat(shmid,0,0);
            printf("shmat OK!\n");
            printf("read from shm : %s\n",shmaddr);//直接读取,打印
            sleep(1);
            shmdt(shmaddr);
            printf("quit!\n");
            return 0;
    }
    

    ipcs -m指令查看共享内存

    在这里插入图片描述

六.Linux信号(Signal)

对于Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为Linux提供了一种处理异步事件的方法。比如,终端用户输入了Ctrl+c来中断程序,会通过信号机制停止一个程序。

  1. 信号的定义和作用

    • 信号的定义:

      信号是 Linux 操作系统中用于进程间通信、处理异常等情况的一种机制。它是由操作系统向一个进程或者线程发送的一种异步通知,用于通知该进程或线程某种事件已经发生,需要做出相应的处理。

    • 信号的作用:

      1. **进程间通信:**进程可以通过向其他进程发送信号的方式进行通信,例如某个进程在完成了某项工作之后,可以向另一个进程发送 SIGUSR1 信号,通知其进行下一步的操作。
      2. **处理异常:**信号可以被用来处理程序中的异常情况,例如当一个进程尝试访问未分配的内存或者除以 0 时,系统会向该进程发送 SIGSEGV 或 SIGFPE 信号,用于处理这些异常情况。
      3. **系统调试:**信号可以用于程序的调试,例如在程序运行时,可以向该进程发送 SIGUSR2 信号,用于打印程序的状态信息等。
  2. 信号的名字和编号

    每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。

    信号定义在signal.h头文件中,信号名都定义为正整数。

    具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。

    在这里插入图片描述

    杀死进程

    ps -aux|grep a.out//检索名字有a.out的进程
    kill -9 16757(pid)// -9 是SIGKILL的代号,加上进程的pid是杀死这个进程
    
  3. 信号的处理

    信号的处理有三种方法,分别是:忽略、捕捉和默认动作

    • 忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
    • 捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
    • 系统默认动作,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。
      具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。
  4. 信号函数——入门版

    • 原型

      //信号注册函数
      #include <signal.h>
      typedef void (*sighandler_t)(int);
      sighandler_t signal(int signum, sighandler_t handler);
      //信号发送函数
      #include <sys/types.h>
      #include <signal.h>
      int kill(pid_t pid, int sig);
      
    • 捕捉信号

      include <stdio.h>
      #include <signal.h>
      
      //typedef void (*sighandler_t)(int);
      //sighandler_t signal(int signum, sighandler_t handler);
      void handler(int signal_num)
      {
              printf("signal_num : %d\n",signal_num);
              switch(signal_num){
                      case 2:
                              printf("SIGINT!\n");
                              break;
                      case 9:
                              printf("SIGKILL!\n");
                              break;
                      case 10:
                              printf("SIGUSR1!\n");
                              break;
              }
              printf("never quit!\n");
      }
      int main()
      {
              signal(SIGINT,handler);
              signal(SIGKILL,handler);
              signal(SIGUSR1,handler);
              while(1);
              return 0;
      }
      
    • kill函数发送指令

      #include <stdio.h>
      #include <signal.h>
      #include <sys/types.h>
      //int kill(pid_t pid, int sig);
      
      int main(int argc,char **argv)
      {
              int signum;
              pid_t pid;
              char cmd[128];
      
              signum = atoi(argv[1]);//atoi:asc码转成int型
              pid = atoi(argv[2]);
              printf("signum:%d,pid:%d\n",signum,pid);
      //      kill(pid,signum);//kill函数发送指令
      
              sprintf(cmd,"kill -%d %d",signum,pid);//组合字符串
              system(cmd);//调用system发送指令
              return 0;
      }
      
    • 忽略指令

      //SIG_IGN宏忽略指令,但是SIGKILL和SIGSTOP不能忽略
      signal(SIGINT,SIG_IGN);
      signal(SIGKILL,SIG_IGN);
      
  5. 信号函数—高级版

    • 我们已经成功完成了信号的收发,那么为什么会有高级版出现呢?其实之前的信号存在一个问题就是,虽然发送和接收到了信号,可是总感觉少些什么,既然都已经把信号发送过去了,为何不能再携带一些数据呢?

      正是如此,我们需要另外的函数来通过信号传递的过程中,携带一些数据。

    • sigaction 的函数原型

      #include <signal.h>
      int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
      
      struct sigaction {
         void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
         void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
         sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
         int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
       };
      //回调函数句柄sa_handler、sa_sigaction只能任选其一
      
    • sigqueue函数原型

      #include <signal.h>
      int sigqueue(pid_t pid, int sig, const union sigval value);
      union sigval {
         int   sival_int;
         void *sival_ptr;
       };
      
    • 接收端

      #include <signal.h>
      #include <stdio.h>
      
      //int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
      //void     (*sa_sigaction)(int, siginfo_t *, void *);
      void handler(int signum, siginfo_t *info, void * context)
      {
              printf("SIGUSR1's signum = %d\n",signum);
              if(context != NULL){
                      printf("send pid = %d\n",info->si_pid);
                      printf("get data = %d\n",info->si_int);
                      printf("get data = %d\n",info->si_value.sival_int);
              }
      }
      int main()
      {
              printf("pid = %d\n",getpid());
              struct sigaction action;
              action.sa_sigaction = handler;
              action.sa_flags = SA_SIGINFO;
      
              sigaction(SIGUSR1,&action,NULL);
              while(1);
              return 0;
      }
      
    • 发送端

      #include <signal.h>
      #include <stdio.h>
      
      //int sigqueue(pid_t pid, int sig, const union sigval value);
      
      int main(int argc,char **argv)
      {
              int signum;
              pid_t pid;
      
              signum = atoi(argv[1]);
              pid = atoi(argv[2]);
              union sigval value;
              value.sival_int = 100;
              sigqueue(pid,signum,value);
              printf("send done!\n");
              return 0;
      }
      

七.信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

  1. 特点

    • 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
    • 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
    • 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
    • 支持信号量组。
  2. 原型

    最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。

    Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

    #include <sys/sem.h>
    // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
    int semget(key_t key, int num_sems, int sem_flags);
    // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
    int semop(int semid, struct sembuf semoparray[], size_t numops);  
    // 控制信号量的相关信息
    int semctl(int semid, int sem_num, int cmd, ...);
    
  3. 示例

    • 创建一个信号量

      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 semid;
              semid = semget(key,1,IPC_CREAT|0666);//创建信号量,“1”代表创建一个
              union semun seminit;//定义一个初始化信号量的联合体
              seminit.val = 1;//定义联合体中信号量的初始值
              semctl(semid,0,SETVAL,seminit);//初始化第“0”个信号量的值为“1”
      //在semctl函数中的命令有多种,这里就说两个常用的:
      /*SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
      IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。*/
      
    • pv操作

      信号量可以理解为一个信号灯,是一种公共资源,当有进程占用时,信号灯为红灯,表示被占用,其他进程不能使用,要在外面等待,此时信号量执行p操作。

      当一个进程完成工作,需要将信号灯置为绿灯,表示其他进程可以使用,此时信号量执行v操作来释放资源。

      信号量为二元信号量时:1为绿灯,0为红灯;p操作是将信号量减一操作,将信号量变为0,v操作是将信号量加一操作,将信号量变为1。

    • 示例

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/sem.h>
      #include <stdio.h>
      #include <stdlib.h>
      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) */
      };
      void sem_p(int semid)//p操作:占用资源,资源如果被占用,则等待
      {
              struct sembuf buf;
              buf.sem_num = 0;
              buf.sem_op = -1;//信号量减一
              buf.sem_flg = SEM_UNDO;
              semop(semid,&buf,1);
              printf("get key!\n");
      }
      void sem_v(int semid)//v操作:释放资源
      {
              struct sembuf buf;
              buf.sem_num = 0;
              buf.sem_op = 1;//信号量加一
              buf.sem_flg = SEM_UNDO;
              semop(semid,&buf,1);
              printf("put the key back!\n");
      }
      int main()
      {
              int semid;
              int pid;
              key_t key;
              key = ftok(".",2);//创建key
      
              semid = semget(key,1,IPC_CREAT|0666);//创建信号量
              union semun seminit;
              seminit.val = 0;
              semctl(semid,0,SETVAL,seminit);//设置信号量初识值0
              if((pid = fork()) == -1){
                      printf("fork error!\n");
                      exit(-1);
              }
              if(pid >0){//因为信号量初识值为0,被占用状态,所以需要等待释放后才能进行父进程操作
                      sem_p(semid);
                      printf("this is father process!\n");
                      sem_v(semid);
                      semctl(semid,0,IPC_RMID);
              }
              if(pid == 0){//父进程堵塞,子进程先运行
                      printf("this is child process!\n");
                      sem_v(semid);//释放资源后,父进程才能运行
              }
              return 0;
      }
      
      
  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值