信号量上 java 信号量

转:

一.什么是信号量

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。

信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

二.信号量的分类

在学习信号量之前,我们必须先知道——Linux提供两种信号量:

(1)内核信号量,由内核控制路径使用

(2)用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEMV信号量。

POSIX信号量又分为有名信号量和无名信号量。

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

倘若对信号量没有以上的全面认识的话,你就会很快发现自己在信号量的森林里迷失了方向。

三.内核信号量

1.内核信号量的构成

内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。只有在资源被释放时,进程才再次变为可运行。

只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。

内核信号量是struct semaphore类型的对象,它在<asm/semaphore.h>中定义:

struct semaphore {
  atomic_t count;
  int sleepers;
  wait_queue_head_t wait;
  }

count:相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源。

wait:存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。

sleepers:存放一个标志,表示是否有一些进程在信号量上睡眠。

2.内核信号量的相关函数

(1)初始化:

void sema_init (structsemaphore *sem, int val);

void init_MUTEX (structsemaphore *sem);//将sem的值置为1,表示资源空闲

void init_MUTEX_LOCKED(struct semaphore *sem); //将sem的值置为0,表示资源忙

(2)申请内核信号量所保护的资源:

void down(struct semaphore* sem);// 可引起睡眠

intdown_interruptible(struct semaphore * sem); //down_interruptible能被信号打断

int down_trylock(structsemaphore * sem);// 非阻塞函数,不会睡眠。无法锁定资源则

马上返回

(3)释放内核信号量所保护的资源:

void up(struct semaphore *sem);

4.内核信号量的使用例程

在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。

ssize_tglobalvar_write(struct file *filp, const char *buf, size_t len,loff_t *off)
{
 //获得信号量
 if(down_interruptible(&sem))
 {
  return -ERESTARTSYS;
 }
 //将用户空间的数据复制到内核空间的global_var
 if(copy_from_user(&global_var, buf,sizeof(int)))
 {
信号量(上) java 信号量
  up(&sem);
  return -EFAULT;
 }
 //释放信号量
 up(&sem);
 returnsizeof(int);
}

四.POSIX 信号量与SYSTEM V信号量的比较

1.对POSIX来说,信号量是个非负整数。常用于线程间同步。

而SYSTEM V信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为SYSTEM VIPC服务的,信号量只不过是它的一部分。常用于进程间同步。

2.POSIX信号量的引用头文件是“<semaphore.h>”,而SYSTEMV信号量的引用头文件是“<sys/sem.h>”。

3.从使用的角度,System V信号量是复杂的,而Posix信号量是简单。比如,POSIX信号量的创建和初始化或PV操作就很非常方便。

五.POSIX信号量详解

1.无名信号量

无名信号量的创建就像声明一般的变量一样简单,例如:sem_tsem_id。然后再初始化该无名信号量,之后就可以放心使用了。

无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。也就是说,无名信号量必须是多个进程(线程)的共享变量,无名信号量要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。

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

int sem_init(sem_t *sem, int pshared, unsigned intvalue);
1)pshared==0 用于同一多线程的同步;
2)若pshared>0 用于多个相关进程间的同步(即由fork产生的)

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;
呼醒正在等待该信号量的任意线程。

注意:在这些函数中,只有sem_post是信号安全的函数,它是可重入函数

(a)无名信号量在多线程间的同步

无名信号量的常见用法是将要保护的变量放在sem_wait和sem_post中间所形成的临界区内,这样该变量就会被保护起来,例如:

#include<pthread.h>

#include<semaphore.h>

#include<sys/types.h>

#include<stdio.h>

#include<unistd.h>

int number;// 被保护的全局变量

sem_t sem_id;

void* thread_one_fun(void *arg)

{

sem_wait(&sem_id);

printf("thread_one have thesemaphoren");

number++;

printf("number = %dn",number);

sem_post(&sem_id);

}

void* thread_two_fun(void *arg)

{

sem_wait(&sem_id);

printf("thread_two have the semaphoren");

number--;

printf("number = %dn",number);

sem_post(&sem_id);

}

int main(int argc,char *argv[])

