Linux之信号量比较全面个人总结Word下载.docx

上传人:b****8 文档编号:22860434 上传时间:2023-02-05 格式:DOCX 页数:25 大小:27.10KB
下载 相关 举报
Linux之信号量比较全面个人总结Word下载.docx_第1页
第1页 / 共25页
Linux之信号量比较全面个人总结Word下载.docx_第2页
第2页 / 共25页
Linux之信号量比较全面个人总结Word下载.docx_第3页
第3页 / 共25页
Linux之信号量比较全面个人总结Word下载.docx_第4页
第4页 / 共25页
Linux之信号量比较全面个人总结Word下载.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

Linux之信号量比较全面个人总结Word下载.docx

《Linux之信号量比较全面个人总结Word下载.docx》由会员分享,可在线阅读,更多相关《Linux之信号量比较全面个人总结Word下载.docx(25页珍藏版)》请在冰豆网上搜索。

Linux之信号量比较全面个人总结Word下载.docx

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

sleepers:

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

2.内核信号量中的等待队列(删除,没有联系)

上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。

当某任务由于没有某种条件没有得到满足时,它就被挂到等待队列中睡眠。

当条件得到满足时,该任务就被移出等待队列,此时并不意味着该任务就被马上执行,因为它又被移进工作队列中等待CPU资源,在适当的时机被调度。

内核信号量是在内部使用等待队列的,也就是说该等待队列对用户是隐藏的,无须用户干涉。

由用户真正使用的等待队列我们将在另外的篇章进行详解。

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

(1)初始化:

voidsema_init(structsemaphore*sem,intval);

voidinit_MUTEX(structsemaphore*sem);

//将sem的值置为1,表示资源空闲

voidinit_MUTEX_LOCKED(structsemaphore*sem);

//将sem的值置为0,表示资源忙

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

voiddown(structsemaphore*sem);

//可引起睡眠

intdown_interruptible(structsemaphore*sem);

//down_interruptible能被信号打断

intdown_trylock(structsemaphore*sem);

//非阻塞函数,不会睡眠。

无法锁定资源则

马上返回

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

voidup(structsemaphore*sem);

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

在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。

Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。

ssize_tglobalvar_write(structfile*filp,constchar*buf,size_tlen,loff_t*off)

{

 //获得信号量

 if(down_interruptible(&

sem))

 {

  return-ERESTARTSYS;

 }

 //将用户空间的数据复制到内核空间的global_var

 if(copy_from_user(&

global_var,buf,sizeof(int)))

  up(&

sem);

  return-EFAULT;

 //释放信号量

 up(&

 returnsizeof(int);

}

四.POSIX信号量与SYSTEMV信号量的比较

1.对POSIX来说,信号量是个非负整数。

常用于线程间同步。

而SYSTEMV信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为SYSTEMVIPC服务的,信号量只不过是它的一部分。

常用于进程间同步。

2.POSIX信号量的引用头文件是“<

semaphore.h>

”,而SYSTEMV信号量的引用头文件是“<

sys/sem.h>

”。

3.从使用的角度,SystemV信号量是复杂的,而Posix信号量是简单。

比如,POSIX信号量的创建和初始化或PV操作就很非常方便。

五.POSIX信号量详解

1.无名信号量

无名信号量的创建就像声明一般的变量一样简单,例如:

sem_tsem_id。

然后再初始化该无名信号量,之后就可以放心使用了。

无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。

也就是说,无名信号量必须是多个进程(线程)的共享变量,无名信号量要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。

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

sem_destroy

intsem_init(sem_t*sem,intpshared,unsignedintvalue);

 

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

2)若pshared>

0用于多个相关进程间的同步(即由fork产生的)

intsem_getvalue(sem_t*sem,int*sval);

取回信号量sem的当前值,把该值保存到sval中。

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

 

1)返回0

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

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

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

intsem_wait(sem_t*sem);

//这是一个阻塞的函数

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

若sem>

0,那么它减1并立即返回。

若sem==0,则睡眠直到sem>

0,此时立即减1,然后返回。

intsem_trywait(sem_t*sem);

//非阻塞的函数

其他的行为和sem_wait一样,除了:

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

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

intsem_post(sem_t*sem);

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

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

注意:

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

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

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

#include<

pthread.h>

sys/types.h>

stdio.h>

unistd.h>

intnumber;

//被保护的全局变量

sem_tsem_id;

