信号量的使用

2023-05-16

信号量

英文名字:semaphore

这里的进程信号量会为下列的信号量打基础

  • Ucos系统的信号量
  • c线程的信号量
  • java进程和线程的信号量

信号量作用

当多个进程/线程进行共享操作时,用于资源保护,以防止出现相互干扰的情况

信号量用于“资源的保护“

(1)进程信号量---实现的是进程所操作资源的保护。
(2)线程信号量---实现的是线程所操作资源的保护。

什么是进程的资源保护

1.多个进程同时向共享内存里面写数据时,可能会出现数据相互干扰的情况

  1. 某个进程写数据操作还没有写完时,进程的时间片就到了

  2. 然后被切换到另一个"共享内存"的写进程上运行

  3. 这个进程会接着往共享内存里面写数据,此时显然就把第一个进程写的数据给干扰了

    这就形成了数据相互干扰。

2.当多个进程同时共享向文件里面写数据时,同样会出现和共享写“共享内存”相同的情况


避免出现相互干扰,需要加入资源保护的措施

保护的目的就是:

保证每个进程在没有把数据读、写完整之前,其它进程不能进行读、写操作,以防止干扰别人

资源保护中资源指的就是操作的数据
保护的目的就是不要出现相互干扰,导致紊乱和错误数据的产生。

资源保护的种类:同步或互斥

信号量在资源保护中:即可以实现互斥又可以实现同步

但是文件锁只能实现互斥不同实现同步

互斥

对于互斥操作来说,多进程共享操作时:

  1. 多个进程间不关心谁先操作、谁后操作的先后顺序问题

  2. 它们只关心一件事,那就是我在操作时别人不能操作

---就算当前正在操作的进程它的时间片到了,切换到了其它进程上;
---但是当该进程检测到上一个进程还没有操作完时,该进程在当前的时间片内会休眠;
---直到再次切换会上一个进程,将操作完成后再切换回来,此时才能进行操作。

跟上厕所时把门关起来是一样的:

我在蹲坑时你不能蹲,你在蹲坑时我不能蹲,这就是互斥

至于蹲坑先后顺序并没有要求。

同步

同步其实本身就包含了互斥,不过同步不仅仅只互斥

同步对于谁先操作、谁后操作的先后顺序有要求

规定A进程先写,然后是B进程写,然后是C进程写,绝对不能出现这操作顺序以外的顺序

很像:银行办理业务,又注重互斥还注重顺序

互斥和同步的实现本质–加锁

信号量就是一个加锁机制,通过加锁来实现同步和互斥

同步和互斥的目的其实就是为了实现“资源”的保护,不要让数据出现紊乱

信号量是什么

信号量从代码上就是:结构体,链表,数组等数据结构写的锁。

通过数据结构可以标记这个进程的执行情况,实现加锁功能

信号量是加锁机制,为什么会被归到了进程间通信里面

资源保护时,某个进程的操作没有完全完成之前,别人是不能操作的

那么进程间必须相互知道对方的操作状态,也就是一定会涉及通信过程

信号量实现资源保护的本质

通过通信让各个进程了解到操作状态,然后查看自己能不能操作

信号量实现互斥的原理

进程信号量既能实现进程的互斥,也能实现进程的同步

父子进程同时写文件(不加信号量)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void print_err(char *str){
    perror(str);
    exit(-1);
}
int main(void){
    int ret =0;
    /*fork()前打开open*/
    int fd=-1;
    //fork之前无需O_APPEND,父子进程不相互干扰
    fd=open("./share_file",O_RDWR|O_CREAT|O_TRUNC,0664);
    if(fd==-1) print_err("open fails\n");
    ret=fork();
    if(ret>0){//父进程写数据
           while(1){
              write(fd,"aaaaaaaaa ",10);
              write(fd,"bbbbbbbbb\n",10);
           }
    }
    else if(ret==0){//子进程写数据
          while(1){
              write(fd,"ccccccccc ",10);
              write(fd,"ddddddddd\n",10);
           }
    }
    return 0;
}

父子进程这两个相同的“文件描述符”指向的是相同的“文件表”,文件表的指针位置是同一个不会覆盖

如果父子进程各自打开文件,各自会有自己的文件表,那么虽然通过索引得到同样的文件,但是各自文件表中的指针位置不同步,那么读写会覆盖

不能保证互斥,因为会切换进程

进程信号量实现互斥的原理

什么是进程信号量

