0807|IO进程线程day9 IPC对象概念及示例(消息队列、共享内存、信号灯集)

本文介绍了Linux下的IPC(进程间通信)机制,包括消息队列、共享内存和信号灯集。详细阐述了它们的概念、原理、特点,还介绍了相关函数的功能、原型、参数及返回值,并给出了代码示例和作业练习,帮助理解进程间通信的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 0 什么是IPC机制

一、消息队列(message queue)

1.1 消息队列的概念

1) 消息队列的原理

2) 消息队列的特点

3) 查看消息队列

1.2 消息队列的函数

1) ftok【计算键值】

2) msgget【通过key在内核内存中找到对应的消息队列,并返回队列id】

3) msgsnd【打包数据发送到队列】

练习:将数据发送至消息队列中,当类型位0时停止输入

4) msgrcv【从消息队列中读取数据】

代码示例:

i. msgtyp == 0

ii. msgtyp > 0

iii. msgtyp

5) msgctl【控制消息队列,用于删除消息队列】

常用示例:

6) 作业

二、共享内存(shared memory)

2.1 共享内存的概念

1) 共享内存的原理

​2) 共享内存的特点

3) 查看共享内存

2.2 共享内存的函数

1) ftok【计算键值】

2) shmget【通过key在内核内存中找到对应的消息队列,并返回队列id】

3) shmat【将共享内存映射到用户空间中】

4) shmdt【断开映射】

5) shmctl【控制共享内存,常用于删除共享内存】

6) 常用示例

7) 函数使用示例

写入

读取

结果

8) 作业

三、信号灯集(semaphore)

3.1 信号灯集的概念

1)信号灯集的原理

i)信号灯的查看:ipcs ipcs -s ipcrm -s semid

2)信号灯集的核心操作

3.2 信号灯集的函数

1)ftok

2)semget

3)semop

4)semctl

5)代码示例


 0 什么是IPC机制

概念:        

        IPC机制:Inter Process Communication,即进程间通信机制。

        进程与进程间的用户空间相互独立,内核空间共享。所以如果要实现进程间的通信,需要使用进程间通信机制。

分类(3类):

  1. 传统的进程间通信机制
        无名管道  pipe
        有名管道  fifo
        信号      signal
  2. system v操作系统的IPC对象
        消息队列  message queue
        共享内存  shared memory
        信号灯集  semaphore
  3. 可用于跨主机传输的通信
        套接字  socket

一、消息队列(message queue)

1.1 消息队列的概念

1) 消息队列的原理

   消息队列是在内核中创建一个容器(队列),进程需要将数据打包成结点,添加到队尾。或者从队列中读取结点,实现进程间通信。

2) 消息队列的特点

  1. 消息队列是面向记录的,其中消息具有特定的格式以及优先级。
  2. 消息队列总体上是根据先进先出的原则来实现读取的,也可以选择消息的类型后,按照先进先出的原则读取。
  3. 消息队列独立于进程。等进程结束后,消息队列以及其中的内容不会消失,依然存在,除非手动删除,或者重启操作系统。

3) 查看消息队列

查看消息队列: ipcs
                          ipcs -q
              
删除消息队列:ipcrm -q  msqid

1.2 消息队列的函数

1) ftok【计算键值】

功能:

        ① 该函数通过pathname提供的id,以及proj_id提供的8bit的值,计算key值(键值),给msgget shmget semget函数使用.

        ② 只要pathname和proj_id一致,则计算的key值就一致。那么通过相同key值找到的IPC对象就是同一个。

原型:

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

       key_t ftok(const char *pathname, int proj_id);

参数:

    char *pathname:文件的路径以及名字; 该文件必须存在且可访问
    int proj_id:传入一个非0参数;

返回值:

        成功,返回计算得到的key值;

        失败,返回-1,更新errno;

2) msgget【通过key在内核内存中找到对应的消息队列,并返回队列id】