void*thread_one_fun(void*arg)

sem_wait(&

sem_id);

printf("

thread_onehavethesemaphore\n"

);

number++;

number=%d\n"

number);

sem_post(&

void*thread_two_fun(void*arg)

thread_twohavethesemaphore\n"

number--;

intmain(intargc,char*argv[])

number=1;

pthread_tid1,id2;

sem_init(&

sem_id,0,1);

pthread_create(&

id1,NULL,thread_one_fun,NULL);

id2,NULL,thread_two_fun,NULL);

pthread_join(id1,NULL);

pthread_join(id2,NULL);

printf("

main,,,\n"

return0;

上面的例程,到底哪个线程先申请到信号量资源,这是随机的。

如果想要某个特定的顺序的话,可以用2个信号量来实现。

例如下面的例程是线程1先执行完,然后线程2才继续执行,直至结束。

sem_tsem_id1,sem_id2;

sem_id1);

sem_id2);

sem_init(&

sem_id1,0,1);

//空闲的

sem_id2,0,0);

//忙的

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

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

产生)的。

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

此时,mutex就可以用来同步相关进程了。

errno.h>

stdlib.h>

sys/stat.h>

fcntl.h>

sys/mman.h>

intmain(intargc,char**argv)

intfd,i,count=0,nloop=10,zero=0,*ptr;

sem_tmutex;

//openafileandmapitintomemory

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,initializesemaphore*/

if(sem_init(&

mutex,1,1)<

0)//

{

perror("

semaphoreinitilization"

exit(0);

}

if(fork()==0)

{/*childprocess*/

for(i=0;

i<

nloop;

i++)

sem_wait(&

mutex);

printf("

child:

%d\n"

(*ptr)++);

sem_post(&

/*backtoparentprocess*/

parent:

2.有名信号量

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

这决定了它的用途非常广:

既可以用于线程,也可以用于相关进程间,甚至是不相关进程。

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

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

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

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

区别是有名信号量使用sem_open代替sem_init,另外在结束的时候要像关闭文件一样去关闭这个有名信号量。

(1)打开一个已存在的有名信号量,或创建并初始化一个有名信号量。

一个单一的调用就完成了信号量的创建、初始化和权限的设置。

sem_t*sem_open(constchar*name, 

intoflag,mode_tmode,intvalue);

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>

File1:

server.c<

/u>

sys/ipc.h>

sys/shm.h>

#defineSHMSZ27

charSEM_NAME[]="

vik"

;

intmain()

charch;

intshmid;

key_tkey;

char*shm,*s;

sem_t*mutex;

//namethesharedmemorysegment

key=1000;

//create&

initializesemaphore

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

if(mutex==SEM_FAILED)

unabletocreatesemaphore"

sem_unlink(SEM_NAME);

exit(-1);

//createthesharedmemorysegmentwiththiskey

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

if(shmid<

0)

failureinshmget"

//attachthissegmenttovirtualmemory

shm=shmat(shmid,NULL,0);

//startwritingintomemory

s=shm;

for(ch='

A'

ch<

='

Z'

ch++)

sem_wait(mutex);

*s++=ch;

sem_post(mutex);

//thebelowloopcouldbereplacedbybinarysemaphore

while(*shm!

='

*'

sleep

(1);

sem_close(mutex);

shmctl(shmid,IPC_RMID,0);

exit(0);

File2:

client.c<

initializeexistingsemaphore

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

reader:

unabletoexecutesemaphore"

shmid=shmget(key,SHMSZ,0666);

//startreading

for(s=shm;

*s!

=NULL;

s++)

putchar(*s);

//oncedonesignalexitingofreader:

Thiscanbereplacedbyanothersemaphore

*shm='

六.SYSTEMV信号量

这是信号量值的集合,而不是单个信号量。

相关的信号量操作函数由<

引用。

1.信号量结构体

内核为每个信号量集维护一个信号量结构体,可在<

找到该定义:

structsemid_ds{

structipc_permsem_perm;

/*信号量集的操作许可权限*/

structsem*sem_base;

/*某个信号量sem结构数组的指针,当前信号量集

中的每个信号量对应其中一个数组元素*/

ushortsem_nsems;

/*sem_base数组的个数*/

time_tsem_otime;

/*最后一次成功修改信号量数组的时间*/

time_tsem_ctime;

/*成功创建时间*/

};

structsem{

ushortsemval;

/*

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 电力水利

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1