简单理解的话:
----信号量其实是OS创建的一个共享变量
--------进程在进行操作之前,会先检查这个变量的值,这变量的值就是一个标记
--------通过这个标记就可以知道可不可以操作,以实现互斥

信号量是共享变量,这个变量的值就是操作标记

二值信号量
  • 同步和互斥时使用的都是二值信号量
  • 二值信号量的值就两个:0和1,0表示不可以操作,1表示可以操作

比如父进程写之前将共享变量值设为0,进行加锁,两个写语句设为加锁代码段;如果没写完就切换,那么切换后的进程发现变量值为0,就阻塞等待,不进行操作;直到两个write结束了,就解锁,也就是把共享变量值设为1。如此循环

多值信号量
  • 信号量的最大值>1,比如为3的话,信号量允许的值为0、1、2、3

多值信号量用的不是很多

信号量集合

信号量其实是一个OS创建的,供相关进程共享的int变量,只不过在调用相关API创建信号量时,创建的都是一个信号量集合

所谓集合就是可能会包含好多个信号量

  • 用于互斥时,集合中只包含一个信号量
  • 用于同步时,集合中会包含多个信号量,至于多少个,需要看情况。

信号量的使用

使用步骤

  1. 进程调用semget函数创建新的信号量集合,或者获取已有的信号量集合

  2. 调用semctl函数给集合中的每个信号量设置初始值

  3. 调用semop函数,对集合中的信号量进行pv操作

  4. 调用semctl删除信号量集合

创建或获取->设初值->pv操作->删除

PV操作

pv操作其实说白了就是加锁、解锁操作

  • P操作(加锁):对信号量的值进行-1,如果信号量的值为0,p操作就会阻塞
  • V操作(解锁):对信号量的值进行+1,V操作不存在阻塞的问题

通过pv操作(加锁、解锁),就能够实现互斥,以防止出现干扰

信号量的API

semget()

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//根据key值创建新的、或者获取已有的信号量集合,并返回其标识符
int semget(key_t key, int nsems, int semflg);
/*
实现互斥时:集合中只需要一个信号量
实现同步时:集合中需要多个信号量
*/
  • key:设置同消息队列和共享内存。

    一般都使用ftok获取key值

  • nsems:指定集合中信号量的个数

    • 用于互斥时,数量都指定为1,因为只需要一个信号量
    • 如果是同步的话就需要至多为多个
  • semflg:设置同消息队列和共享内存

    一般都设置为0664|IPC_CREAT

成功返回信号量集合的标识符,失败返回-1

semctl()

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//根据cmd的要求对集合中的各个信号量进行控制
int semctl(int semid, int semnum, int cmd, ...);
//返回值:调用成功返回非-1值,失败则返回-1,errno被设置

...表示它是一个变参函数,如果第四个参数用不到的话,可以省略不写

  • semid:信号量标识符

  • semnum:集合中某个信号量的编号

    • 信号量的编号为非负整数,而且是自动从0开始编号的

    通过信号量编号就能找到集合中对应信号量

  • cmd:控制选项

    • IPC_STAT:将信号量的属性信息从内核读到第四个参数所以指定的struct semid_ds缓存中

    • IPC_SET:修改属性信息,此时也会用到struct semid_ds结构体变量

    • IPC_RMID:删除信号量

      当集合中所有的信号量都被删除后,信号量集合也就被删除了

      删除操作时第四个参数用不到,比如:semctl(semid, 0, IPC_RMID);

    • SETVAL:通过第四个参数,给集合中semnu编号的信号量设置一个int初始值

  • SETVAL确属于进程信号量所独有的选项

如果是二值信号量的话,设置初始值要么是0,要么是1

如果信号量的目的是互斥的话,基本都是设置为1

当设置为1后,多几个进程互斥操作时,那就是谁先运行就谁先操作

第四个参数具体设置依赖于“cmd”

  • cmd为IPC_STAT:第四个参数应为struct semid_ds类型的缓存
  • cmd为SETVAL:第四个参数应该设置为一个int的值,用于初始化信号量

第四个参数对应内容是变着的,为了应对这种变化就用到了一个联合体

 union semun {
		int              val;    
		struct semid_ds *buf;    
		unsigned short  *array;  /* 不做要求 */
		struct seminfo  *__buf;  /* 不做要求 */
		};