功能:通过key值到内核内存中找对应的消息队列,并返回消息队列的id--->msqid;

原型:

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

       int msgget(key_t key, int msgflg);

参数:

    key_t keyftok函数返回出来的key值;
    int msgflg

  • IPC_CREAT:若消息队列不存在,则创建消息队列。若消息队列存在,则忽略该选项;
  • IPC_CREAT|0664:创建的同时指定消息队列的权限。
  • IPC_CREAT|IPC_EXCL:若消息队列不存在,则创建消息队列。若消息队列存在,则报错;

返回值:

        >=0, 成功返回消息队列的id号 msqid;

        =-1, 函数运行失败,更新errno;

3) msgsnd【打包数据发送到队列】

功能:将数据打包后发送到消息队列中;

原型:

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

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

    int msqid   :指定要发送到哪个消息队列中;
    void *msgp:指定要发送的消息包的首地址;

    通用格式如下:
    struct msgbuf 
    {
        long mtype;       /* message type, must be > 0 */    消息类型,必须大于0;
        char mtext[1];    /* message data */  消息内容,类型根据需求修改,想要发什么类型就填什么类型。
                                              大小与下一个参数msgsz指定的一致
    };   

    size_t msgsz:消息内容的大小,以字节为单位。   
    int msgflg

  • 0:阻塞方式发送,当消息队列满了,则当前函数阻塞;
  • IPC_NOWAIT:非阻塞方式,当消息队列满了,该函数不阻塞,且函数运行失败,errno == EAGAIN.

返回值:

        =0, 函数运行成功;

        =-1, 函数运行失败,更新errno;

练习:将数据发送至消息队列中,当类型位0时停止输入

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct msgbuf
{
    long mtype;
    char mtext[128];
};

int main(int argc, const char *argv[])
{
    //创建消息队列
    //创建key值
    key_t key =ftok("/home/ubuntu/IO/05_IPC/04_msg/",1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建消息队列
    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    printf("msqid = %d\n",msqid);

    struct msgbuf sndbuf;
    while(1)
    {
        printf("请输入消息类型 >>> ");                                        
        scanf("%ld",&sndbuf.mtype);
        getchar();
        if(0 == sndbuf.mtype)
            break;

        printf("请输入消息内容 >>> ");
        fgets(sndbuf.mtext,sizeof(sndbuf.mtext),stdin);
        sndbuf.mtext[strlen(sndbuf.mtext)-1]=0;

        //向消息队列中发送消息
        if(msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtext),0) < 0)
        {
            perror("msgsnd");
            return -1;
        }
        printf("发送成功\n");
        system("ipcs -q");//让c代码执行shell命令
    }


    return 0;
}
                                                                              
                                                                              

4) msgrcv【从消息队列中读取数据】

功能:从消息队列中读取数据;

原型:

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
       
       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
 int msgflg);

参数:

    int msqid   :指定要发送到哪个消息队列中;
    void *msgp:指定要发送的消息包的首地址;

    通用格式如下:
    struct msgbuf 
    {
        long mtype;       /* message type, must be > 0 */    消息类型,必须大于0;
        char mtext[1];    /* message data */  消息内容,类型根据需求修改,想要发什么类型就填什么类型。
                                              大小与下一个参数msgsz指定的一致
    };   


    size_t msgsz:消息内容的大小,以字节为单位。   
    long msgtyp :指定要读取的消息类型;

    msgtyp == 0, 读取消息队列中的第一条消息; 先进先出;
    msgtyp > 0,  指定消息类型读取,读取消息队列中第一条消息类型为 msgtyp参数指定的消息;
                 msgflg指定了MSG_EXCEPT,读取消息队列中第一条消息类型 不等于 msgtyp参数指定的消息;
                 vi -t MSG_EXCEPT;   #define MSG_EXCEPT      020000 
    msgtyp < 0,  读取消息队列中第一条最小的,且类型小于等于 msgtyp参数绝对值的消息。         

   int msgflg

        0:阻塞方式,当消息队列中没有消息了,该函数阻塞; 

        IPC_NOWAIT:非阻塞方式运行,当消息队列中没有消息了,该函数不阻塞,运行失败,errno == ENOMSG;

