信号量实现

7
我在以下程序中遇到了错误。我想演示如何使用信号量使两个进程共享变量。有人可以指导我吗?
我无法调试这些错误...
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<semaphore.h>
int main()
{
  int pid,mutex=1;
  int semid;               /* semid of semaphore set */
  key_t key = 1234; /* key to pass to semget() */
  int nsems = 1; /* nsems to pass to semget() */
  semid=semget(key,nsems,IPC_CREAT|0666);
  if (semid<0) 
  { 
    perror("Semaphore creation failed ");
  }
  if ((pid = fork()) < 0) 
  {
    perror("fork");
    return 1;
  }
  else if(pid==0)
  {
    sem_wait(&semid);
    printf("IN CHILD PROCESS :\n");
    mutex++; 
    printf("value of shared variable =%d",mutex);
    sem_post(&semid);
    return 0;
  }
  sem_wait(&semid);
  printf("IN PARENT PROCESS :\n");
  mutex--;
  printf("value of shared variable =%d",mutex);
  sem_post(&semid);
  return 0;
} 

我家里没有Linux......这是我在大学尝试实现的程序......我不知道确切的错误......它与semid有关。 - chinu
标志是IPC_CREAT而不是IPC_CREATE,请编辑代码并包含shm_wait()和shm_post()的定义。 - Barath Ravikumar
谢谢……但是shm_wait()和shm_post()是什么? - chinu
好的...但是sem_wait和sem_post是预定义函数,它们在semaphore.h头文件中定义了...那么为什么还需要重新定义它们呢? - chinu
不,你不必这样做,但是你必须使用选项-pthread 进行编译,才能使它们正常工作。更多详情请参见此处:https://dev59.com/sGXWa4cB1Zd3GeqPKSwc - Barath Ravikumar
显示剩余3条评论
4个回答

26
您的基础知识存在问题,程序无法正常工作,因此请重新学习基础知识并重写程序。
您需要进行以下一些更正:
1) 您需要创建一个信号量类型的变量。
sem_t semvar;

2) 函数 sem_wait(), sem_post() 需要信号量变量作为参数,但是你却传递了信号量 id,这是没有意义的。

sem_wait(&semvar);
   //your critical section code
sem_post(&semvar);

3) 你正在将信号量传递给 sem_wait()sem_post() 而没有初始化它。在使用之前,必须将其初始化为1(在您的情况下),否则会发生死锁。

ret = semctl( semid, 1, SETVAL, sem);
if (ret == 1)
     perror("Semaphore failed to initialize");

请查阅信号量API的man手册,并仔细学习例子


我有一个疑问.....如果我使用sem_t创建一个信号量变量,那么我们如何获取该变量的semid?我的程序是否需要使用semid?即使阅读了手册,我仍然感到困惑。 - chinu
使用semgets()函数获取semid后,你可以在代码中使用semid,例如在上述代码中,你可以在semctl()中使用semid。 - Barath Ravikumar
1
(sem_wait, sem_post) 不应与 semctl 一起使用,因为它们不兼容。我刚刚写了一个关于这个问题的答案。 - David Beck
更重要的是,为什么每个人都说“man page”而不是“main page”。通常,我会说这是一个打字错误,但即使在“main page”上也写着“man page”。(见:[图:初始化信号量]) - Loufs
你的回答在方法上是正确的,但问题是关于使用信号量实现IPC,而你的回答是关于一个临界区问题sem_wait和sem_post。 - Ayush joshi

18

你的代码存在一个根本性问题,就是混用了两种API。不幸的是,在线资源并没有很好地指出这一点,但在类Unix系统中有两个信号量API:

  • POSIX IPC API,这是一个标准API
  • System V API,它来自旧的Unix世界,但几乎所有的Unix系统都可用

从上面的代码中可以看到,你使用了来自System V API的semget(),并试图通过来自POSIX API的sem_post()进行post操作。这是不可能混用的。

要决定要使用哪种信号量API,你没有太多好的资源。最好的方法是参考Stevens的"Unix Network Programming"。你可能感兴趣的部分在第2卷中。