//这个联合体类型并没有被定义在信号量相关的系统头文件中,使用时需要自己定义这个类型
指定struct semid_ds缓存时
union semun sem_un; //定义一个联合体变量
struct semid_ds buff; //定义一个struct semid_ds缓存
sem_un.buf = &buff;  //现在整个联合体的值就是buf中缩放的buff的地址	
semctl(semid, 0, IPC_STAT, sem_un); //这里将联合体传递给semctl函数,
                               //其实就是将buff的地址传递给了semctl函数
定信号量的int初始值
union semun sem_un; 
sem_un.val = 1;  //现在整个联合体的值就是1	
semctl(semid, 0, IPC_STAT, sem_un); 
删除信号量

int semctl(int semid, int semnum, int cmd, …);

删除信号量集合时,并不需要把所有的信号量都删除掉后才能删除
---------只需要指定semid和IPC_RMID就可以不把整个信号量集合删除,其中第二个参数semnum没有被用到,所以semnum的值可以随便写,不过我们一般都是把它写为0。
所以删除整个信号量集合时,删除的写法可以统一的为:
						semctl(semid, 0, IPC_RMID);

semop()

op是operate操作的意思

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//功能:对指定的信号量进行p操作、或者是v操作
int semop(int semid, struct sembuf *sops, size_t nsops);
//返回值:调用成功返回0,失败则返回-1,errno被设置
  • p操作:将信号量的值-1
    • 当信号量的值为0时,p操作默认是阻塞的
  • v操作:将信号量的值+1
    • v操作不存在阻塞的问题
对于二值信号量来说
v操作后,值就从0变为了1
这就表示我操作完了,其它进程运行时就可以进行p操作了

参数

  • semid:信号量集合的标识符
  • sops:这个参数更好理解的写法是struct sembuf sops[]
  • nsops:用于指定数组元素个数的
1. 每一个数组成员对应一个信号量
2. 每一个元素都是一个struct sembuf结构体变量

内部成员的决定着:

  • 你要对集合中哪一个信号量进行操作
  • 要进行的是p操作呢,还是v操作
//结构体成员
	struct sembuf{
			unsigned short sem_num;  
			short          sem_op;
			short          sem_flg;  
				}
//这个结构体不需要我们自己定义,因为在semop的头文件中已经定义了
  • sem_num:信号量编号,决定对集合中哪一个信号量进行pv操作

  • sem_op:设置为-1,表示想-1进行p操作,设置1表示想+1进行v操作

  • sem_flg

    • IPC_NOWAIT

      如果你不想阻塞的话,可以指定这个选项

    • SEM_UNDO

      如果进程在结束时没有V操作的话,OS会自动帮忙V操作,防止死锁

以二值信号量为例:

当进程在v操作之前就结束时,信号量的值就会一直保持为0,那么其它进程将永远无法p操作成功,会使得进程永远休眠下去,这造成就是死锁。

互斥完整程序

信号量封装程序

#ifndef mm
#define mm
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sem.h>
void print_err(char* str){
    perror(str);
    exit(-1);
}
union semun {//联合体
	int              val;    
	struct semid_ds *buf;    
};
int create_or_get_sem(int nsems){
     int semid=0;
     int fd=open("./semaFile",O_CREAT|O_RDWR,0664);
     if(fd==-1) print_err("open fails\n");
     key_t key=0;
     key=ftok("./semaFile",'a');
     if(key==-1) print_err("ftok fails\n");
     semid=semget(key,nsems,0664|IPC_CREAT);
     if(semid==-1) print_err("semget fails\n");
     
     return semid;
}
//初始化信号量----传信号量的编号,信号量集合的某个具体信号编号,初值
void init_sema(int semid,int semnum,int val){
    int ret=-1;
    union semun sem_un;
    sem_un.val=val;
    ret=semctl(semid,semnum,SETVAL,sem_un);
    if(ret==-1) print_err("semctl fails\n");
}
//删除信号量
//信号量从0开始连续,传入删除的总数,单个删
void del_sema(int semid,int nsems){
    int i=0;
    for(;i<nsems;i++)
           semctl(semid,i,IPC_RMID);
    //删除文件
   // remove("./share_file");
}
//加锁P操作,资源-1
void p_sema(int semid,int sembuf[],int n){//传入要设置的信号量编号集合
    struct sembuf sem_buf[n];
    int i=0;
    for(;i<n;i++){
         sem_buf[i].sem_num=sembuf[i];
         sem_buf[i].sem_op=-1;//p操作-1
         sem_buf[i].sem_flg=SEM_UNDO;
    }
    semop(semid,sem_buf,n);
}
//释放锁v操作,资源(值)加一
void v_sema(int semid,int sembuf[],int n){
    struct sembuf sem_buf[n];
    int i=0;
    for(;i<n;i++){
         sem_buf[i].sem_num=sembuf[i];
         sem_buf[i].sem_op=1;//v操作+1
         sem_buf[i].sem_flg=SEM_UNDO;
    }
    semop(semid,sem_buf,n);
}
#endif

