Linux进程间通信(五):信号量 semget()、semop()、semctl()

Linux进程间通信(五):信号量 semget()、semop()、semctl()

参考链接:https://www.cnblogs.com/52php/p/5851570.html

FreeBSD Manual Pages:https://www.freebsd.org/cgi/man.cgi?query=semget&sektion=2

man:https://man7.org/linux/man-pages/man2/shmop.2.html

这篇文章将讲述别一种进程间通信的机制——信号量。注意请不要把它与信号混淆起来,信号与信号量是不同的两种事物。

解释1

一、什么是信号量

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

二、信号量的工作原理

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行

V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

三、Linux的信号量机制

Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。

1、semget()函数

它的作用是创建一个新信号量或取得一个已有信号量,原型为:

int semget(key_t key, int num_sems, int sem_flags);

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget()函数并提供一个键,再由系统生成一个相应的信号标识符(semget()函数的返回值),只有semget()函数才直接使用信号量键,所有其他的信号量函数使用由semget()函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

semget()函数成功返回一个相应信号标识符(非零),失败返回-1.

2、semop()函数

它的作用是改变信号量的值,原型为:

int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);

sem_id是由semget()返回的信号量标识符,sembuf结构的定义如下:

struct sembuf{
    short sem_num; // 除非使用一组信号量,否则它为0
    short sem_op;  // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
                   // 一个是+1,即V(发送信号)操作。
    short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,
                   // 并在进程没有释放该信号量而终止时,操作系统释放信号量
};

3、semctl()函数

该函数用来直接控制信号量信息,它的原型为:

int semctl(int sem_id, int sem_num, int command, ...);

如果有第四个参数,它通常是一个union semum结构,定义如下:

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};

前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个

SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。

IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

四、进程使用信号量通信

下面使用一个例子来说明进程间如何使用信号量来进行通信,这个例子是两个相同的程序同时向屏幕输出数据,我们可以看到如何使用信号量来使两个进程协调工作,使同一时间只有一个进程可以向屏幕输出数据。注意,如果程序是第一次被调用(为了区分,第一次调用程序时带一个要输出到屏幕中的字符作为一个参数),则需要调用set_semvalue()函数初始化信号并将message字符设置为传递给程序的参数的第一个字符,同时第一个启动的进程还负责信号量的删除工作。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

在main函数中调用semget()来创建一个信号量,该函数将返回一个信号量标识符,保存于全局变量sem_id中,然后以后的函数就使用这个标识符来访问信号量。

源文件为seml.c,代码如下:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>

union semun
{
  int val;
  struct semid_ds *buf;
  unsigned short *arry;
};

static int sem_id = 0;
static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();

int main(int argc, char *argv[])
{
    char message = 'X';
    int i = 0;

    // 创建信号量
    sem_id = semget((key_t) 1234, 1, 0666 | IPC_CREAT);

    if (argc > 1)
    {
        // 程序第一次被调用,初始化信号量
        if (!set_semvalue())
        {
            fprintf(stderr, "Failed to initialize semaphore\n");
            exit(EXIT_FAILURE);
        }

        // 设置要输出到屏幕中的信息,即其参数的第一个字符
        message = argv[1][0];
        sleep(2);
    }

    for (i = 0; i < 10; ++i)
    {
        // 进入临界区
        if (!semaphore_p())
        {
            exit(EXIT_FAILURE);
        }

        // 向屏幕中输出数据
        printf("%c", message);

        // 清理缓冲区,然后休眠随机时间
        fflush(stdout);
        sleep(rand() % 3);

        // 离开临界区前再一次向屏幕输出数据
        printf("%c", message);
        fflush(stdout);

        // 离开临界区,休眠随机时间后继续循环
        if (!semaphore_v())
        {
            exit(EXIT_FAILURE);
        }
        sleep(rand() % 2);
    }

    sleep(10);
    printf("\n%d - finished\n", getpid());

    if (argc > 1)
    {
        // 如果程序是第一次被调用,则在退出前删除信号量
        sleep(3);
        del_semvalue();
    }
    exit(EXIT_SUCCESS);
}

static int set_semvalue()
{
    // 用于初始化信号量,在使用信号量前必须这样做
    semun sem_union;

    sem_union.val = 1;
    if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
    {
        return 0;
    }
    return 1;
}

static void del_semvalue()
{
    // 删除信号量
    semun sem_union;

    if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    {
        fprintf(stderr, "Failed to delete semaphore\n");
    }
}

