一、信号量简介

讯号量是解决进程之间的同步与互斥的IPC机制,互斥与同步关系存在的症结在于临界资源。临界资源是在同一个时刻只容许有限个(一般只有一个)进程可以访问(读)或更改(写)的资源,临界资源包括:硬件资源(处理器、内存、存储器以及其他外围设备等)和软件资源(共享代码段、共享结构和变量等)。

讯号量是描述某一种资源是否可用的变量,讯号量的值表示当前可用的资源的数目linux查看信号量命令,若讯号量的值等于0则意味着目前没有可用的资源。

对讯号量进行的两个原子操作(PV操作)

P操作:等待。假如sv小于0,减少sv。假如sv为0,挂起这个进程的执行。

V操作:发送讯号。假如有进程被挂起等待sv,使其恢复执行。假如没有进行被挂起等待sv,降低sv。

Linux系统承继了Unix系统的两种讯号量:

A、内核讯号量,由内核控制路径使用

B、用户态进程使用的讯号量,分为POSIX讯号量和SYSTEMV讯号量。POSIX讯号量又分为有名讯号量和无名讯号量,有名讯号量,其值保存在文件中,可用于线程、进程间的同步;无名讯号量linux deepin,其值保存在显存中。

POSIX讯号量与SYSTEMV讯号量的区别如下:

A、POSIX讯号量是个非负整数,常用于线程间同步。SYSTEMV讯号量是一个或多个讯号量的集合,是一个讯号量结构体,讯号量是它的一部份,常用于进程间同步。

B、POSIX讯号量的引用头文件是,而SYSTEMV讯号量的引用头文件是。

C、SystemV讯号量是复杂的,而POSIX讯号量是简单的。

二、POSIX无名讯号量1、无名讯号量

无名讯号量的创建与普通变量一样,申明后初始化即可,比如:sem_tsemid=1。无名讯号量常用于多线程间的同步,也可用于相关进程间的同步。无名讯号量必须是多个进程(线程)的共享变量,无名讯号量要保护的变量也必须是多个进程(线程)的共享变量,无名讯号量的值保存在显存中。

常见的无名讯号量相关函数:

int sem_init(sem_t *sem, int pshared, unsigned int value);

pshared==0用于同一多线程的同步;

pshared>0用于多个相关进程间的同步(即由fork形成的)

int sem_destroy(sem_t *sem);

销毁讯号量

int sem_getvalue(sem_t *sem, int *sval);

拿回讯号量sem的当前值linux shell,把值保存到sval中。

若有1个或更多的线程或进程调用sem_wait阻塞在该讯号量上,该函数返回两种值:

1)返回0

2)返回阻塞在该讯号量上的进程或线程数量

linux采用返回的第一种策略。

sem_wait(或sem_trywait)相当于P操作,即申请资源。

int sem_wait(sem_t *sem); // 阻塞的函数

测试所指定讯号量的值,它的操作是原子的。

若sem>0,这么它减1并立刻返回。

若sem==0,则睡眠直至sem>0,此时立刻减1,之后返回。

int sem_trywait(sem_t *sem); // 非阻塞的函数

其他的行为和sem_wait一样,不仅:

若sem==0,不是睡眠,而是返回一个错误EAGAIN。

sem_post相当于V操作,释放资源。

int sem_post(sem_t *sem);

把指定的讯号量sem的值加1;

呼醒正在等待该讯号量的任意线程。

2、无名讯号量在多线程间的同步

无名讯号量的常见用法是即将保护的变量放到sem_wait和sem_post中间所产生的临界区内。

使用讯号量实现多线程同步(线程执行次序随机)实例:

#include 
#include 
#include 

#include #include int number; // 被保护的全局变量 sem_t semid; void* thread_one(void *arg) { sem_wait(&semid); printf("thread_one have the semaphoren"); number++; printf("number = %dn",number); sem_post(&semid); } void* thread_two(void *arg) { sem_wait(&semid); printf("thread_two have the semaphore n"); number--; printf("number = %dn",number); sem_post(&semid); } int main(int argc, char *argv[]) { number = 1; pthread_t tid1, tid2; sem_init(&semid, 0, 1); pthread_create(&tid1,NULL,thread_one, NULL); pthread_create(&tid2,NULL,thread_two, NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); printf("main running...n"); return 0; }

使用讯号量实现多线程同步(线程执行次序指定)实例:

#include 
#include 
#include 
#include 
#include 

查看端口命令 linux_linux命令查看密码_linux查看信号量命令

int number; // 被保护的全局变量 sem_t semid1, semid2; void* thread_one(void *arg) { sem_wait(&semid1); printf("thread_one have the semaphoren"); number++; printf("number = %dn",number); sem_post(&semid1); } void* thread_two(void *arg) { sem_wait(&semid2); printf("thread_two have the semaphore n"); number--; printf("number = %dn",number); sem_post(&semid2); } int main(int argc, char *argv[]) { number = 1; pthread_t tid1, tid2; sem_init(&semid1, 0, 1); sem_init(&semid2, 0, 0); pthread_create(&tid1,NULL,thread_one, NULL); pthread_create(&tid2,NULL,thread_two, NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); printf("main running...n"); sem_destroy(&semid1); sem_destroy(&semid2); return 0; }

