信号量 Linux函数 semget();semctl();semop();

Linux进程通信之信号量

信号量(semaphore)是变量,是一种特殊的变量。它紧取正值。对信息号量的操作只有2中:

等待(wait)和发送信号(signal.

信号量比较难理解。下面我们一个个的看一下各个函数。

与信号量处理的函数有:semget();semctl();semop();

第一个semget()函数,与共享内存的shmget()函数类似。

使用格式:

#include<sys/sem.h>

int  semget(key_t  _key ,int  _nsems,int _semflg);

功能:创建一个新的信号量或获取一个已经存在的信号量的键值。

返回值:成功返回信号量的标识码ID。失败返回-1

参数:

_key  为整型值,用户可以自己设定。有两种情况:

1.       键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量。

2.       键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。

_nsems 表示初始化信号量的个数。比如我们要创建一个信号量,则该值为1.,创建2个就是2

_semflg  :信号量的创建方式或权限。有IPC_CREATIPC_EXCL

IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。

IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。

下面我们看个小例子:

 

 #include<stdio.h>

#include<sys/sem.h>

#define MYKEY 6666

int main()

{

 

        int semid;

        semid=semget(MYKEY,1,IPC_CREAT|0666);//创建了一个权限为666的信号量

        printf("semid=%d\n",semid);

        return 0;

}

运行结果为:

我们可以用ipcs –s 来查看是否创建成功。

ipcrm  -s   semid号来删除指定的信号量。

=============================================================================

第二个函数semctl()控制信号量的函数。

在这个函数中我们可以删除信号量或初始化信号量。

格式:

#include<sys/sem.h>

 

int  semctl(int _semid  ,int _semnum,int _cmd  ……);

功能:控制信号量的信息。

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

参数:

  _semid   信号量的标志码(ID),也就是semget()函数的返回值;

_semnum,  操作信号在信号集中的编号。从0开始。

_cmd    命令,表示要进行的操作。

下面列出的这些命令来源于百度!

参数cmd中可以使用的命令如下:

IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。

IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。

IPC_RMID将信号量集从内存中删除。

GETALL用于读取信号量集中的所有信号量的值。

GETNCNT返回正在等待资源的进程数目。

GETPID返回最后一个执行semop操作的进程的PID。

GETVAL返回信号量集中的一个单个的信号量的值。

GETZCNT返回这在等待完全空闲的资源的进程数目。

SETALL设置信号量集中的所有的信号量的值。

SETVAL设置信号量集中的一个单独的信号量的值。

 

 

Semunion ;4个参数是可选的;semunion :union semun的实例。

union semun {

 

   int   val;

   struct   semid_ds  *buf;

   unsigned short   *arrary;

};

//下面是给一个信号量初始化的代码。

        union semun  sem_args;

        unsigned short array[1]={1};

        sem_args.array = array;

        ret = semctl(semid, 0, SETALL, sem_args);//0代表对1个信号来量初始化,即有1个资源

         if (-1 == ret)

        {

                perror("semctl");

                exit(EXIT_FAILURE);

        }

================================================

 

 第三个函数  semop();信号来那个操作处理的函数。

 

格式:#include<sys/sem.h>

int    semop(int   semid ,struct    sembuf   *_sops ,size_t  _nsops);

功能:用户改变信号量的值。也就是使用资源还是释放资源使用权。

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

参数:

   _semid : 信号量的标识码。也就是semget()的返回值。

 _sops是一个指向结构体数组的指针。

 struct   sembuf{

     unsigned short  sem_num;//第几个信号量,第一个信号量为0

     short  sem_op;//对该信号量的操作。

     short _semflg;

};

 

sem_num:  操作信号在信号集中的编号。第一个信号的编号为0

sem_op : 如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0

_semflg IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。

IPC_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

 

nsops:操作结构的数量,恒大于或等于1

 

下面看个例子:

源文件1

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

#include<sys/stat.h>

#include<sys/sem.h>

 

 

#define SEM_KEY 6666

 

union semun {

    int setval;

    struct semid_ds *buf;

    unsigned short *array;

};

 

 

int main(int argc ,char *argv[])

{

 

    int  shmid;

    float   *addr;

    float h,w;

    shmid= shmget(ftok(".",1000),getpagesize(),IPC_CREAT  | 0666);//创建一个共享内存,权限为0666

    printf("shmid=%d\n",shmid);

    if(shmid==-1)

    {

       perror("shmget error:");

       exit(EXIT_FAILURE);

    }

    addr=shmat(shmid,0,0);//获取共享内存的起始地址,且为可读可写

    if(-1==*addr)

    {

       perror("shmat error:");

       exit(EXIT_FAILURE);

    }

 

    int semid;

    int ret;

    semid = semget(SEM_KEY, 2, IPC_CREAT | 0600);//创建2个信号量

    if (-1 == semid)

    {

       perror("semget");

       exit(EXIT_FAILURE);

    }

    printf("semid = %d\n", semid);

 //   初始化2个信号量

    union semun sem_args;

    unsigned short array[2]={1,1};

    sem_args.array = array;

    ret = semctl(semid, 1, SETALL, sem_args);//SETALL代表设置信号集中所有的信号量的值。1,代表2个,sem_args是具体初始化的值放在共用体中。

    if (-1 == ret)

    {

       perror("semctl");

       exit(EXIT_FAILURE);

    }

//对资源的使用处理操作

    struct sembuf sem_opt_wait1[1] = {0, -1, SEM_UNDO};

    struct sembuf sem_opt_wakeup1[1] = {0, 1, SEM_UNDO};

    struct sembuf sem_opt_wait2[1] = {1, -1, SEM_UNDO};

    struct sembuf sem_opt_wakeup2[1] = {1, 1, SEM_UNDO};

 

    while(1)

    {

       semop(semid, sem_opt_wait2, 1);//获取进程2的资源,让进程2等待

       printf(" enter you height(CM) and height(KG):\n");

       scanf("%f%f",addr,addr+1);

       semop(semid, sem_opt_wakeup1, 1);//唤醒进程1,即释放资源的使用权

       if(*addr==1||*(addr+1)==1 ) break;//如果输入身高是1或体重是1就退出

    }

 

    return 0;

}

 

 

源程序2

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

#include<sys/sem.h>

#include<string.h>

#include<sys/stat.h>

 

#define SEM_KEY 6666

 

union semun {

    int setval;

    struct semid_ds *buf;

    unsigned short *array;

};

 

 

int main(int argc ,char *argv[])

{

 

    int  shmid;

    float  *addr;

    float h,w;

    shmid= shmget(ftok(".",1000),getpagesize(), IPC_CREAT | 0666);//打开刚才原程序1创建的共享内存,权限为0666

        printf("shmid=%d\n",shmid);

    if(shmid==-1)

    {

 

       perror("shmget error:");

       exit(EXIT_FAILURE);

    }

    addr =shmat(shmid,0,0);//获取共享内存的起始地址,且为可读可写

    if(-1== *addr)

    {

       perror("shmat error:");

       exit(EXIT_FAILURE);

    }

 

    int semid;

    int ret;

    semid = semget(SEM_KEY, 0, IPC_CREAT | 0600););//取得信号量

    if (-1 == semid)

    {

       perror("semget");

       exit(EXIT_FAILURE);

    }

    printf("semid = %d\n", semid);

 

//对资源的使用处理操作

    struct sembuf sem_opt_wait1[1] = {0, -1, SEM_UNDO};

    struct sembuf sem_opt_wakeup1[1] = {0, 1, SEM_UNDO};

    struct sembuf sem_opt_wait2[1] = {1, -1, SEM_UNDO};

    struct sembuf sem_opt_wakeup2[1] = {1, 1, SEM_UNDO};

 

    while(1)

    {

       semop(semid, sem_opt_wait1, 1);//获取进程1的资源好让进程1等待。

       h=*addr;

       w=*(addr+1);

                printf("h=%f\tw=%f\n",h,w);

        if(h==1.0 || w==1.0) break;;//如果输入身高是1或体重是1就退出

       int ret=w/(h*h/10000);

       if(ret>=20 &&ret <=25)

       {

           printf("ok!\n");

       }else if(ret <20)

       {

           printf("thin!\n");

 

       } else

       {

           printf("fat!\n");

       }

       semop(semid, sem_opt_wakeup2, 1);//释放进程2的资源,即唤醒进程2

 

    }

       if(-1==semctl(semid,1,IPC_RMID,0))//删除信号量

       {

       perror("semctl error:");

       exit(EXIT_FAILURE);

        }

    if(-1==shmdt(addr))//释放共享内存,使其不再有任何指向它的指针

    {

       perror("shmdt error:");

       exit(EXIT_FAILURE);

 

    }

    if (shmctl(shmid,IPC_RMID,0)==-1)//删除共享内存

    {

       perror("shctl error:");

       exit(EXIT_FAILURE);

    }

 

 

    return 0;

}

 //两个进程相互制约,而达到资源的有效使用。

 