static int semaphore_p()
{
    // 对信号量做减1操作,即等待P(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;//P()
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr, "semaphore_p failed\n");
        return 0;
    }

    return 1;
}

static int semaphore_v()
{
    // 这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1; // V()
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr, "semaphore_v failed\n");
        return 0;
    }

    return 1;
}

运行结果:

root@hecs-x-medium-2-linux:/home/c++/homework# ./seml 0 & ./seml
[1] 23216
XXXX00XX00XX00XX00XX00XX00XX00XXXX000000
23217 - finished
root@hecs-x-medium-2-linux:/home/c++/homework#
23216 - finished

注:这个程序的临界区为main函数for循环不的semaphore_p()和semaphore_v()函数中间的代码。

例子分析:

同时运行一个程序的两个实例,注意第一次运行时,要加上一个字符作为参数,例如本例中的字符‘O’,它用于区分是否为第一次调用,同时这个字符输出到屏幕中。

因为每个程序都在其进入临界区后和离开临界区前打印一个字符,所以每个字符都应该成对出现,正如你看到的上图的输出那样。

在main函数中循环中我们可以看到,每次进程要访问stdout(标准输出),即要输出字符时,每次都要检查信号量是否可用(即stdout有没有正在被其他进程使用)。

所以,当一个进程A在调用函数semaphore_p()进入了临界区,输出字符后,调用sleep()时,另一个进程B可能想访问stdout,但是信号量的P请求操作失败,只能挂起自己的执行,当进程A调用函数semaphore_v()离开了临界区,进程B马上被恢复执行。

然后进程A和进程B就这样一直循环了10次。

细节: This function has three or four arguments, depending on cmd. When there are

four, the fourth has the type union semun. The calling program must define this

union as follows: 非得要求调用者自己定义。

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) */
};

五、对比例子——进程间的资源竞争

看了上面的例子,你可能还不是很明白,不过没关系,下面我就以另一个例子来说明一下,它实现的功能与前面的例子一样,运行方式也一样,都是两个相同的进程,同时向stdout中输出字符,只是没有使用信号量,两个进程在互相竞争stdout。它的代码非常简单,文件名为normalprint.c,代码如下:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char message = 'X';
    int i = 0;
    if (argc > 1)
    {
        message = argv[1][0];
    }

    for (i = 0; i < 10; ++i)
    {
        printf("%c", message);
        fflush(stdout);
        sleep(rand() % 3);
        printf("%c", message);
        fflush(stdout);
        sleep(rand() % 2);
    }
    sleep(10);
    printf("\n%d - finished\n", getpid());

    exit(EXIT_SUCCESS);
}

运行结果:

root@hecs-x-medium-2-linux:/home/c++/homework# ./normalprint 0 &  ./normalprint
[1] 23587
X0XXX000X0X0X0XXX000X0X0X0X0X0XX0X0X00X0
23588 - finished
root@hecs-x-medium-2-linux:/home/c++/homework#
23587 - finished

例子分析:

从上面的输出结果,我们可以看到字符‘X’和‘O’并不像前面的例子那样,总是成对出现,因为当第一个进程A输出了字符后,调用sleep休眠时,另一个进程B立即输出并休眠,而进程A醒来时,再继续执行输出,同样的进程B也是如此。

所以输出的字符就是不成对的出现。这两个进程在竞争stdout这一共同的资源。通过两个例子的对比,我想信号量的意义和使用应该比较清楚了。

六、使用信号量资源计数

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <sys/time.h>

const int string_len = 4096;
const int share_m_number = 2;
const int init_sleep_time_us = 2;

int timestamp();

class Flag
{
    public:
        int done;
        // 0 means empty 1 means full
        int mem_empty[share_m_number];
        Flag();
};

Flag *f;

class Data
{
    public:
        char data[string_len];
        int size;
};

Data *d;

class Semaphore
{
    public:
        // semun 类声明
        union semun
        {
            int val;
            struct semid_ds *buf;
            unsigned short *arry;
        };
        // 信号id变量声明
        int sem_id = 0;
        // 函数声明
        int create_semvalue();
        int set_semvalue(int source_number);
        void del_semvalue();
        int semaphore_p();
        int semaphore_v();
};

Semaphore semaphore;

FILE *read_fp = NULL, *write_fp=NULL;

