进程间通信之信号量

常见的SYSTEM V信号量函数

(a)关键字和描述符

      SYSTEM V信号量是SYSTEM V IPC(即SYSTEM V进程间通信)的组成部分,其他的有SYSTEM V消息队列,SYSTEM V共享内存。而关键字和IPC描述符无疑是它们的共同点,也使用它们,就不得不先对它们进行熟悉。这里只对SYSTEM V信号量进行讨论。

      IPC描述符相当于引用ID号,要想使用SYSTEM V信号量(或MSG、SHM),就必须用IPC描述符来调用信号量。而IPC描述符是内核动态提供的(通过semget来获取),用户无法让服务器和客户事先认可共同使用哪个描述符,所以有时候就需要到关键字KEY来定位描述符。

某个KEY只会固定对应一个描述符(这项转换工作由内核完成),这样假如服务器和客户事先认可共同使用某个KEY,那么大家就都能定位到同一个描述符,也就能定位到同一个信号量,这样就达到了SYSTEM V信号量在进程间共享的目的。

(b)创建和打开信号量

int semget(key_t  key, int  nsems, int  oflag)

(1) nsems>0  : 创建一个新的信号量集,指定集合中信号量的数量,一旦创建就不能更改。

(2) nsems==0 : 访问一个已存在的集合

(3) 返回的是一个称为信号量标识符的整数,semop和semctl函数将使用它。

(4) 创建成功后信号量结构被设置:

    .sem_perm 的uid和gid成员被设置成的调用进程的有效用户ID和有效组ID

    .oflag 参数中的读写权限位存入sem_perm.mode

    .sem_otime 被置为0,sem_ctime被设置为当前时间

    .sem_nsems 被置为nsems参数的值

    该集合中的每个信号量不初始化,这些结构是在semctl,用参数SET_VAL,SETALL初始化的。

 

      semget函数执行成功后,就产生了一个由内核维持的类型为semid_ds结构体的信号量集,返回semid就是指向该信号量集的引索。

(c)关键字的获取

      有多种方法使客户机和服务器在同一IPC结构上会合:

(1)服务器可以指定关键字IPC_PRIVATE创建一个新IPC结构,将返回的标识符存放在某处(例如一个文件)以便客户机取用。关键字 IPC_PRIVATE保证服务器创建一个新IPC结构。这种技术的缺点是:服务器要将整型标识符写到文件中,然后客户机在此后又要读文件取得此标识符。

IPC_PRIVATE关键字也可用于父、子关系进程。父进程指定 IPC_PRIVATE创建一个新IPC结构,所返回的标识符在fork后可由子进程使用。子进程可将此标识符作为exec函数的一个参数传给一个新程序。

 

(2)在一个公用头文件中定义一个客户机和服务器都认可的关键字。然后服务器指定此关键字创建一个新的IPC结构。这种方法的问题是该关键字可能已与一个 IPC结构相结合,在此情况下,get函数(msgget、semget或shmget)出错返回。服务器必须处理这一错误,删除已存在的IPC结构,然后试着再创建它。当然,这个关键字不能被别的程序所占用。

 

(3)客户机和服务器认同一个路径名和课题ID(课题ID是0~ 2 5 5之间的字符值),然后调用函数ftok将这两个值变换为一个关键字。这样就避免了使用一个已被占用的关键字的问题。

使用ftok并非高枕无忧。有这样一种例外:服务器使用ftok获取得一个关键字后,该文件就被删除了,然后重建。此时客户端以此重建后的文件来ftok所获取的关键字就和服务器的关键字不一样了。所以一般商用的软件都不怎么用ftok。

一般来说,客户机和服务器至少共享一个头文件,所以一个比较简单的方法是避免使用ftok,而只是在该头文件中存放一个大家都知道的关键字。

     

(d)设置信号量的值(PV操作)

int semop(int  semid, struct  sembuf  *opsptr, size_t  nops);

(1) semid: 是semget返回的semid

(2)opsptr: 指向信号量操作结构数组

(3) nops : opsptr所指向的数组中的sembuf结构体的个数

 

struct sembuf {

    short sem_num;    // 要操作的信号量在信号量集里的编号,

    short sem_op;     // 信号量操作

    short sem_flg;     // 操作表示符

};

(4) 若sem_op 是正数,其值就加到semval上,即释放信号量控制的资源

    若sem_op 是0,那么调用者希望等待到semval变为0,如果semval是0就返回;

若sem_op 是负数,则表示要获取由该信号量控制的资源,若semval大于或等于sem_op的绝对值,则从semval中减去sem_op的绝对值,若semval小于sem_op的绝对值那么调用者希望等待semval变为大于或等于sem_op的绝对值;

例如,当前semval为2,而sem_op = -3,那么怎么办?

注意:semval是指semid_ds中的信号量集中的某个信号量的值

(5) sem_flg

    SEM_UNDO     由进程自动释放信号量

    IPC_NOWAIT    不阻塞

     

到这里,读者肯定有个疑惑:semop希望改变的semval到底在哪里?我们怎么没看到有它的痕迹?其实,前面已经说明了,当使用semget时,就产生了一个由内核维护的信号量集(当然每个信号量值即semval也是只由内核才能看得到了),用户能看到的就是返回的semid。内核通过semop函数的参数,知道应该去改变semid所指向的信号量的哪个semval。

(e)对信号集实行控制操作(semval的赋值等)

int semctl(int  semid, int  semum, int  cmd, ../* union semun arg */);

 

semid是信号量集合;

semnum是信号在集合中的序号;

semum是一个必须由用户自定义的结构体,在这里我们务必弄清楚该结构体的组成:

unionsemun

{

    int val;                //cmd == SETVAL

    struct semid_ds *buf     // cmd == IPC_SET或者 cmd == IPC_STAT

    ushort *array;          // cmd == SETALL,或 cmd = GETALL

};

val只有cmd ==SETVAL时才有用,此时指定的semval = arg.val。

注意:当cmd == GETVAL时,semctl函数返回的值就是我们想要的semval。千万不要以为指定的semval被返回到arg.val中。

    array指向一个数组,当cmd==SETALL时,就根据arg.array来将信号量集的所有值都赋值;当cmd ==GETALL时,就将信号量集的所有值返回到arg.array指定的数组中。

buf指针只在cmd==IPC_STAT或IPC_SET时有用,作用是semid所指向的信号量集(semid_ds结构体)。一般情况下不常用,这里不做谈论。

另外,cmd == IPC_RMID还是比较有用的。

(f)例码

待续。。。。。

源自互联网。。。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值