运行结果:

  • 38
    点赞
  • 221
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
目录 封面 -12 封底 -11 扉页 -10 版权 -9 版权声明 -8 前言 -7 目录 -3 第一部分 简介 1 第1章 简介 2 1.1 概述 2 1.2 进程、线程与信息共享 3 1.3 IPC对象的持续性 4 1.4 名字空间 5 1.5 fork、exec和exit对IPC对象的影响 7 1.6 出错处理:包裹函数 8 1.7 Unix标准 9 1.8 书中IPC例子索引表 11 1.9 小结 13 习题 13 第2章 Posix IPC 14 2.1 概述 14 2.2 IPC名字 14 2.3 创建与打开IPC通道 16 2.4 IPC权限 18 2.5 小结 19 习题 19 第3章 System V IPC 20 3.1 概述 20 3.2 key_t键和ftok函数 20 3.3 ipc_perm结构 22 3.4 创建与打开IPC通道 22 3.5 IPC权限 24 3.6 标识符重用 25 3.7 ipcs和ipcrm程序 27 3.8 内核限制 27 3.9 小结 28 习题 29 第二部分 消息传递 31 第4章 管道和FIFO 32 4.1 概述 32 4.2 一个简单的客户-服务器例子 32 4.3 管道 32 4.4 全双工管道 37 4.5 popen和pclose函数 39 4.6 FIFO 40 4.7 管道和FIFO的额外属性 44 4.8 单个服务器,多个客户 46 4.9 对比迭代服务器与并发服务器 50 4.10 字节流与消息 51 4.11 管道和FIFO限制 55 4.12 小结 56 习题 57 第5章 Posix消息队列 58 5.1 概述 58 5.2 mq_open、mq_close和mq_unlink函数 59 5.3 mq_getattr和mq_setattr函数 61 5.4 mq_send和mq_receive函数 64 5.5 消息队列限制 67 5.6 mq_notify函数 68 5.7 Posix实时信号 78 5.8 使用内存映射I/O实现Posix消息队列 85 5.9 小结 101 习题 101 第6章 System V消息队列 103 6.1 概述 103 6.2 msgget函数 104 6.3 msgsnd函数 104 6.4 msgrcv函数 105 6.5 msgctl函数 106 6.6 简单的程序 107 6.7 客户-服务器例子 112 6.8 复用消息 113 6.9 消息队列上使用select和poll 121 6.10 消息队列限制 122 6.11 小结 124 习题 124 第三部分 同步 125 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 互斥锁和条件变量的属性 136 7.8 小结 139 习题 139 第8章 读写锁 140 8.1 概述 140 8.2 获取与释放读写锁 140 8.3 读写锁属性 141 8.4 使用互斥锁和条件变量实现读写锁 142 8.5 线程取消 148 8.6 小结 153 习题 153 第9章 记录上锁 154 9.1 概述 154 9.2 对比记录上锁与文件上锁 157 9.3 Posix fcntl记录上锁 158 9.4 劝告性上锁 162 9.5 强制性上锁 164 9.6 读出者和写入者的优先级 166 9.7 启动一个守护进程的唯一副本 170 9.8 文件作锁用 171 9.9 NFS上锁 173 9.10 小结 173 习题 174 第10章 Posix信号量 175 10.1 概述 175 10.2 sem_open、sem_close和sem_unlink函数 179 10.3 sem_wait和sem_trywait函数 180 10.4 sem_post和sem_getvalue函数 180 10.5 简单的程序 181 10.6 生产者-消费者问题 186 10.7 文件上锁 190 10.8 sem_init和sem_destroy函数 191 10
Linux提供了几个信号量函数来实现进程间同步和互斥操作。这些函数包括: 1. `semget()`:创建一个新的信号量或获取一个已有的信号量的标识符。 2. `semop()`:对信号量进行操作,如设置、释放、获取等。 3. `semctl()`:对信号量进行控制,如获取、设置、删除等。 下面是这些函数的详细说明: 1. `semget()` ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); ``` 参数说明: - `key`:信号量的标识符,可以使用 `ftok()` 函数生成。 - `nsems`:需要的信号量数量。 - `semflg`:信号量的创建标志,可以是 `IPC_CREAT`、`IPC_EXCL` 或者二者的或运算。 返回值:如果成功,则返回信号量的标识符,否则返回 -1。 2. `semop()` ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, unsigned nsops); ``` 参数说明: - `semid`:信号量的标识符。 - `sops`:操作信号量的结构体指针。 - `nsops`:操作的信号量数量。 返回值:如果成功,则返回 0,否则返回 -1。 `semop()` 操作信号量的结构体包括: ```c struct sembuf { short sem_num; // 信号量的编号 short sem_op; // 信号量的操作 short sem_flg; // 操作标志 }; ``` 其中,`sem_num` 表示需要操作的信号量的编号,从 0 开始;`sem_op` 表示操作类型,一般为正数(V 操作)或负数(P 操作);`sem_flg` 表示操作标志,一般为 0 即可。 3. `semctl()` ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...); ``` 参数说明: - `semid`:信号量的标识符。 - `semnum`:信号量的编号,如果是对所有信号量进行操作,则为 0。 - `cmd`:需要执行的控制命令,可以是 `IPC_STAT`、`IPC_SET`、`IPC_RMID` 等。 - 可变参数:根据不同的命令而定,如 `IPC_SET` 则需要传入 `struct semid_ds *buf`。 返回值:根据不同的命令而定,如 `IPC_STAT` 则返回 0,否则返回 -1。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值