int read(int cnt)
{
    size_t size_of_elements = 1;
    size_t number_of_elements = string_len;
    size_t read_bytes = fread(d[cnt].data, size_of_elements, number_of_elements, read_fp);
//    printf("%s\n", d[cnt].data);
//    printf("%d\n", int(read_bytes));
    return read_bytes;
}

int write(int cnt, int size)
{
    size_t size_of_elements = 1;
    size_t number_of_elements = size;
    for(int i=0;i!=size;i++)
    {
        if(d[cnt].data[i]>='a'&&d[cnt].data[i]<='z')
        {
           d[cnt].data[i]-=32;
        }
    }
    size_t write_bytes = fwrite(d[cnt].data, size_of_elements, number_of_elements, write_fp);
//    printf("%s\n", d[cnt].data);
//    printf("%d\n", int(write_bytes));
    return write_bytes;
}

void X()
{
    int cnt=0;
    int read_able = 0;
    int done = 0;
    int sleep_time_us = init_sleep_time_us;
    read_fp = fopen("data.txt", "r");
    while(!done)
    {
        read_able = 0;
        // 首先检测能不能read
        semaphore.semaphore_p();
        if(f->mem_empty[cnt]==0)
        {
            read_able=1;
        }
        semaphore.semaphore_v();
        //如果能read,就read,sleep3,然后进入临界区,去把flag改为1,否则等一会,让write先处理一会儿
        if(read_able)
        {
            int read_bytes = read(cnt);
            semaphore.semaphore_p();
            if(read_bytes==0)
            {
                f->done = 1;
                done = 1;
//                printf("read done\n");
            }
            else
            {
                d[cnt].size = read_bytes;
                f->mem_empty[cnt]=1;
//                printf("read d[%d]\n", cnt);
//                printf("read set f->mem_empty[%d]=1\n", cnt);
            }
            semaphore.semaphore_v();
            if(read_bytes)
            {
                cnt++;
                cnt%=share_m_number;
            }
            sleep_time_us = init_sleep_time_us;
        }
        else
        {
//            printf("read failed, write not done!\n");
//            sleep(1);
            usleep(sleep_time_us);
            sleep_time_us *=2;
        }
    }
    fclose(read_fp);
}

void Y()
{
    int done=0;
    int cnt=0;
    int write_able = 0;
    int size=0;
    int sleep_time_us = init_sleep_time_us;
    write_fp = fopen("out.txt", "w");
    while(!done)
    {
        size=0;
        write_able = 0;
        // 首先看看能不能write
        semaphore.semaphore_p();
        if(f->mem_empty[cnt])
        {
            write_able = 1;
            size = d[cnt].size;
        }
        done = f->done;
        semaphore.semaphore_v();
        // 如果能write,那么write,sleep3,再将flag置为0
        if(write_able)
        {
            write(cnt, size);
            semaphore.semaphore_p();
            f->mem_empty[cnt]=0;
//            printf("write d[%d]\n", cnt);
//            printf("write set f->mem_empty[%d]=0\n", cnt);
            semaphore.semaphore_v();
            cnt++;
            cnt%=share_m_number;
            sleep_time_us = init_sleep_time_us;
        }
        else
        {
//            printf("write failed, read not done!\n");
//            sleep(1);
            usleep(sleep_time_us);
            sleep_time_us *=2;
        }
    }
    fclose(write_fp);
}

int main(int argc, char *argv[])
{
    int start_time = timestamp();
    // 创建进程共享的状态数组
    f = (Flag *)mmap(0, sizeof(Flag)*share_m_number, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,  0);

    // 创建共享内存
    d = (Data *)mmap(0, sizeof(Data)*share_m_number, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,  0);

    // 创建信号量
    if(!semaphore.create_semvalue())
    {
        exit(0);
    }

    // 设置信号量
    semaphore.set_semvalue();

    // 创建进程
    pid_t pid = fork();

    if(pid==0)
    {
        printf("X pid %d\n", getpid());
        X();
        printf("X end!\n");
        munmap(d, sizeof(Data)*share_m_number);
        munmap(f, sizeof(Flag)*share_m_number);
    }
    else
    {
        printf("Y pid %d\n", getpid());
        Y();
        printf("Y end!\n");
        munmap(d, sizeof(Data)*share_m_number);
        munmap(f, sizeof(Flag)*share_m_number);
    }
    int end_time = timestamp();
    double cost_time = (end_time-start_time)*1.0/1000;
    printf("cost time %lfs\n", cost_time);
    // 信号量不能删除太早,太早写进程就无法再访问共享内存了,
    // 所以稍微等一等写进程
    // 信号量我删除了两次会有一次出错,emmmm,就这吧
    sleep(3);
    semaphore.del_semvalue();
    return 0;
}

