【进程间通信】信号量的使用/共享内存

信号量:

:特殊的变量 值 > 0 减1 代表获取资源 p操作 (会阻塞)
加1 代表释放资源,v操作 (会阻塞)

  • 作用 : 同步进程 (程序的红绿灯,控制对进程对某个资源的访问)

  • 临界资源:同一时刻,只允许一个进程访问的资源

  • 临界区:访问临界资源的代码段

  • 请添加图片描述
    帮助手册 man semget;(信号量)man semctl(封装)

  • 信号量是由内核控制的。不受A/B进程的控制
    semget() 创建/获取一个已存在的信号量
    //A 创建好信号量(需要初始化信号量semget---semctl()) ,B只需要加入(无需再初始化semget()获取信号量)
    semop() p,v 修改信号量
    semctl() 初始化,删除信号量 (初始值为1,表明资源空闲)
    sem_init()// semget—semctl(),emget()
    sem_p()// semop() -1
    sem_v()// semop() +1
    sem_destory() semctl()
    引用头文件:

#include <sys/sem.h>
//创建和获取信号量
//key 相当于信号量的标识,如果使用同一个key,就是在使用同一个信号量
int semget(key_t key, int nsems, int semflg);                             /          // 整数值 //创建信号量个数 //如文件的权限
  //可以创建一组信号量,只是创建了一个而已  IPC_CRET|IPC_EXCL|0600

//如果创建失败,两种情况(信号量已存在,(semget()获取即可,或 创建失败)
//如果创建成功,接下来
//初始化信号量

int semctl(int semid, int semnum, int cmd, ...);
                     //semnum:第几(第一个0)个信号(可以创建一组(多个 )可以分别初始化       
                     //cmd   命令(SETVAL) (IPC_RMID)
                     // ... 联合体(给SETVAL赋值)     
semctl(semid,0,SETVAL,a)  //给信号量赋值
semctl(semid,0,IPC_RMID) //删除信号量                          

int val; /Value for SETVAL; (给信号量赋值)

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

使用 union semun 联合体给信号量赋值
union semun a;
a.val = 1;

                        //  对信号量的处理
int semop(int semid, struct sembuf *sops, size_t nsops);

unsigned short            sem_num;  /* semaphore number */
           short          sem_op;   /* semaphore operation */
           short          sem_flg = SEM_UNDO;  /* operation flags */                   //p操作后,当程序异常终止,可以自动执行V操作

例如:创建a.c / b.c文件
信号量文件 sem.c
头文件:

  • sem.h文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/sem.h>

union semun
{
    int val;
};

void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
  • sem.c文件
#include "sem.h"
static int semid = -1;
void sem_init()
{
    semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
    if ( semid == -1 )//全新创建失败,可能已存在
    {
        semid = semget((key_t)1234,1,0600);
        if ( semid == -1 )
        {
            printf("create sem failed\n");
        }
    }
    else//全新创建成功,初始化
    {
       union semun a;
       a.val = 1;
       if ( semctl(semid,0,SETVAL,a) == -1 )
       {
           printf("semctl setval failed\n");
       } 
    }
}
void sem_p()
{
    struct sembuf a;
    a.sem_num = 0;
    a.sem_op = -1;//p
    a.sem_flg = SEM_UNDO;

    if( semop(semid,&a,1) == -1 )
    {
        printf("semop p failed\n");
    }
}
void sem_v()
{
    struct sembuf a;  //处理信号量
    a.sem_num = 0;  //表示第1个信号量
    a.sem_op = 1;//给信号量赋值
    a.sem_flg = SEM_UNDO;  //表示如果p操作后,程序异常结束,可以自动执行v操作,释放资源。

    if( semop(semid,&a,1) == -1 )   //判断V操作是否成功
    {
        printf("semop v failed\n");
    }
}
void sem_destroy()
{
    if ( semctl(semid,0,IPC_RMID) == -1 )
    {
        printf("destroy failed\n");
    }
}
  • a.c 文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "sem.h"

int main()
{
    sem_init();
    for( int i = 0; i < 5; i++ )
    {
        sem_p();
        printf("A");
        fflush(stdout);
        int n = rand() % 3;
        sleep(n);
        printf("A");
        fflush(stdout);
        sem_v();

        n = rand() % 3;
        sleep(n);

    }
    sleep(10);
    sem_destroy();
    exit(0);
}
  • b.c文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "sem.h"
int main()
{
    sem_init();
    for( int i = 0; i < 5; i++ )
    {
        sem_p();
        printf("B");
        fflush(stdout);
        int n = rand() % 3;
        sleep(n);
        printf("B");
        fflush(stdout);
        sem_v();
        n = rand() % 3;
        sleep(n);
    }
}

编译命令:

                  gcc -c sem.c
                  gcc -o a a.c sem.c
                  gcc -o b b.c sem.c