父子进程通信程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "semaphore.h"
#include <signal.h>
#define NSEMS 1
int semID = -1;
void signal_fun(int signo)
{ //捕获删除
    del_sema(semID, NSEMS);
    exit(-1);
}
int main(void)
{
    int ret = 0;
    /*fork()前打开open*/
    int fd = -1;
    // fork之前无需O_APPEND,父子进程不相互干扰
    fd = open("./share_file", O_RDWR | O_CREAT | O_TRUNC, 0664);
    if (fd == -1)
        print_err("open fails\n");
    // fork前创建信号量
    semID = create_or_get_sem(NSEMS);
    //父进程初始化
    int i;
    for (i = 0; i < NSEMS; i++)
        init_sema(semID, i, 1); //谁先来谁使用
    ret = fork();
    if (ret > 0)
    { //父进程写数据
        //父进程捕获
        signal(SIGINT, signal_fun);
        int sem_op[1] = {0}; //定义要操作的编号集合
        while (1)
        {
            sem_op[0] = 0;
            p_sema(semID, sem_op, 1); // P加锁,资源减一
            write(fd, "aaaaaaaaa ", 10);
            write(fd, "bbbbbbbbb\n", 10);
            sem_op[0] = 0;
            v_sema(semID, sem_op, 1); // v释放锁,资源加一
        }
    }
    else if (ret == 0)
    {                        //子进程写数据
        int sem_op[1] = {0}; //定义要操作的编号集合
        while (1)
        {
            sem_op[0] = 0;
            p_sema(semID, sem_op, 1); // P加锁,资源减一
            write(fd, "ccccccccc ", 10);
            write(fd, "ddddddddd\n", 10);
            sem_op[0] = 0;
            v_sema(semID, sem_op, 1); // v释放锁,资源加一
        }
    }
    return 0;
}

程序复习

#ifndef MY_SEMA
#define MY_SEMA
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
//创建或得到一个信号量的semId
//semget(key,信号量集合个数,0664|IPC_CREAT);
int create_get_sema(int numSema){
    int fd=-1;
    key_t key=0;
    int semId=0;
    fd=open("./share_file",O_CREAT|O_RDWR,0664);
    key=ftok("./share_file",'a');
    semId=semget(key,numSema,0664|IPC_CREAT);
}
//联合:cmd的第四个可变参数
union semun {
		int  val;    
		struct semid_ds *buf;    
};
//初始化信号量
//semctl(semId,信号量集合个数,SETVAL,初值)
void init_sema(int semId,int numSema,int val){
    union semun ss;
    ss.val=val;
    semctl(semId,numSema,SETVAL,ss);
}
//删除信号量
//semctl(semId,信号量的集合个数,IPC_RMID)
void del_sema(int semId,int n){
   int i;
   for(i=0;i<n;i++)
         semctl(semId,i,IPC_RMID);
}
//P操作,加锁,资源-1
//sembuf成员:信号量编号;pv(+1,-1);IPC_NOWAIT//SEM_UNDO
//semop(semid,sembuf数组地址,数组个数)
void p_sema_plus(int semId,int semaSet[],int n){
    struct sembuf semBuf[n];
    int i;
    for(i=0;i<n;i++){
        semBuf[i].sem_num=i;
        semBuf[i].sem_op=-1;
        semBuf[i].sem_flg=SEM_UNDO;
    }
    semop(semId,semBuf,n);
}
//v操作,释放锁,资源+1
void v_sema_minus(int semId,int semaSet[],int n){
    struct sembuf semBuf[n];
    int i;
    for(i=0;i<n;i++){
        semBuf[i].sem_num=i;
        semBuf[i].sem_op=1;//v+1
        semBuf[i].sem_flg=SEM_UNDO;
    }
    semop(semId,semBuf,n);
}
#endif
#include"semaphore.h"
#include<signal.h>
#include<stdlib.h>
#define SEMA 1
int semId=-1;
void signal_fun(int signo){
    del_sema(semId,SEMA);
    exit(-1);
}
int main(){
    int fd=-1;
    fd=open("./share_file",O_CREAT|O_RDWR|O_TRUNC,0664);
    semId=create_get_sema(SEMA);
    /*信号量初始化*/
    int i;
    for(i=0;i<SEMA;i++)
        init_sema(semId,i,1);
    int ret=fork();
    if(ret>0){//父进程写,一定别加等号
           signal(SIGINT,signal_fun);//父进程调用捕获
           int semaSET[1]={0};
       while(1){
           //p,v操作前必须定义信号量集合
           semaSET[0]=0;
           p_sema_plus(semId,semaSET,1);
           write(fd,"hello\t",6);
           write(fd,"world\n",6);
           semaSET[0]=0;//只有一个信号量,编号为0
           v_sema_minus(semId,semaSET,1);
       }
    }
    else if(ret==0){
           int semaSET[1]={0};
       while(1){
           semaSET[0]=0;
           p_sema_plus(semId,semaSET,1);
           write(fd,"learn\t",6);
           write(fd,"happy\n",6);
           semaSET[0]=0;//只有一个信号量,编号为0
           v_sema_minus(semId,semaSET,1);
       }
    }
    return 0;
}