返回值:

        >0, 成功读取到的字节数;

        =-1, 函数运行失败,更新errno;

代码示例
若消息队列中有消息:
    mtype 100   101  99   100  101
    mtext aaa   bbb  ccc  ddd  eee
i. msgtyp == 0
    while(1)                                                                   
    {
        //阻塞方式读取消息队列中第一条消息,先进先出的原则
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, 0);
        
        //非阻塞方式读取消息队列中第一条消息,先进先出的原则
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, IPC_NOWAIT);
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
​
输出顺序:
    100 aaa   101 bbb   99 ccc   100 ddd    101 eee
ii. msgtyp > 0
    while(1)
    {
        //1.阻塞方式读取消息队列中第一条消息类型 == 101 的消息
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, 0);
​
        //2.非阻塞方式读取消息队列中第一条消息 == 101 的消息
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT);
                                                                                  
        //3.非阻塞方式读取消息队列中第一条消息类型 != 101的消息 
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT|020000);
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
注释1,2的现象:
    101 bbb     101 eee
第3个的现象:
    100 aaa    99 ccc   100 ddd  
iii. msgtyp
    while(1)
    {
        //阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, 0);
       
        //非阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, IPC_NOWAIT);
​
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
​现象:
res=128 : 99 ccc
res=128 : 100 aaa
res=128 : 100 ddd
​

5) msgctl【控制消息队列,用于删除消息队列】

功能:控制消息队列,常用于删除消息队列;

原型:

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

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数:

        ​​​​​​int msqid   :指定要控制的消息队列的id号;
        int cmd

    IPC_STAT:获取消息队列的属性,属性存储在第三个参数中;
    IPC_SET:设置消息队列属性,属性存储在第三个参数中;
    IPC_RMID:删除消息队列,第三个参数无效,填NULL即可;

返回值:

        成功,返回0;

        失败,返回-1,更新errno

常用示例:

    //删除消息队列
    if(msgctl(msqid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    printf("删除消息队列成功\n");

6) 作业

1. 要求用消息队列实现AB进程对话

  • A进程先发送一句话给B进程,B进程接收后打印
  • B进程再回复一句话给A进程,A进程接收后打印
  • 重复1.2步骤,当收到quit后,要结束AB进程

2. 实现随时收发:用多进程 多线程。

二、共享内存(shared memory)

2.1 共享内存的概念

1) 共享内存的原理

        在内核内存中创建一个共享内存,共享内存可以被分别映射到不同的进程的用户空间中,每个进程在各自的用户空间中就可以操作同一个共享内存,从而可以操作同一个物理地址空间。

2) 共享内存的特点

① 共享内存是 最高效的 进程间通信方式。

  • 进程可以在用户空间,通过指针直接访问共享内存,对共享内存进行读写,不需要任何的数据拷贝。

② 共享内存是在内核空间中被创建,可以被映射到不同的进程中。

③ 多个进程可以同事访问共享内存,因此对于共享内存的操作,需要引入进程的同步互斥机制:信号灯集

④ 共享内存独立于进程,即使进程结束,共享内存及其中的数据依然存在,除非手动删除或者重启操作系统。

⑤ 共享内存中的数据即使被读取后,依然存在,不会被删除。

3) 查看共享内存

查看共享内存:ipcs
              ipcs -m   
删除共享内存:ipcrm -m  shmid

2.2 共享内存的函数

1) ftok【计算键值】

功能:① 该函数通过pathname提供的id,以及proj_id提供的8bit的值,计算key值(键值),给msgget shmget semget函数使用.

          ② 只要pathname和proj_id一致,则计算的key值就一致。那么通过相同key值找到的IPC对象就是同一个。