Flag::Flag()
{
    done=0;
    memset(mem_empty, 0 ,sizeof(mem_empty));
}

int Semaphore::create_semvalue()
{
    // 创建信号量
    sem_id = semget((key_t) 1234, 1, 0666 | IPC_CREAT);
    if(sem_id<=0)
    {
        printf("Create semaphore falied!\n");
        return 0;
    }
    return 1;
}

int Semaphore::set_semvalue(int source_number)
{
    // 用于初始化信号量,在使用信号量前必须这样做
    Semaphore::semun sem_union;

    sem_union.val = source_number;
    if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
    {
        printf("Set semaphore failed!\n");
        return 0;
    }
    return 1;
}

void Semaphore::del_semvalue()
{
    // 删除信号量
    Semaphore::semun sem_union;

    if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    {
        fprintf(stderr, "Failed to delete semaphore\n");
    }
}

int Semaphore::semaphore_p()
{
    // 对信号量做减1操作,即等待P(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;//P()
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr, "semaphore_p failed\n");
        return 0;
    }

    return 1;
}

int Semaphore::semaphore_v()
{
    // 这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1; // V()
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr, "semaphore_v failed\n");
        return 0;
    }
    return 1;
}

int timestamp()
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    return (tv.tv_sec*1000 + tv.tv_usec/1000);
}

七、信号量的总结

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。

解释2

PV signal

wait操作:如果S大于零,wait就在一个原子操作中测试S,并对其进行减量运算;

如果S等于零,wait就在一个原子操作中测试S,并阻塞调用程序。

signal操作:如果有线程在信号量上阻塞,S就等于零,signal就会解除对某个等待线程的阻塞;

如果没有线程在信号量上阻塞,signal就对S进行增量运算。

信号量分为:内核信号量和用户态进程使用信号量

POSIX信号量又分为:有名信号量和无名信号量、

他们之间的区别近似与有名管道与无名管道。

有名信号量,其值保存在文件中,所以它可以用于线程也可以用于进程间的同步。无名信号量,其值保存在内存中。

下面介绍一下,与信号量有关的函数吧,主要应用到的函数有:

1.        Semget()

2.        Semop()

3.        Semctl()

其中,semget函数是为了获取信号量的标识码的,然后其他的函数只能通过对semget的返回值对信号量进行处理。

Semget()函数:

#include<sys/sem.h>

Int semget(key_t _key,int_nsems,int _semflg);

函数的功能:是创建一个信号量或者是为了获得一个新的键值

返回值:成功的返回信号量的标识码,如果函数调用失败返回-1

第一个参数_key,为整型值,是允许其他的进程访问信号量的一个整型的变量。所以的信号都是通过间接的方式获得的,运行的程序会提供一个信号的键值,系统为每一个键值赋予一个信号量,其他的处理函数只能通过对semget函数的返回值进行处理。

第二个参数_nsems,参数表示信号量的编号,几乎总是取值为1.

第三个参数_semflg,信号量的权限。

系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。

系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。

打开和存取操作与参数semflg中的内容相关。

IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。

IPC_EXCL当和 IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。

如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。

如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1。

IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。

下面是一个打开和创建信号量的程序:

int mykey=5555;
int semid;
semid=semget(mykey,numsems,IPC_CREAT|0660);

if(semid==-1)

{

    perror(“semget :”);

      exit(1);

}

需要说明的是,返回一个信号量的ID号。其他通过对信号量的ID号进行操作。

semop()函数使用:

函数功能:用户改变信号量的值.

返回值:函数调用成功返回0,失败返回-1

参数semid:系统分配给该信号量的一个ID号,通过semget函数的返回值来获得,也称为信号的标识码

参数sops:一个指向信号量结构体数组的指针,信号量的结构体至少有3个成员。

其结构如下:

struct sembuf

{

unsigned short sem_num;操作信号在信号集中的编号,第一个信号的编号为0,第二个为1,一次后推

short sem_op;信号资源,如果有就分配,如果为负值就等待(wait),如果正值就分配信号资源

short sem_flg;信号操作标志,有两种状态,一个是SEM_UNDO,另一个是SEM_NOWAIT

}