{

number = 1;

pthread_t id1, id2;

sem_init(&sem_id, 0, 1);

pthread_create(&id1,NULL,thread_one_fun,NULL);

pthread_create(&id2,NULL,thread_two_fun,NULL);

pthread_join(id1,NULL);

pthread_join(id2,NULL);

printf("main,,,n");

return 0;

}

上面的例程,到底哪个线程先申请到信号量资源,这是随机的。如果想要某个特定的顺序的话,可以用2个信号量来实现。例如下面的例程是线程1先执行完,然后线程2才继续执行,直至结束。

int number;// 被保护的全局变量

sem_t sem_id1, sem_id2;

void* thread_one_fun(void *arg)

{

sem_wait(&sem_id1);

printf("thread_one have thesemaphoren");

number++;

printf("number = %dn",number);

sem_post(&sem_id2);

}

void* thread_two_fun(void *arg)

{

sem_wait(&sem_id2);

printf("thread_two have the semaphoren");

number--;

printf("number = %dn",number);

sem_post(&sem_id1);

}

int main(int argc,char *argv[])

{

number = 1;

pthread_t id1, id2;

sem_init(&sem_id1, 0,1);// 空闲的

sem_init(&sem_id2, 0,0);// 忙的

pthread_create(&id1,NULL,thread_one_fun,NULL);

pthread_create(&id2,NULL,thread_two_fun,NULL);

pthread_join(id1,NULL);

pthread_join(id2,NULL);

printf("main,,,n");

return 0;

}

(b)无名信号量在相关进程间的同步

说是相关进程,是因为本程序中共有2个进程,其中一个是另外一个的子进程(由fork

产生)的。

本来对于fork来说,子进程只继承了父进程的代码副本,mutex理应在父子进程中是相互独立的两个变量,但由于在初始化mutex的时候,由pshared = 1指定了mutex处于共享内存区域,所以此时mutex变成了父子进程共享的一个变量。此时,mutex就可以用来同步相关进程了。

#include<semaphore.h>

#include<stdio.h>

#include<errno.h>

#include<stdlib.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<sys/mman.h>

int main(int argc, char **argv)

{

int fd, i,count=0,nloop=10,zero=0,*ptr;

sem_tmutex;

//opena 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);

if(sem_init(&mutex,1,1) <0)//

{

perror("semaphoreinitilization");

exit(0);

}

if (fork() == 0)

{

for (i = 0; i < nloop;i++)

{

sem_wait(&mutex);

printf("child:%dn", (*ptr)++);

sem_post(&mutex);

}

exit(0);

}

for (i = 0; i < nloop;i++)

{

sem_wait(&mutex);

printf("parent:%dn", (*ptr)++);

sem_post(&mutex);

}

exit(0);

}

2.有名信号量

有名信号量的特点是把信号量的值保存在文件中。

这决定了它的用途非常广:既可以用于线程,也可以用于相关进程间,甚至是不相关进程。

(a)有名信号量能在进程间共享的原因

由于有名信号量的值是保存在文件中的,所以对于相关进程来说,子进程是继承了父进程的文件描述符,那么子进程所继承的文件描述符所指向的文件是和父进程一样的,当然文件里面保存的有名信号量值就共享了。

(b)有名信号量相关函数说明

有名信号量在使用的时候,和无名信号量共享sem_wait和sem_post函数。

区别是有名信号量使用sem_open代替sem_init,另外在结束的时候要像关闭文件一样去关闭这个有名信号量。
(1)打开一个已存在的有名信号量,或创建并初始化一个有名信号量。一个单一的调用就完成了信号量的创建、初始化和权限的设置。

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

name是文件的路径名;

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

mode_t控制新的信号量的访问权限;
Value指定信号量的初始化值。

注意:

这里的name不能写成/tmp/aaa.sem这样的格式,因为在linux下,sem都是创建在/dev/shm目录下。你可以将name写成“/mysem”或“mysem”,创建出来的文件都是“/dev/shm/sem.mysem”,千万不要写路径。也千万不要写“/tmp/mysem”之类的。

当oflag = O_CREAT时,若name指定的信号量不存在时,则会创建一个,而且后面的mode和value参数必须有效。若name指定的信号量已存在,则直接打开该信号量,同时忽略mode和value参数。

当oflag = O_CREAT|O_EXCL时,若name指定的信号量已存在,该函数会直接返回error。

