您的位置:首页 > 大数据 > 人工智能

POSIX semaphore: sem_open, sem_close, sem_post, sem_wait

2014-07-01 15:14 253 查看
http://www.cnblogs.com/BloodAndBone/archive/2011/01/18/1938552.html

一、Posix有名信号灯

1.posix有名信号灯函数

函数sem_open创建一个新的有名信号灯或打开一个已存在的有名信号灯。有名信号灯总是既可用于线程间的同步,又能用于进程间的同步。

1. sem_open

名称::

sem_open

功能:

创建并初始化有名信号灯

头文件:

#include

函数原形:

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

参数:

name 信号灯的外部名字

oflag 选择创建或打开一个现有的信号灯

mode 权限位

value 信号灯初始值

返回值:

成功时返回指向信号灯的指针,出错时为SEM_FAILED

oflag参数能是0、O_CREAT(创建一个信号灯)或O_CREAT|O_EXCL(如果没有指定的信号灯就创建),如果指定了O_CREAT,那
么第三个和第四个参数是需要的;其中mode参数指定权限位,value参数指定信号灯的初始值,通常用来指定共享资源的书面。该初始不能超过
SEM_VALUE_MAX,这个常值必须低于为32767。二值信号灯的初始值通常为1,计数信号灯的初始值则往往大于1。

如果指定了O_CREAT(而没有指定O_EXCL),那么只有所需的信号灯尚未存在时才初始化他。所需信号灯已存在条件下指定O_CREAT不是个错
误。该标志的意思仅仅是“如果所需信号灯尚未存在,那就创建并初始化他”。不过所需信号灯等已存在条件下指定O_CREAT|O_EXCL却是个错误。

sem_open返回指向sem_t信号灯的指针,该结构里记录着当前共享资源的数目。

/*sempost.c*/
#include
#include
#include
#include
#include

int main(int argc,char **argv)
{
sem_t *sem;
int val;

if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],0);
sem_post(sem);
sem_getvalue(sem,&val);
printf(“value=%d\n”, val);
exit(0);
}

二. 关于posix有名信号灯使用的几点注意

我们要注意以下几点:

1.Posix有名信号灯的值是随内核持续的。也就是说,一个进程创建了一个信号灯,这个进程结束后,这个信号灯还存在,并且信号灯的值也不会改动。

下面我们利用上面的几个程式来证实这点

#./semopen test

#./semgetvalue test

value=1 信号的值仍然是1

2。当持有某个信号灯锁的进程没有释放他就终止时,内核并不给该信号灯解锁。

#./semopen test

#./semwait test&

pid 1834 has semaphore,value=0

#./semgetvalue test

value=0 信号量的值变为0了

3.posix有名信号灯应用于多线程

#include

#include

#include

#include

#include

void *thread_function(void *arg); /*线程入口函数*/

void print(pid_t); /*共享资源函数*/

sem_t *sem; /*定义Posix有名信号灯*/

int val; /*定义信号灯当前值*/

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

{

int n=0;

if(argc!=2)

{

printf(“please input a file name!\n”);

exit(1);

}

sem=sem_open(argv[1],O_CREAT,0644,3); /*打开一个信号灯*/

while(n++循环创建5个子线程,使他们同步运行*/

{

if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0)

{

perror(“Thread creation failed”);

exit(1);

}

}

pthread_join(a_thread,NULL);

sem_close(bin_sem);

sem_unlink(argv[1]);

}

void *thread_function(void *arg)

{

sem_wait(sem); /*申请信号灯*/

print(); /*调用共享代码段*/

sleep(1);

sem_post(sem); /*释放信号灯*/

printf(“I’m finished,my tid is %d\n”,pthread_self());

}

void print()

{

printf(“I get it,my tid is %d\n”,pthread_self());

sem_getvalue(sem,&val);

printf(“Now the value have %d\n”,val);

}

程式用循环的方法建立5个线程,然后让他们调用同一个线程处理函数thread_function,在函数里我们利用信号量来限制访问共享资源的线程数。共享资源我们用print函数来代表,在真正编程中他有能是个终端设备(如打印机)或是一段有实际意义的代码。