原型:

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

       key_t ftok(const char *pathname, int proj_id);

参数:

    char *pathname:文件的路径以及名字; 该文件必须存在且可访问
    int proj_id:传入一个非0参数;

返回值:

        成功,返回计算得到的key值;

        失败,返回-1,更新errno;

2) shmget【通过key在内核内存中找到对应的消息队列,并返回队列id】

功能:通过key值到内核内存中找对应的共享内存,并返回共享内存的id--->msqid;

原型:

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

       int shmget(key_t key, size_t size, int shmflg);

参数:

    key_t key ftok函数返回出来的key值;

    ssize_t 指定要申请多少个字节的共享内存;
    int shmflg

  • IPC_CREAT:若共享内存不存在,则创建共享内存。若共享内存存在,则忽略该选项;
  • IPC_CREAT|0664:创建的同时指定共享内存的权限。
  • IPC_CREAT|IPC_EXCL:若共享内存不存在,则创建共享内存。若共享内存存在,则报错;

返回值:

        >=0, 成功返回共享内存的id号 shmid;

        =-1, 函数运行失败,更新errno;

3) shmat【将共享内存映射到用户空间中】

功能:将共享内存映射到用户空间中;

原型:

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

       void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

    int shmid   :指定要映射的共享内存id号;
    const void *shmaddr 

        指定共享内存要映射到用户空间的位置,填对应空间的首地址; 例如:(void*)0x10

        填NULL,代表让操作系统自动映射;
    int shmgflg

  • 0:默认方式映射,进程对共享内存可读可写;
  • SHM_RDONLY:只读,进程对共享内存只读;

返回值:

        成功,返回共享内存映射到用户空间的首地址;

        失败,返回 (void *) -1,更新errno;

注意:

        获取到的映射空间的首地址的指向不允许修改,若修改后会导致首地址找不到,导致内存泄漏,与堆空间首地址不能改变的概念一致

4) shmdt【断开映射】

功能:将共享内存与进程的用户空间断开映射;

           当进程不想操作共享内存的时候,就可以断开映射

原型:

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

 
       int shmdt(const void *shmaddr);

参数:

    void *shmaddr 指定要断开映射的用户空间的首地址;

返回值:

        成功,返回0;

        失败,返回-1,更新errno;

5) shmctl【控制共享内存,常用于删除共享内存】

功能:通过key值到内核内存中找对应的共享内存,并返回共享内存的id--->msqid;

原型:

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

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

   ​​​​​​int msqid   :指定要控制的消息队列的id号;
   int cmd

    IPC_STAT:获取消息队列的属性,属性存储在第三个参数中;
    IPC_SET: 设置消息队列属性,属性存储在第三个参数中;
    IPC_RMID:删除消息队列,第三个参数无效,填NULL即可;

返回值:

        成功,返回0;

        失败,返回-1,更新errno;

6) 常用示例

    //删除共享内存
    if(shmctl(shmid, IPC_RMID, NULL) < 0)
    {
        perror("shmctl");
        return -1;
    }
    printf("删除共享内存成功\n");

7) 函数使用示例

写入
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    //创建key值                                             
    key_t key = ftok("./",10);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建共享内存,获得shmid号
    int shmid = shmget(key, 32, IPC_CREAT|0664);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    printf("shmid = %d\n",shmid);

    //映射共享内存到用户空间
    void* addr= shmat(shmid, NULL, 0);
    if((void*)-1 == addr)
    {
        perror("shmat");
        return -1;
    }
    printf("addr = %p\n",addr);

    //先往共享内存中存储一个int类型数据
    //再在int类型数据后面存储一个字符串
    *(int*)addr=10;
    strcat((char*)addr+4,"hello world");
    //*(char*)addr+4="hello world";


    system("ipcs -m");
    return 0;
}
读取
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./",10);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建共享内存,获得shmid号
    int shmid = shmget(key, 32, IPC_CREAT|0664);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    printf("shmid = %d\n",shmid);

    //映射共享内存到用户空间
    void* addr= shmat(shmid, NULL, 0);
    if((void*)-1 == addr)
    {                                             
        perror("shmat");
        return -1;
    }
    printf("addr = %p\n",addr);

    //先往共享内存中存储一个int类型数据
    //再在int类型数据后面存储一个字符串
    printf("%d",*(int*)addr);
    printf("%s\n",(char*)addr+4);

    system("ipcs -m");
    return 0;
}
结果:

8) 作业

1.要求在共享内存中存入字符串 “1234567”。A进程循环打印字符串,B进程循环倒置字符串,要求结果不允许出现乱序:

        提示:共享内存中存储 flag + string.

三、信号灯集(semaphore)

3.1 信号灯集的概念

1)信号灯集的原理

① 信号灯:又称之为信号量,用于线程之间的同步互斥机制

② 信号灯集:又称之为信号量数组,集合中有一个或多个信号灯。信号灯集中灯的编号从0开始,其实就是数组下标。

i)信号灯的查看:ipcs ipcs -s ipcrm -s semid

2)信号灯集的核心操作

PV操作:

        P操作:申请信号量的操作,减操作

        V操作:释放信号量的操作,加操作

        wait for zero:阻塞等待信号量的值为0,解除阻塞。当信号量的值不为0阻塞,当为0时解除阻塞。

3.2 信号灯集的函数

1)ftok

功能:

        通过pathname参数给定文件的id号 和 proj_id给定的非0参数,计算key值(键值),提供给msgget shmget semget函数使用;

        只要key值不变,则通过key值找到的IPC对象就是同一个。

原型:

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

       key_t ftok(const char *pathname, int proj_id);

参数:

    char *pathname:文件路径以及名字; 文件必须存在且可以访问即可;
    int proj_id:非0参数,用户自定义;

返回值:

        成功,返回key值;

        失败,返回-1,更新errno;

    key_t key = ftok("/home/ubuntu/", 1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }                                           
    printf("key = %#x\n", key);

2)semget

功能:通过key值找到对应的信号灯集; 信号灯集中灯的值默认为0

原型:

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

       int semget(key_t key, int nsems, int semflg);

参数:

    key_t key:ftok计算出来的键值;
    int nsems:信号灯集中有几个信号灯;
    int semflg:
        IPC_CREAT:如果信号灯集不存在,则创建,如果存在则忽略该选项;
            IPC_CREAT | 0664
        IPC_CREAT|IPC_EXCL:如果信号灯集存在,则该函数运行失败,errno == EEXIST;

返回值:

        成功,返回非负数,即信号灯集id , semid;

        失败,返回-1,更新errno;

3)semop

功能:P操作 V操作 wait for zero操作

原型:

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

       int semop(int semid, struct sembuf *sops, size_t nsops);

参数:

    int semid:指定要操作的信号灯集:
    struct sembuf *sops:
        struct membuf{
           unsigned short sem_num;  /* semaphore number */     指定要操作的信号灯集中灯的下标(编号)
           short          sem_op;   /* semaphore operation */  V操作:正整数,例如+2,则灯的值会加上2
                                                               P操作:负整数,例如-2,则灯的值为-2;
                                                                   当不够减的时候,该函数会阻塞;
                                                               wait for zero操作:当信号量的值不为0阻塞,当为0时解除阻塞                                                               
           short          sem_flg;  /* operation flags */
      0:默认操作方式,该阻塞的时候阻塞。
                                                               IPC_NOWAIT:非阻塞方式,该阻塞的时候不阻塞,函数返回运行失败情况;    
        }
    size_t nsops:指定要控制的信号灯集中灯的个数;

返回值:

        成功,返回非负数,即信号灯集id , semid;

        失败,返回-1,更新errno;

4)semctl

功能:控制信号灯集;