这两个API非常不同。它们都支持教科书式的信号量,但是System V API中有一些值得一提的优缺点:

  • 它建立在信号量集上,因此一旦你使用semget()创建了一个对象,那么它就是一组信号量而不是单个信号量
  • System V API允许你在这些集合上执行原子操作。所以你可以修改或等待一组中的多个信号量
  • SysV API允许你等待信号量达到阈值而不仅仅是非零。虽然也支持等待非零阈值,但我的上一句话意味着这一点
  • 每个Unix系统的信号量资源都非常有限。你可以使用'ipcs'命令来检查这些资源
  • System V信号量有一个撤销功能,所以你可以确保异常程序终止不会使你的信号量处于不良状态

4

通过调整消费者速率和生产者速率(使用sleep),来更好地理解代码的运作。以下是消费者-生产者模拟代码(在容器的最大限制下)。

以下为参考代码:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

sem_t semP, semC;
int stock_count = 0;
const int stock_max_limit=5;

void *producer(void *arg) {
    int i, sum=0;
    for (i = 0; i < 10; i++) {

        while(stock_max_limit == stock_count){
            printf("stock overflow, production on wait..\n");
            sem_wait(&semC);
            printf("production operation continues..\n");
        }

        sleep(1);   //production decided here
        stock_count++;
        printf("P::stock-count : %d\n",stock_count);
        sem_post(&semP);
        printf("P::post signal..\n");
    }
 }

void *consumer(void *arg) {
    int i, sum=0;
    for (i = 0; i < 10; i++) {

        while(0 == stock_count){
            printf("stock empty, consumer on wait..\n");
            sem_wait(&semP);
            printf("consumer operation continues..\n");
        }

        sleep(2);   //consumer rate decided here
        stock_count--;
        printf("C::stock-count : %d\n", stock_count);
        sem_post(&semC);
        printf("C::post signal..\n");
        }
}

int main(void) {

    pthread_t tid0,tid1;
    sem_init(&semP, 0, 0);
    sem_init(&semC, 0, 0);

        pthread_create(&tid0, NULL, consumer, NULL);
        pthread_create(&tid1, NULL, producer, NULL);
        pthread_join(tid0, NULL);
        pthread_join(tid1, NULL);

    sem_destroy(&semC);
    sem_destroy(&semP);

    return 0;
}

1
请查看下面的示例代码,了解信号量实现(锁定和解锁)。
    #include<stdio.h>
    #include<stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include<string.h>
    #include<malloc.h>
    #include <sys/sem.h>
    int main()
    {
            int key,share_id,num;
            char *data;
            int semid;
            struct sembuf sb={0,-1,0};
            key=ftok(".",'a');
            if(key == -1 ) {
                    printf("\n\n Initialization Falied of shared memory \n\n");
                    return 1;
            }
            share_id=shmget(key,1024,IPC_CREAT|0744);
            if(share_id == -1 ) {
                    printf("\n\n Error captured while share memory allocation\n\n");
                    return 1;
            }
            data=(char *)shmat(share_id,(void *)0,0);
            strcpy(data,"Testing string\n");
            if(!fork()) { //Child Porcess
                 sb.sem_op=-1; //Lock
                 semop(share_id,(struct sembuf *)&sb,1);

                 strncat(data,"feeding form child\n",20);

                 sb.sem_op=1;//Unlock
                 semop(share_id,(struct sembuf *)&sb,1);
                 _Exit(0);
            } else {     //Parent Process
              sb.sem_op=-1; //Lock
              semop(share_id,(struct sembuf *)&sb,1);

               strncat(data,"feeding form parent\n",20);

              sb.sem_op=1;//Unlock
              semop(share_id,(struct sembuf *)&sb,1);

            }
            return 0;
    }

@Gyan 好的...这是新的..在这里将共享内存ID传递给接受sem id的信号量API是如何工作的? - Haswell
确实,我猜代码中有一个错别字:shm与sem。 - David Beck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接