运行结果为:

#gcc ?lpthread ?o 8_1 8_1.c

#./8_1 test

I get it,my tid is 1082330304

Now the value have 2

Iget it,my pid is 1894

Now the value have 1

Iget it,my pid is 1895

Now the value have 0

I’m finished,my pid is 1893

I’m finished,my pid is 1894

I’m finished,my pid is 1895

I get it,my pid is 1896

Now the value have 2

I get it,mypid is 1897

Now the value have 1

I’m finished,my pid is 1896

I’m finished,my pid is 1897

4.posix有名信号灯应用于多进程

下面就是应用Posix有名信号灯的一个小程序。用它来限制访问共享代码的进程数目。

#include

#include

#include

#include

void print(pid_t);

sem_t *sem; /*定义Posix有名信号灯*/

int val; /*定义信号灯当前值*/

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

{

int n=0;

if(argc!=2)

{

printf(“please input a file name!\n”);

exit(1);

}

sem=sem_open(argv[1],O_CREAT,0644,2); /*打开一个信号灯, 初值设为2*/

while(n++循环创建5个子进程,使它们同步运行*/

{

if(fork()==0)

{

sem_wait(sem); /*申请信号灯*/

print(getpid()); /*调用共享代码段*/

sleep(1);

sem_post(sem); /*释放信号灯*/

printf(“I’m finished,my pid is %d\n”,getpid());

return 0;

}

}

wait(); /*等待所有子进程结束*/

sem_close(sem);

sem_unlink(argv[1]);

exit(0);

}

void print(pid_t pid)

{

printf(“I get it,my pid is %d\n”,pid);

sem_getvalue(sem,&val);

printf(“Now the value have %d\n”,val);

}

程序编译后运行会得到如下结果:

#./8_2 8_2.c

I get it,my tid is 1082330304

Now the value have 1

I get it,my tid is 1090718784

Now the value have 0

I finished,my pid is 1082330304

I finished,my pid is 1090718784

I get it,my tid is 1099107264

Now the value have 1

I get it,my tid is 1116841120

Now the value have 0

I finished,my pid is 1099107264

I finished,my pid is 1116841120

I get it,my tid is 1125329600

Now the value have 1

I finished,my pid is 1125329600

三、基于内存的信号灯

前面已经介绍了Posix有名信号灯。这些信号灯由一个name参数标识,它通常指代文件系统中的某个文件。然而Posix也提供基于内存的信号灯,它们由应用程序分配信号灯的内存空间,然后由系统初始化它们的值。

7.

名称::

sem_init/sem_destroy

功能:

初始化/关闭信号等

头文件:

#include

函数原形:

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

int sem_getvalue(sem_t *sem);

参数:

sem 指向信号灯的指针

shared 作用范围

value 信号灯初始值

返回值:

若成功则返回0,否则返回-1。

基于内存的信号灯是由sem_init初始化的。sem参数指向必须由应用程序分配的sem_t变量。如果shared为0,那么待初始化的信号灯是在同
一进程的各个线程共享的,否则该信号灯是在进程间共享的。当shared为零时,该信号灯必须存放在即将使用它的所有进程都能访问的某种类型的共享内存
中。跟sem_open一样,value参数是该信号灯的初始值。

使用完一个基于内存的信号灯后,我们调用sem_destroy关闭它。

除了sem_open和sem_close外,其它的poisx有名信号灯函数都可以用于基于内存的信号灯。

注意:posix基于内存的信号灯和posix有名信号灯有一些区别,我们必须注意到这些。

1.sem_open不需要类型与shared的参数,有名信号灯总是可以在不同进程间共享的。

2.sem_init不使用任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号灯的值。因此,对于一个给定的信号灯,我们必须小心保证只调用一次sem_init。

3.sem_open返回一个指向某个sem_t变量的指针,该变量由函数本身分配并初始化。但sem_init的第一个参数是一个指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init函数初始化。

4.posix有名信号灯是通过内核持续的,一个进程创建一个信号灯,另外的进程可以通过该信号灯的外部名(创建信号灯使用的文件名)来访问它。
posix基于内存的信号灯的持续性却是不定的,如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它
也会消失。如果某个基于内存的信号灯是在不同进程间同步的,该信号灯必须存放在共享内存区中,这要只要该共享内存区存在,该信号灯就存在。

5.基于内存的信号灯应用于线程很麻烦(待会你会知道为什么),而有名信号灯却很方便,基于内存的信号灯比较适合应用于一个进程的多个线程。

下面是posix基于内存的信号灯实现一个进程的各个线程间的互次。

#include

#include

#include

#include

#include

#incude

void *thread_function(void *arg); /*线程入口函数*/

void print(void); /*共享资源函数*/

sem_t bin_sem; /*定义信号灯*/

int value; /*定义信号量的灯*/

int main()

{

int n=0;

pthread_t a_thread;

if((sem_init(&bin_sem,0,2))!=0) /*初始化信号灯,初始值为2*/

{

perror(“sem_init”);

exit(1);

}

while(n++循环创建5个线程*/

{

if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0)

{

perror(“Thread creation failed”);

exit(1);

}

}

pthread_join(a_thread,NULL);/*等待子线程返回*/

}

void *thread_function(void *arg)

{

sem_wait(&bin_sem); /*等待信号灯*/

print();

sleep(1);

sem_post(&bin_sem); /*挂出信号灯*/

printf(“I finished,my pid is %d\n”,pthread_self());

pthread_exit(arg);

}

void print()

{

printf(“I get it,my tid is %d\n”,pthread_self());

sem_getvalue(&bin_sem,&value); /*获取信号灯的值*/

printf(“Now the value have %d\n”,value);

}

posix基于内存的信号灯和有名信号灯基本是一样的,上面的几点区别就可以了。

下面是运行结果:

#gcc –lpthread –o seminitthread seminitthread.c

#./seminitthread

I get it,my tid is 1082330304

Now the value have 1

I get it,my tid is 1090718784

Now the value have 0

I finished,my pid is 1082330304

I finished,my pid is 1090718784

I get it,my tid is 1099107264

Now the value have 1

I get it,my tid is 1116841120

Now the value have 0

I finished,my pid is 1099107264

I finished,my pid is 1116841120

I get it,my tid is 1125329600

Now the value have 1

I finished,my pid is 1125329600

下面的程序并不能得到我们想要的结果。

#include

#include

#include

#include

void print(pid_t);

sem_t *sem; /*定义Posix有名信号灯*/

int val; /*定义信号灯当前值*/

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

{

int n=0;

sem=sem_open(argv[1],O_CREAT,0644,3); /*打开一个信号灯*/

sem_getvalue(sem,&val); /*查看信号灯的值*/

printf(“The value have %d\n”,val);

while(n++循环创建5个子进程,使它们同步运行*/

{

if(fork()==0)

{

sem_wait(sem); /*申请信号灯*/

print(getpid()); /*调用共享代码段*/

sleep(1);

sem_post(sem); /*释放信号灯*/

printf(“I’m finished,my pid is %d\n”,getpid());

return 0;

}

wait(); /*等待所有子进程结束*/

return 0;

}

void print(pid_t pid)

{

printf(“I get it,my pid is %d\n”,pid);

sem_getvalue(sem,&val);

printf(“Now the value have %d\n”,val);

}

下面是运行结果:

#cc –lpthread –o sem sem.c

#./sem

The value have 3

I get it,my pid is 2236

Now the value have 2

I get it,my pid is 2237

Now the value have 2

I get it,my pid is 2238

Now the value have 2

I get it,my pid is 2239

Now the value have 2

Iget it,my pid is 2240

Now the value have 2

I’m finished,my pid is 2236

I’m finished,my pid is 2237

I’m finished,my pid is 2238

I’m finished,my pid is 2239

I’m finished,my pid is 2240

问题在于sem信号灯不在共享内存区中。fork出来的子进程通常不共享父进程的内存空间。子进程是在父进程内存空间的拷贝上启动的,它跟共享内存不是一回事。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: