linux系统⽂件操作sem_open,sem_close,sem_post,sem_wait
信号灯分为有名信号灯和内存信号灯
⼀、Posix有名信号灯
1.posix有名信号灯函数
函数sem_open创建⼀个新的有名信号灯或打开⼀个已存在的有名信号灯。有名信号灯总是既可⽤于线程间的同步,⼜能⽤于进程间的同步。
1. sem_open
名称::
sem_open
功能:
创建并初始化有名信号灯
头⽂件:
#include <semaphore.h>
函数原形:
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信号灯的指针,该结构⾥记录着当前共享资源的数⽬。
/*semopen.c*/#include <stdio.h>#include <stdlib.h>#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>int main(int argc,char**argv){sem_t *sem;if(argc!=2){printf(“please input a file name!\n”);exit(1);}sem=sem_open(argv[1],O_CREAT,0644,1);exit(0);}
2. sem_close
名称::
sem_close
功能:
关闭有名信号灯
头⽂件:
#include
函数原形:
int sem_close(sem_t *sem);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。
⼀个进程终⽌时,内核还对其上仍然打开着的所有有名信号灯⾃动执⾏这样的信号灯关闭操作。不论该进程是⾃愿终⽌的还是⾮⾃愿终⽌的,这种⾃动关闭都会发⽣。
但应注意的是关闭⼀个信号灯并没有将他从系统中删除。这就是说,Posix有名信号灯⾄少是随内核持续的:即使当前没有进程打开着某个信号灯,他的值仍然保持。
3. sem_unlink
名称::
sem_unlink
功能:
从系统中删除信号灯
头⽂件:
#include
函数原形:
int sem_unlink(count char *name);
参数:
name 信号灯的外部名字
返回值:
若成功则返回0,否则返回-1。
有名信号灯使⽤sem_unlink从系统中删除。
每个信号灯有⼀个引⽤计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从⽂件系统中删除。也就是要等待最后⼀个sem_close发⽣。
/*semunlink.c*/#include#include#include#include#includeint main(int argc,char**argv){sem_t *sem;int val;if(argc!=2){printf(“please input a file name!\n”);exit(1);}if((sem_unlink(argv[1]))!=0)perror(“sem_unlink”);elseprintf(“success”);exit(0);}
4. sem_getvalue
名称::
sem_getvalue
功能:
测试信号灯
头⽂件:
#include
函数原形:
int sem_getvalue(sem_t *sem,int *valp);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。
sem_getvalue在由valp指向的正数中返回所指定信号灯的当前值。如果该信号灯当前已上锁,那么返回值或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。
代码
5. sem_wait/sem_trywait
名称::
sem_wait/sem_trywait
功能:
等待共享资源
头⽂件:
#include
函数原形:
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
参数:
sem 指向信号灯的指针
返回值:
返回值:
若成功则返回0,否则返回-1。
我们能⽤sem_wait来申请共享资源,sem_wait函数能测试所指定信号灯的值,如果该值⼤于0,那就将他减1并即时返回。我们就能使⽤申请来的共享资源了。如果该值等于0,调⽤线程就被进⼊睡眠状态,直到该值变为⼤于0,这时再将他减1,函数随后返回。sem_wait操作必须是原⼦的。 sem_wait和sem_trywait的差别是:当所指定信号灯的值已是0时,后者并不将调⽤线程投⼊睡眠。相反,他返回⼀个EAGAIN错误。
下⾯的程式我们先不去运⾏,稍后再运⾏。
代码
6. sem_post
名称::
sem_post
功能:
挂出共享资源
头⽂件:
#include
函数原形:
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem,int *valp);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。
当⼀个线程使⽤完某个信号灯时,他应该调⽤sem_post来告诉系统申请的资源已⽤完。本函数和sem_wait函数的功能正好相反,他把所指定的信号灯的值加1,然后唤醒正在等待该信号灯值变为正数的任意线程。
下⾯的程式我们先不去运⾏,稍后再运⾏。
代码
⼆. 关于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’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)
{
linux下的sleep函数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 2237
I’m finished,my pid is 2238
I’m finished,my pid is 2239
I’m finished,my pid is 2240
问题在于sem信号灯不在共享内存区中。fork出来的⼦进程通常不共享⽗进程的内存空间。⼦进程是在⽗进程内存空间的拷贝上启动的,它跟共享内存不是⼀回事。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论