Linux 进程间通信之信号量

目录

  • 概述
  • 信号量接口函数
  • 信号量用于同步
  • 信号量用于互斥
  • IPC操作指令

概述

进程通信的概念请参考上一篇文章:Linux进程间通信之共享内存。
信号量作用:用于进程/线程同步互斥的机制。
信号量主要用于控制多个进程间或一个进程内的多个线程间对共享资源的访问, 相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志,除了用于共享资源的访问控制外,还可用于进程同步。

信号量接口函数

包含头文件:
#include
semget ()创建信号量
int semget(key_t key, int num_sems, int sem_flags);

  • key:信号量的唯一标识,为非0正值,可自定义,用于区别不同信号量。
  • num_sems:信号量的数目,非0正值。
  • sem_flags:标志位,和open函数相似。IPC_CREAT标志是创建或者使用已有的信号量,而IPC_CREATE和IPC_EXCL结合使用可以确保创建出的是一个新的、唯一的信号量,如果该信号量已存在,它将返回一个错误。创建时给出的权限可以是:0666。
  • 返回值:成功返回信号量ID,错误返回-1。

semctl()初始化信号量:
int semctl(int sem_id, int sem_num, int command, …);

  • semid:信号量ID。
  • sem_num:信号量编号,从0开始编号,如果semget中num_sems为1,则此参数为0
  • command:为 SETVAL(设置信号灯值) IPC_RMID(删除整个信号灯集)。
  • 第四个参数:当command为SETVAL时需要第四个参数,该参数结构体参考:
union semun {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) */
 };
  • 返回值:成功返回0,错误返回-1.

semop()操作信号量值PV:
int semop(int sem_id, struct sembuf * sops, size_t nsops);

  • sem_id:信号量ID;
  • sops:描述对信号灯操作的结构体(数组),结构体定义如下:
struct sembuf
{
    short sem_num;/*信号量的编号*/
    short sem_op;/*P操作为-1,V操作为1*/
    short sem_flg;/*0标识阻塞方式,IPC_NOWAIT非阻塞*/
}
  • nsops:要操作的信号灯的个数(一次操作sops数组里的前nsops个元素)
  • 返回值:成功返回0,失败返回-1。

信号量用于同步

信号量用于同步,步骤如下:

  1. semget()创建信号量。
  2. semctl()设置初始值(SETVAL)为0.
  3. semop()操作信号量。A进程进行V(+1)操作,相当于发送信号量semPost(),B进程P(-1)操作,相当于等待接收信号量semWait()。
    Linux 进程间通信之信号量_第1张图片

实验目的:进程A接收到串口信息,向进程B发送信号量;B接收到信号量打印“sem wait ok”。
实验代码:
a.c

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


static int semid = 0;

int semInit(void)
{
    semid = semget((key_t)0x1234,1,IPC_CREAT | 0600);
    if(semid == -1)
    {
        printf("semget error");
	    return -1;
    }
     union semun a;
     a.val = 0;//初始化信号量的值为0
     if(semctl(semid,0,SETVAL,a)==-1)
     {
         perror("semctl init error");
         return -1;
      }
    return 0;
}

int semPost(void)
{
    struct sembuf buf;
    buf.sem_num = 0;//信号量下标,semget中信号量的数量初始化为1,此处只能为0
    buf.sem_op = 1;//V操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)//1表示操作数,sembuf的数量
    {
        perror("p error");
        return -1;
    }
	return 0;
}

int semWait(void)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//P操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)
    {
        perror("v error");
        return -1;
    }
    return 0;
}

int semDestroy(void)
{
    if(semctl(semid,0,IPC_RMID)==-1)//0代表信号量集
    {
        perror("semctl destroy error");
       return -1;
    }
	return 0;
}


int main()
{
	char shmptr[100]; 
    semInit();
    while(1)
    {
		printf("input:");  
		scanf("%s",shmptr);  
        semPost();
    }
}

b.c

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


static int semid = 0;

int semInit(void)
{
    semid = semget((key_t)0x1234,1,IPC_CREAT | 0600);
    if(semid == -1)
    {
        printf("semget error");
	    return -1;
    }
     union semun a;
     a.val = 0;//初始化信号量的值为0
     if(semctl(semid,0,SETVAL,a)==-1)
     {
         perror("semctl init error");
         return -1;
      }
    return 0;
}