执行: ./a& ./b& (表示在后台同时执行)
当结束进程,使用ipcs命令查看是否将a.c 和b.c均结束
如果没有使用ipcrm手动结束。防止下次执行出现异常。(除非关机重启,负责未删除的信号量一直在)
信号量学习主要了解使用信号量的过程,
初始化,
p操作
v操作
删除信号量

共享内存

  • 多个进程共享同一块物理内存。
    (先开辟一块物理内存,多个进程将其映射到自己的虚拟地址空间,如果有一个进程修改此内存上的内容,其他共享的进程也会立刻看到。)
    但是没有提供同步机制,需要信号量来控制进程的同步
    定义两个信号量
    目的:保证在读端没有写数据的时候,只允许写端写一次数据。
    请添加图片描述

(实现管道的功能)

  • sem.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/sem.h>
#define  SEM1  0
#define  SEM2  1
#define  SEM_MAX  2
union semun
{
    int val;
};

void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
  • sem.c
 #include "sem.h"
static int semid = -1;
void sem_init()
{  //创建信号量                 //最多创建
    semid = semget((key_t)1234,SEM_MAX,IPC_CREAT|IPC_EXCL|0600);
    if ( semid == -1 )//全新创建失败,可能已存在
    {                  //获取信号量
        semid = semget((key_t)1234,SEM_MAX,0600);
        if ( semid == -1 )
        {
            printf("create sem failed\n");
        }
    }
    else//全新创建成功,初始化
    {
       union semun a;
       int arr[SEM_MAX] = {1,0}; //两个信号量,初始值分别为1,0
       for( int i = 0; i < SEM_MAX; i++ )
       {
            a.val = arr[i];         //给信号量赋值,控制信号量,成功返回0
            if ( semctl(semid,i,SETVAL,a) == -1 )
            {              // i:给哪个信号量赋值
                printf("semctl setval failed\n");
            }
       } 
    }
}
void sem_p(int index)
{        //用输入的index来控制不同信号量
    if ( index < 0 || index >= SEM_MAX )
    {
        return;
    }
    struct sembuf a;
    a.sem_num = index;
    a.sem_op = -1;//p
    a.sem_flg = SEM_UNDO;

    if( semop(semid,&a,1) == -1 )
    {
        printf("semop p failed\n");
    }
}
void sem_v(int index)
{
    if ( index < 0 || index >= SEM_MAX )
    {
        return;
    }
    struct sembuf a;
    a.sem_num = index;
    a.sem_op = 1;//v
    a.sem_flg = SEM_UNDO;

    if( semop(semid,&a,1) == -1 )
    {
        printf("semop v failed\n");
    }
}
void sem_destroy()
{
    if ( semctl(semid,0,IPC_RMID) == -1 )
    {
        printf("destroy failed\n");
    }
}


  • 对两个信号量的处理:(S1 S2)
    else//全新创建成功,初始化
    {
    union semun a;
    int arr[SEM_MAX] = {1,0}; //两个信号量,初始值分别为1,0
    for( int i = 0; i < SEM_MAX; i++ )
    {
    a.val = arr[i]; //给信号量赋值,控制信号量,成功返回0
    if ( semctl(semid,i,SETVAL,a) == -1 )
    { // i:给哪个信号量赋值
    printf(“semctl setval failed\n”);
    }
  • test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include "sem.h"

int main()
{
        
    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
    if ( shmid == -1 )
    {
        exit(1);
    }

    char * s = (char*)shmat(shmid,NULL,0);
    if ( s == (char*)-1 )
    {
        exit(1);
    }

    sem_init();
    while( 1 )
    {
        sem_p(SEM2);
        if ( strncmp(s,"end",3) == 0 )
        {
            break;
        }
        printf("read:%s\n",s);
        sem_v(SEM1);
    }
    shmdt(s);
    sem_destroy(); //在读操作结束后 销毁信号量
    exit(0);
}

//在读操作结束后 销毁信号量

  • main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include "sem.h"

int main()
{
    //创建共享内存  key:标识   size: 128   
    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
    if ( shmid == -1 )
    {
        exit(1);
    }
     //将申请的物理内存映射到进程的虚拟地址空间中
     //若映射成功,返回共享内存的首地址,否则返回空
    char* s = (char*)shmat(shmid,NULL,0); 
    if ( s == (char*)-1 )
    {
        exit(1);
    }

    sem_init();
    while( 1 )
    {
        printf("input\n");
        char buff[128] = {0};
        //键盘获取数据
        fgets(buff,128,stdin);
        
        sem_p(SEM1);
       //(读操作) 将获取的数据,复制到共享内存中
        strcpy(s,buff);
        sem_v(SEM2);
        //结束
        if ( strncmp(buff,"end",3) == 0 )
        {
            break;
        }
    }
    //断开映射
    shmdt(s);
    exit(0);
}

请添加图片描述
p77《linux平台开发》
作业:
请添加图片描述
a进程只输出a
b进程只输出b
c进程只输出c
打印结果 abcabcabc…

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值