(2) 一旦你使用了一信号量,销毁它们就变得很重要。

在做这个之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量,注意如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作用。

也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。因为每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。

(c)有名信号量在无相关进程间的同步

前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于共享内存区,只有这样才能被无相关的进程所共享。

在下面这个例子中,服务进程和客户进程都使用shmget和shmat来获取得一块共享内存资源。然后利用有名信号量来对这块共享内存资源进行互斥保护。

<u&gt;File1: server.c</u>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include <stdio.h>

#include<semaphore.h>

#include<sys/types.h>

#include<sys/stat.h>

#include <fcntl.h>

#define SHMSZ 27

char SEM_NAME[]= "vik";

int main()

{

charch;

intshmid;

key_tkey;

char*shm,*s;

sem_t*mutex;

//namethe shared memory segment

key= 1000;

//create& initialize semaphore

mutex= sem_open(SEM_NAME,O_CREAT,0644,1);

if(mutex== SEM_FAILED)

{

perror("unable to create semaphore");

sem_unlink(SEM_NAME);

exit(-1);

}

//createthe shared memory segment with this key

shmid= shmget(key,SHMSZ,IPC_CREAT|0666);

if(shmid<0)

{

perror("failurein shmget");

exit(-1);

}

//attachthis segment to virtual memory

shm= shmat(shmid,NULL,0);

//startwriting into memory

s =shm;

for(ch='A';ch<='Z';ch++)

{

sem_wait(mutex);

*s++= ch;

sem_post(mutex);

}

//thebelow loop could be replaced by binary semaphore

while(*shm!= '*')

{

sleep(1);

}

sem_close(mutex);

sem_unlink(SEM_NAME);

shmctl(shmid,IPC_RMID, 0);

exit(0);

}

<u>File 2:client.c</u>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include <stdio.h>

#include<semaphore.h>

#include<sys/types.h>

#include<sys/stat.h>

#include <fcntl.h>

#define SHMSZ 27

char SEM_NAME[]= "vik";

int main()

{

charch;

intshmid;

key_tkey;

char*shm,*s;

sem_t*mutex;

//namethe shared memory segment

key= 1000;

//create& initialize existing semaphore

mutex= sem_open(SEM_NAME,0,0644,0);

if(mutex== SEM_FAILED)

{

perror("reader:unableto execute semaphore");

sem_close(mutex);

exit(-1);

}

//createthe shared memory segment with this key

shmid= shmget(key,SHMSZ,0666);

if(shmid<0)

{

perror("reader:failurein shmget");

exit(-1);

}

//attachthis segment to virtual memory

shm= shmat(shmid,NULL,0);

//startreading

s =shm;

for(s=shm;*s!=NULL;s++)

{

sem_wait(mutex);

putchar(*s);

sem_post(mutex);

}

//once done signal exiting of reader:This can be replaced byanother semaphore

*shm= '*';

sem_close(mutex);

shmctl(shmid,IPC_RMID, 0);

exit(0);

}

  

爱华网本文地址 » http://www.413yy.cn/a/25101015/247646.html

更多阅读

大盘发出量价背离信号 大盘量价背离

大盘发出量价背离信号玖福投资组合 就在市场传言MLF(中期借贷便利)加量续作一日之后,1月21日晚间,央行发布消息证实,近期人民银行续作了到期的2695亿元中期借贷便利,并新增500亿元。1月22日,据交易员透露,央行将在公开市场进行500亿元7天

QuoteTracker技术2:简单信号预警

QuoteTracker技术2:简单信号预警还是那句话,工欲善其事……本帖介绍下简单信号的预警,比较简单,不少朋友应该都知道。我根据实际需要,分为3类:关键点位预警,量能放大预警,趋势线预警。在开始前首先要提醒的是,由于QT软件数据有略微延迟,所以

信号量实例 linux 信号量

PV原语的含义  P操作和V操作是不可中断的程序段,称为原语。PV原语及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量sem是一整数,sem大于等于零时代表可供并发进程使用的资源实体数,但sem小于零时则表示正在等待使用临界区

声明:《信号量上 java 信号量》为网友花凌若别离分享!如侵犯到您的合法权益请联系我们删除