SEM_NOWAIT//对信号的操作不能满足是,semop()函数就会阻塞,并立即返回,同时设置错误信息。

SEM_UNDO//程序结束时(无论是否正常结束),保证信号值会被重新设为semop()调用前的值。这样做

的目的在于避免在异常情况下结束时未将锁定资源解锁,造成该资源永远锁定。

下面写一个小例子声明一个结构体:

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

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

semctl()函数:

功能:控制信号量的信息

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

第一个参数semid:信号量标识码,还是通过semget来获得的,是semget函数的一个返回值

第二个参数semnum:信号量的编号,当用到信号量数组的时候,这个编号就会起到作用

第三个参数cmd:为允许的命令,其范围在:

第四个参数union semun:是一个共同体

结构如下:

union semun{

int val;

struct semid_ds *buf;

unsigned short *array;

}

样例

下面看一个小案例吧,来说明一下信号量的使用,这里我们用到了两个进程,也就是要写两个函数,也用到了共享内存的知识。

进程1:

/*
* filename:shm
*
*/
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/stat.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<fcntl.h>
#include<stdlib.h>
#include<time.h>
#include<stdio.h>
#include<string.h>
#define SEM_KEY1 5555
union semun
{
    int setval;
    struct semid_ds *buf;
    unsigned short *array;
};
int main()
{
    int shmid,semid;
    int *addr;
    int h=0,w=0;
    int ret;
    union semun sem_args;
    unsigned short array[2]={1,1};
    sem_args.array=array;
    shmid=shmget(ftok("/etc/passwd",1001),getpagesize(),IPC_CREAT|IPC_EXCL|S_IRUSR| S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    if(shmid==-1)
    {
        perror(“shmget error:”);
        exit(EXIT_FAILURE);
    }
    semid=semget(SEM_KEY1,2,IPC_CREAT|0600);
    if(semid==-1)
    {
        perror(“semget error:”);
        exit(EXIT_FAILURE);
    }
    ret=semctl(semid,1,SETALL,sem_args);
    if(ret==-1)
    {
        perror(“shmctl error:”);
        exit(EXIT_FAILURE);
    }
    struct sembuf sem1_opt_wakeup[1]={0,1,SEM_UNDO};
    struct sembuf sem2_opt_wait[1]={1,-1,SEM_UNDO};

     while(1)
    {
        int i=0;
        semop(semid,sem2_opt_wait,1);
        addr=shmat(shmid,0,0);
        printf(“please input your infornation:\n”);
        scanf(“%d%d”,addr,addr+1);
        shmdt(addr);
        semop(semid,sem1_opt_wakeup,1);
        printf(“退出清输入1\n”);
        scanf(“%d”,&i);
        if(i==1)
        {
            shmctl(shmid,IPC_RMID,NULL);
            semctl(semid,IPC_RMID,NULL);
            exit(1);
        }
    }
return 0;
}

进程2:

#include<sys/types.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#define SEM_KEY 5555

int main()
{
    int shmid,semid;
    int *addr;
    int h,w;
    float result,sum;
    struct sembuf sem1_opt_wait[1]={0,-1,SEM_UNDO};
    struct sembuf sem2_opt_wakeup[1]={1,1,SEM_UNDO};

     while(1)
    {
        shmid=shmget(ftok("/etc/passwd",1001),getpagesize(),S_IRUSR| S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
        sleep(1);
        if(shmid==-1)
        {
            perror(“shmget error:”);
            exit(EXIT_FAILURE);
        }
        semid=semget(SEM_KEY,2,0600);
        if(semid==-1)
        {
            perror(“semget error:”);
            exit(EXIT_FAILURE);
        }
        addr=shmat(shmid,0,0);
        w=*(addr+1);
        h=*addr;
        sum=h*h/10000;
        result=w/sum;

        semop(semid,sem1_opt_wait,1);

        if(result>25)
        {
            printf(“胖拉!\n”);
            *(addr+2)=0;
        }
        else if(result<20)
        {
            printf(“瘦啦!\n”);
            *(addr+2)=0;
        }
        else
        {
            if(result>5&&result<20)
            {
                printf(“正常!\n”);
                *(addr+2)=0;
            }
        }
        shmdt(addr);
        semop(semid,sem2_opt_wakeup,1);
        sleep(2);
    }
}
文章目录