原型:

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

       int semctl(int semid, int semnum, int cmd, ...);

参数:

    int semid:指定要控制哪个信号灯集;
    int semnum:指定要控制灯集中的哪个灯,填对应的编号;
    int cmd:
        IPC_STAT:获取信号灯集的属性,存储到最后一个参数中,最后一个参数的类型:struct semid_ds *buf
        IPC_RMID:删除信号灯集,第二个参数无意义,最后一个参数不用填;
        GETALL:获取信号灯集中,所有灯的值。第二个参数无意义,最后一个参数的类型:unsigned short  *array
        GETVAL:获取信号灯集中,指定灯的值,最后一个参数不用填; 获取到的数据从返回值返回;
        SETALL:设置信号灯集中,所有灯的值。第二个参数无意义,最后一个参数的类型:unsigned short  *array
        SETVAL:获取信号灯集中,指定灯的值,最后参数类型:int val
        
    ... :
            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) */
           };
if(semctl(semid, 0, IPC_RMID) < 0)
{
    perror("semctl");                   
    return -1;
}

5)代码示例

P
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./",1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //获取信号灯集的id号-->semid
    int semid = semget(key,2,IPC_CREAT|0664);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    printf("semid= %d\n",semid);

    struct sembuf sops;

    sops.sem_num = 0;   //制定控制0号灯
    sops.sem_op = -1;   //P操作 -1                           
    sops.sem_flg = 0;   //阻塞方式运行
    if(semop(semid,&sops,1) < 0)
    {
        perror("semop");
        return -1;
    }
    printf("P operation success __%d__\n",__LINE__);

    system("ipcs -s");
    return 0;
}
V
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./",1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //获取信号灯集的id号-->semid
    int semid = semget(key,2,IPC_CREAT|0664);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    printf("semid= %d\n",semid);

    struct sembuf sops;
                                                      
    sops.sem_num = 0;   //制定控制0号灯
    sops.sem_op = 2;   //V操作 +2
    sops.sem_flg = 0;   //阻塞方式运行
    if(semop(semid,&sops,1) < 0)
    {
        perror("semop");
        return -1;
    }
    printf("P operation success __%d__\n",__LINE__);

    system("ipcs -s");
    return 0;
}
wait for zero
#include <sys/ipc.h>
#include <sys/sem.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./",1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //获取信号灯集的id号-->semid
    int semid = semget(key,2,IPC_CREAT|0664);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    printf("semid= %d\n",semid);

    struct sembuf sops;

    sops.sem_num = 0;   //制定控制0号灯
    sops.sem_op  = 0;   //waitfozero 操作 
    sops.sem_flg = 0;   //阻塞方式运行
    if(semop(semid,&sops,1) < 0)
    {
        perror("semop");
        return -1;
    }
    printf("P operation success __%d__\n",__LINE__);

    system("ipcs -s");
    return 0;                                        
}
                                                     

 另
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    //创建key
    key_t key = ftok("/home/ubuntu/", 1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key=%#X\n", key);

    //创建信号灯集,灯集中有两个信号灯,分别为0,1号灯
    int semid = semget(key, 2, IPC_CREAT|0664);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    printf("semid = %d\n", semid);
    system("ipcs -s");

    //设置所有信号灯的值
    unsigned short setall[2] = {100, 200};
    if(semctl(semid, 100, SETALL, setall) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("SETALL \n");

    //获取所有信号灯的值
    unsigned short getall[2] = {0};
    if(semctl(semid, 100, GETALL, getall) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("GETALL: %d %d\n", getall[0], getall[1]);


    //设置指定信号灯的值
    int val = 3;
    if(semctl(semid, 0, SETVAL, val) < 0)
    {
        perror("semctl");
        return -1;
    }

    //获取指定灯的值
    int getval = 0;
    if((getval=semctl(semid, 0, GETVAL)) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("getval = %d\n", getval);
                                                                                                        
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值