信号量实现同步

什么是同步

同步本身就是互斥的,让多个进程按照固定的步调做事(多了顺序)

让三个进程按照顺序打印:11111,22222,33333

#ifndef MY_SEMA
#define MY_SEMA
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
/*错误打印函数*/
void print_err(char* str){
    perror(str);
    exit(-1);
}
/*创建信号量*/
int create_get_sema(int numSema){
    int fd=-1;
    key_t key=0;
    int semId=0;
    fd=open("./share_file",O_RDWR|O_CREAT|O_TRUNC,0664);
    if(fd==-1) print_err("open fails\n");
    key=ftok("./share_file",'a');
    if(key==-1) print_err("ftok fails\n");
    semId=semget(key,numSema,IPC_CREAT|0664);
    if(semId==-1) print_err("semget fails\n");
    return semId;
}
union semun{
     int val;
     struct semid_ds* buf;
};
/*初始化信号量*/
void init_sema(int semId,int numSema,int val){
    union semun sema;
    sema.val=val;
    semctl(semId,numSema,SETVAL,sema);
}
/*删除信号量*/
void del_sema(int semId,int numSema){
    semctl(semId,0,IPC_RMID);//一下子全删
    remove("./share_file");
}
/*P操作,加锁,资源-1*/
void p_sema_minus(int semId,int bufop[],int n){
    int i;
    struct sembuf sem_op[n];
    for(i=0;i<n;i++){
        sem_op[i].sem_num=bufop[i];
        sem_op[i].sem_op=-1;
        sem_op[i].sem_flg=SEM_UNDO;
    }
    semop(semId,sem_op,n);
}
/*V操作,释放锁,资源+1*/
void v_sema_plus(int semId,int bufop[],int n){
    int i;
    struct sembuf sem_op[n];
    for(i=0;i<n;i++){
        sem_op[i].sem_num=bufop[i];
        sem_op[i].sem_op=1;
        sem_op[i].sem_flg=SEM_UNDO;
    }
    semop(semId,sem_op,n);
}
#endif
#include"semaphore.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#define SEMA 3
int semId=-1;
void signal_fun(int signo){
    del_sema(semId,SEMA);
    exit(-1);
}
int main(void){
     int buf_sema[1]={0};
      int ret=0;
    /*创建信号量集合*/
    semId=create_get_sema(SEMA);
    /*初始化信号量集合*/
    int i;
    for(i=0;i<SEMA;i++){
        if(i==0) init_sema(semId,i,1);
        else init_sema(semId,i,0);
    }
    /*三个亲缘进程*/
    //信号量数组,每次pv只针对一个信号量
    ret=fork();
    if(ret>0){
           //父进程再次fork()
           ret=fork();
           if(ret>0){//父进程
               while(1){
                  buf_sema[0]=2;
                  p_sema_minus(semId,buf_sema,1);
                  printf("33333\n");
                  sleep(1);
                  buf_sema[0]=0;
                  v_sema_plus(semId,buf_sema,1);
               }
           }
           else if(ret==0){//子进程2
              while(1){
                buf_sema[0]=1;
                p_sema_minus(semId,buf_sema,1);
                printf("22222\n");
                sleep(1);
                buf_sema[0]=2;
                v_sema_plus(semId,buf_sema,1);
                }
           }
    }
    else if(ret==0){//子进程1
              //负责删除信号量
              signal(SIGINT,signal_fun);
              while(1){
               buf_sema[0]=0;
               p_sema_minus(semId,buf_sema,1);
               printf("11111\n");
               sleep(1);
               buf_sema[0]=1;
               v_sema_plus(semId,buf_sema,1);
              }
    }
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nvz3DdYy-1669371657448)(/home/guojiawei/.config/Typora/typora-user-images/image-20221124181216831.png)]