3、无名讯号量在亲缘进程间的同步

#include 
#include 

查看端口命令 linux_linux查看信号量命令_linux命令查看密码

#include #include #include #include #include #include #include int main(int argc, char **argv) { int fd, i, nloop=10, zero=0, *ptr; sem_t mutex; //open a file and map it into memory fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU); write(fd,&zero,sizeof(int)); ptr = mmap( NULL,sizeof(int),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0 ); close(fd); /* create, initialize semaphore */ if( sem_init(&mutex,1,1) < 0) // { perror("semaphore initilization"); exit(0); } if (fork() == 0) { /* child process*/ sem_wait(&mutex); for (i = 0; i < nloop; i++) { printf("child: %dn", (*ptr)++); } sem_post(&mutex); exit(0); } /* back to parent process */ sem_wait(&mutex); for (i = 0; i < nloop; i++)

查看端口命令 linux_linux查看信号量命令_linux命令查看密码

{ printf("parent: %dn", (*ptr)++); } sem_post(&mutex); exit(0); }

三、POSIX有名讯号量

有名讯号量的特征是把讯号量的值保存在文件中,既可以用于线程,也可以用于亲缘进程间,无亲缘进程间。

sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);

name是文件的路径名,在linux中sem都是创建在/dev/shm目录下。name可以写成“/mysem”或“mysem”,创建下来的文件都是“/dev/shm/sem.mysem”,千万不要写路径。

oflag有O_CREAT或O_CREAT|EXCL两个取值;

mode控制新的讯号量的访问权限;

value指定讯号量的初始化值。

int sem_getvalue(sem_t *sem, int *sval);

拿回讯号量sem的当前值,把值保存到sval中。

若有1个或更多的线程或进程调用sem_wait阻塞在该讯号量上,该函数返回两种值:

1)返回0

2)返回阻塞在该讯号量上的进程或线程数量

linux采用返回的第一种策略。

sem_wait(或sem_trywait)相当于P操作,即申请资源。

int sem_wait(sem_t *sem); // 阻塞的函数

测试所指定讯号量的值,它的操作是原子的。

若sem>0,这么它减1并立刻返回。

若sem==0,则睡眠直至sem>0,此时立刻减1,之后返回。

int sem_trywait(sem_t *sem); // 非阻塞的函数

其他的行为和sem_wait一样,不仅:

若sem==0,不是睡眠,而是返回一个错误EAGAIN。

sem_post相当于V操作,释放资源。

int sem_post(sem_t *sem);

把指定的讯号量sem的值加1;

呼醒正在等待该讯号量的任意线程。

int sem_close(sem_t *sem);
int sem_unlink(const char *name);

四、SYSTEMV讯号量

SYSTEMV讯号量是SYSTEMVIPC的组成部份,SystemV讯号量在内核中维护,其中包括二值讯号量、计数讯号量、计数讯号量集。

系统中记录讯号量的数据结构(structipc_idssem_ids)坐落内核中linux查看信号量命令,系统中的所有讯号量都可以在结构sem_ids中找到访问入口。

struct semid_ds {
    struct ipc_permsem_perm ;
    struct sem* sem_base ; //信号数组指针
    ushort sem_nsem ; //此集中信号个数
    time_t sem_otime ; //最后一次semop时间
    time_t sem_ctime ; //最后一次创建时间

查看端口命令 linux_linux查看信号量命令_linux命令查看密码

} ;

讯号量编程的流程:

1、创建讯号量或获得在系统已存在的讯号量

调用semget()函数,不同进程使用同一个讯号量通配符来获得同一个讯号量

#include 
int semget (key_t key, int nsem, int oflag) ;

返回值是一个称为讯号量标示符的整数,semop和semctl函数将使用它。

key:所创建或打开讯号量集的通配符。须要是惟一的非零整数。

nsem:创建的讯号量集中的讯号量的个数,该参数只在创建讯号量集时有效。通常取值为1.

oflag:调用函数的操作类型,也可用于设置讯号量集的访问权限,SEM_R(read)和SEM_A(alter),也可以是IPC_CREAT或IPC_EXCL

2、初始化讯号量

使用semctl()函数的SETVAL操作

当使用二维讯号量时,一般将讯号量初始化为1

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

sem_id是由semget返回的讯号量标示符。

sem_num是讯号量集中的某一个资源

cmd:表示即将采取的动作。最常用的两个值如下:

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

IPC_RMID:用于删掉一个无需继续使用的讯号量标志符。

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

3、进行讯号量的PV操作

调用semop()函数

实现进程之间的同步和互斥的核心部份

#include 
int semop (int semid, struct sembuf * opsptr, size_t nops) ;

参数semid是一个通过semget函数返回的一个讯号量标示符

参数opsptr是一个表针,指向一个讯号量操作字段

参数nops标注了参数semoparray所指向链表中的元素个数

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

4、如果不须要讯号量,则从系统中删掉它

使用semclt()函数的IPC_RMID操作,在程序中不应当出现对已被删掉的讯号量的操作。

本文原创地址:https://www.linuxprobe.com/ljczsxhlxhly.html编辑:刘遄,审核员:暂无