int semPost(void)
{
    struct sembuf buf;
    buf.sem_num = 0;//信号量下标,semget中信号量的数量初始化为1,此处只能为0
    buf.sem_op = 1;//V操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)//1表示操作数,sembuf的数量
    {
        perror("p error");
        return -1;
    }
	return 0;
}

int semWait(void)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//P操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)
    {
        perror("v error");
        return -1;
    }
    return 0;
}

int semDestroy(void)
{
    if(semctl(semid,0,IPC_RMID)==-1)//0代表信号量集
    {
        perror("semctl destroy error");
       return -1;
    }
	return 0;
}


int main()
{
    semInit();
    while(1)
    {
		semWait();
		printf("sem wait ok\r\n");
    }
}

测试现象:
gcc a.c -o a
gcc b.c -o b
在这里插入图片描述

信号量用于互斥

信号量用于互斥入下图所示,进程B在访问x资源时,此时进程A无法访问;进程A访问x资源时,此时进程B无法访问。可防止资源访问冲突。
步骤如下:

  1. semget()创建信号量。
  2. semctl()设置初始值(SETVAL)为1.
  3. semop()操作信号量。A进程进行P(-1)操作,此时阻止B进程进行P(-1)操作。
    Linux 进程间通信之信号量_第2张图片

实验目的:进程A在串口打印的过程中,进程B不能访问不能使用串口;等待A进程打印完成后,B的串口打印才得到运行。
实验代码:
a.c

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


static int semid = 0;

int semInit(void)
{
    semid = semget((key_t)0x1234,1,IPC_CREAT | 0600);
    if(semid == -1)
    {
        printf("semget error");
	    return -1;
    }
     union semun a;
     a.val = 1;//初始化信号量的值为1
     if(semctl(semid,0,SETVAL,a)==-1)
     {
         perror("semctl init error");
         return -1;
      }
    return 0;
}

int semPost(void)
{
    struct sembuf buf;
    buf.sem_num = 0;//信号量下标,semget中信号量的数量初始化为1,此处只能为0
    buf.sem_op = 1;//V操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)//1表示操作数,sembuf的数量
    {
        perror("p error");
        return -1;
    }
	return 0;
}

int semWait(void)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//P操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)
    {
        perror("v error");
        return -1;
    }
    return 0;
}

int semDestroy(void)
{
    if(semctl(semid,0,IPC_RMID)==-1)//0代表信号量集
    {
        perror("semctl destroy error");
       return -1;
    }
	return 0;
}

int main()
{
    semInit();
    for(int i=0;i<10;i++)
    {
		semWait();
		printf("A1\r\n");  
		sleep(1);
		printf("A2\r\n");  
        semPost();
    }
}

b.c

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


static int semid = 0;

int semInit(void)
{
    semid = semget((key_t)0x1234,1,IPC_CREAT | 0600);
    if(semid == -1)
    {
        printf("semget error");
	    return -1;
    }
     union semun a;
     a.val = 0;//初始化信号量的值为0
     if(semctl(semid,0,SETVAL,a)==-1)
     {
         perror("semctl init error");
         return -1;
      }
    return 0;
}

int semPost(void)
{
    struct sembuf buf;
    buf.sem_num = 0;//信号量下标,semget中信号量的数量初始化为1,此处只能为0
    buf.sem_op = 1;//V操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)//1表示操作数,sembuf的数量
    {
        perror("p error");
        return -1;
    }
	return 0;
}

int semWait(void)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//P操作
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)
    {
        perror("v error");
        return -1;
    }
    return 0;
}

int semDestroy(void)
{
    if(semctl(semid,0,IPC_RMID)==-1)//0代表信号量集
    {
        perror("semctl destroy error");
       return -1;
    }
	return 0;
}

int main()
{
	int i;
    semInit();
    for(i=0;i<10;i++)
    {
		semWait();
		printf("B1\r\n");  
		sleep(1);
		printf("B2\r\n");  
        semPost();
    }
}

测试现象:
A在打印时,B不能执行打印;B在打印时,A不能执行打印。
Linux 进程间通信之信号量_第3张图片

IPC操作指令

查看系统中的IPC:ipcs
删除系统中的共享内存段:ipcrm -m [id]
删除系统中的信号量标志:ipcrm –s [id]

你可能感兴趣的:(Linux,应用编程,Linux进程间通信,信号量,semget,semctl,同步互斥)