易错点
int i;
    for(i=0;i<SEMA;i++){
        if(i==0) init_sema(semId,i,1);
        else init_sema(semId,i,0);//else千万别扔了
  }
/*或者*/
int i;
    for(i=0;i<SEMA;i++){
        if(i==0) {init_sema(semId,i,1);
                  break}//跳出,因为不想执行下述代码
        init_sema(semId,i,0);
  }
  • 创建信号量指定的是要用到的信号量总数,这里是3

  • 但是pv操作传入的要是具体的某一个信号量的数组集合

    int buf_sema[1]={0};
    buf_sema[0]=2;
    p_sema_minus(semId,buf_sema,1);
    buf_sema[0]=0;
    v_sema_plus(semId,buf_sema,1);
    

    创建的三个信号量分别为:0,1,2

    每次p,v只针对一个信号量,也就是信号量数组只有一个元素

    • 数组【下标】=传入的信号量编号

回顾共享内存实现通信

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMSIZE 2046
int shmId=-1;
void* shmAddr=NULL;
char buf[100]={"aaaaaaaaabbbbbbbbbbbcccccccccccccccc"};
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
    open("./sharefile",O_RDWR|O_CREAT,0664);
    key_t key=ftok("./sharefile",'a');
    shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
    //删除前必须取消映射
    shmdt(shmAddr);
    //删除共享内存
    shmctl(shmId,IPC_RMID,NULL);
    remove("./sharefile");
    exit(-1);
}
int main(){
    signal(SIGINT,signal_fun);
    create_get_shmId();
    //NULL:内核自动分配虚拟地址
    shmAddr=shmat(shmId,NULL,SHM_RND);
    //判错
    if(shmAddr==(void*)-1){
        perror("shmat fails\n");
        exit(-1);
    }
    //往共享内存拷贝数据
    while(1){
        memcpy(shmAddr,buf,sizeof(buf));
        sleep(2);
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMSIZE 2046
int shmId=-1;
void* shmAddr=NULL;
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
    open("./sharefile",O_RDWR|O_CREAT,0664);
    key_t key=ftok("./sharefile",'a');
    shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
    //删除前必须取消映射
    shmdt(shmAddr);
    //删除共享内存
    shmctl(shmId,IPC_RMID,NULL);
    remove("./sharefile");
    exit(-1);
}
int main(){
    signal(SIGINT,signal_fun);
    create_get_shmId();
    //NULL:内核自动分配虚拟地址
    shmAddr=shmat(shmId,NULL,SHM_RND);
    //判错
    if(shmAddr==(void*)-1){
        perror("shmat fails\n");
        exit(-1);
    }
    //往共享内存拷贝数据
    while(1){
        if(strlen((char *)shmAddr)!=0){//需要判断,不然打出无用空白
        printf("%s\n",(char*)shmAddr);//别忘了类型强转
        bzero(shmAddr,sizeof(shmAddr));
        }
    }
    return 0;
}
  • 共享内存中:shmat()返回的(void*)shmAddr十分重要

不管是读还是写都需要这个地址,如果读,强转:(char*)

  • 删除需要取消映射

也就是所有程序都需要shmdt(shmAddr)后才能删了共享内存

即:shmctl(shmId,IPC_RMID,NULL)

用信号量解决程序等待问题

读进程如果优先写进程执行,共享内存没数据,会一直循环等待,从而浪费CPU

必须:写进程先写,读进程后读,读写进程有顺序,需要用信号量同步

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "semaphore.h"
#define SHMSIZE 2046
int shmId=-1;
int semaId=-1;
void* shmAddr=NULL;
char buf[100]={"aaaaaaaaabbbbbbbbbbbcccccccccccccccc"};
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
    open("./sharefile",O_RDWR|O_CREAT,0664);
    key_t key=ftok("./sharefile",'a');
    shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
    //删除前必须取消映射
    shmdt(shmAddr);
    //删除共享内存
    shmctl(shmId,IPC_RMID,NULL);
    remove("./sharefile");
    exit(-1);
}
int main(){
    signal(SIGINT,signal_fun);
    semaId=create_get_sema(2);
    //sem 0=1; sem 1=0;
    init_sema(semaId,0,1);
    init_sema(semaId,1,0);
    int semaSet[1]={0};
    create_get_shmId();
    //NULL:内核自动分配虚拟地址
    shmAddr=shmat(shmId,NULL,SHM_RND);
    //判错
    if(shmAddr==(void*)-1){
        perror("shmat fails\n");
        exit(-1);
    }
    //往共享内存拷贝数据
    while(1){
        //p 0
        semaSet[0]=0;//传入第一个信号量
        p_sema_plus(semaId,semaSet,1);
        memcpy(shmAddr,buf,sizeof(buf));
        sleep(2);
        //v 1
        semaSet[0]=1;//传入第二个信号量
        v_sema_minus(semaId,semaSet,1);
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "semaphore.h"
#define SHMSIZE 2046
int shmId=-1;
void* shmAddr=NULL;
int semaId=-1;
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
    open("./sharefile",O_RDWR|O_CREAT,0664);
    key_t key=ftok("./sharefile",'a');
    shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
    //删除前必须取消映射
    shmdt(shmAddr);
    //删除共享内存
    shmctl(shmId,IPC_RMID,NULL);
    del_sema(semaId);
    remove("./sharefile");
    exit(-1);
}
int main(){
    signal(SIGINT,signal_fun);
    semaId=create_get_sema(2);
    create_get_shmId();
    int semaSet[1]={0};
    /*初始化信号量只需要一个进程
    init_sema(semaId,0,1);
    init_sema(semaId,1,0);*/
    //NULL:内核自动分配虚拟地址
    shmAddr=shmat(shmId,NULL,SHM_RND);
    //判错
    if(shmAddr==(void*)-1){
        perror("shmat fails\n");
        exit(-1);
    }
    //往共享内存拷贝数据
    while(1){
        semaSet[0]=1;
        p_sema_plus(semaId,semaSet,1);
        //无需if,肯定有数据
        printf("%s\n",(char*)shmAddr);//别忘了类型强转
        bzero(shmAddr,sizeof(shmAddr));
        semaSet[0]=0;
        v_sema_minus(semaId,semaSet,1);
    }
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

信号量的使用 的相关文章

  • 一个体验超级好的chatgpt国产镜像

    镜像网址 xff1a https chatgpt 6om net 1 xff0c 首页的ui xff0c 基本与chatgpt官方一致 2 xff0c 聊天窗 xff0c 反馈很好 3 xff0c 写代码 xff0c 体验也很棒哟
  • ChatGPT国内初次体验:聊天、Python代码生成等

    ChatGPT国内镜像站初体验 xff0c 聊天 Python代码生成 ChatGPT官网 xff1a https ai com ChatGPT 最近ChatGPT传得沸沸扬扬 xff0c 红得发紫 由于主站在国外 xff0c 我没有资源和
  • 马云回国,首谈ChatGPT

    马云今天回国了 xff0c 这是一个备受关注的消息 作为中国最具代表性的企业家之一 xff0c 马云在过去的二十多年里 xff0c 带领阿里巴巴从一个小小的创业公司 xff0c 发展成为全球最大的电商平台之一 xff0c 同时也推动了中国互
  • 下一代的新操作系统就是ChatGPT!

    什么是CHatgpt xff1f ChatGPT是人工智能研究实验室OpenAI在2022年11月30日推出的聊天机器人模型 xff0c 它使用Transformer神经网络架构 xff0c 训练数据来自包括维基百科 xff0c 以及真实对
  • ChatGPT云桌面:无需科技挂载,即点即用

    ChatGPT是一个由OpenAI开发的人工智能对话语言模型 它被设计为对话式人工智能代理 xff0c 用于客户服务 个人助理和文娱等任务 它可以理解并生成多种语言的文本 xff0c 包括中文 英语 西班牙语 德语等 但从某些地方访问Cha
  • 不写代码、靠“玩” ChatGPT 年入百万,提示工程师正变成硅谷新宠

    以下文章转发自来源于硅星人 xff0c 作者章姝敏 随着 ChatGPT 等 AI 工具的风靡 xff0c 越来越多人将其作为日常生活和工作的助手 为了能更好地与它对话 xff0c 一个新兴职业 提示工程师应运而生 用自然语言对话 xff0
  • 有个周入百万的项目:教人用ChatGPT。

    第一批靠 ChatGPT 赚大钱的人 xff0c 既不是研发人员 xff0c 也不是国内大厂 xff0c 又是这群卖课的 xff1f 不知道大家有没有刷到过下面这张图 xff0c 有人统计了知识星球上 xff0c 这段时间跟 GPT 相关课
  • 用ChatGPT搭建公司内部ChatGPT服务器

    一 前言 我是ChatGPT xff0c 一个由OpenAI训练的大型语言模型 我被设计用于回答各种问题并生成文本 xff0c 可以处理多种自然语言任务 xff0c 例如问答 摘要和翻译等 在我的学习过程中 xff0c 我阅读了数百万篇文本
  • 通过串口波特率计算数据传输速率(每秒字节数)

    这个是个古老的问题 上次用串口还是本科时候 xff0c 最近面试被问到了这个问题竟然整蒙了 xff0c 大致是 xff1a 面试老师问 xff1a 9600的波特率每秒可以传输多少个字节 xff1f 我 xff1a e 直接除以8 emmm
  • ChatGPT搭建AI网站实战

    1 概述 ChatGPT是一款基于GPT 3 5架构的大型语言模型 xff0c 它能够进行自然语言处理和生成对话等任务 作为一款智能化的聊天机器人 xff0c ChatGPT有着广泛的应用场景 xff0c 如在线客服 智能助手 个性化推荐等
  • Midjourney关键词分享!附输出AI绘画参考图

    Midjourney 关键词是指用于 Midjourney 这个 AI 绘画工具的文本提示 xff0c 可以影响生成图像的风格 内容 细节等 Midjourney 关键词有一些基本的语法规则和套用公式 xff0c 也有一些常用的风格词汇和描
  • 【AI绘画】——Midjourney关键词格式解析(常用参数分享)

    目前在AI绘画模型中 xff0c Midjourney的效果是公认的top级别 xff0c 但同时也是相对较难使用的 xff0c 对小白来说比较难上手 xff0c 主要就在于Mj没有webui xff0c 不能选择参数 xff0c 怎么找到
  • ChatGPT总是答非所问?如何使用chatgpt定义角色

    一 x1f4dd 定义角色 xff1a ChatGPT 的角色设定 背景信息 xff1a 提供详细 准确的背景信息和前提条件 xff0c 以便 ChatGPT 提供有针对性的回答和建议 任务目标 xff1a 清晰地描述希望 ChatGPT
  • 免费ChatGPT接入-国内怎么玩chatGPT

    免费ChatGPT中文版 OpenAI 的 GPT 模型目前并不提供中文版的免费使用 xff0c 但是有许多机器学习平台和第三方服务提供商也提供了基于 GPT 技术的中文版模型和 API 下面是一些常见的免费中文版 ChatGPT xff1
  • 分享一个国内可用的免费GPT-AI网站

    背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具 xff0c 近期的热度直接沸腾 x1f30b 我们也忍不住做了一个基于ChatGPT的网站 xff0c 可以免登陆 xff01 xff01 国内可直接对话AI xff0c 也有
  • ChatGPT狂飙之下 云计算加速键启动?

    集微网消息 xff0c ChatGPT一夜爆火 xff0c 成为AI届的 流量收割机 有观点认为 xff0c ChatGPT不仅是AI的成功 xff0c 也是云计算的成功 AIGC xff08 人工智能生成内容 xff09 有望开启云计算产
  • ChatGPT :国内免费可用 ChatGPT +Midjourney绘图

    前言 ChatGPT xff08 全名 xff1a Chat Generative Pre trained Transformer xff09 xff0c 美国OpenAI 研发的聊天机器人程序 xff0c 于2022年11月30日发布 C
  • 分享一个国内免费的ChatGPT镜像网址(亲测有效-5月7日更新)

    最近由于ChatGPT的爆火也让很多小伙伴想去感受一下ChatGPT的魅力 xff0c 那么今天就分享几个ChatGPT国内的镜像网址 xff0c 大家可以直接使用 xff01 记得点赞收藏一下呦 xff01 1 Mental AI 地址

随机推荐