Posix 信号
转载请注明来源:https://www.cnblogs.com/hookjc/
函数sem_open创建一个新的有名信号灯或打开一个已存在的有名信号灯。有名信号灯总是既可用于线程间的同步,又可以用于进程间的同步。
1.posix有名信号灯函数
1.
名称:: |
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 <semaphore.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.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); } |
#gcc –lpthread –o semopen semopen.c
#./semopen
2.
名称:: |
sem_close |
功能: |
关闭有名信号灯 |
头文件: |
#include <semaphore.h> |
函数原形: |
int sem_close(sem_t *sem); |
参数: |
sem 指向信号灯的指针 |
返回值: |
若成功则返回0,否则返回-1。 |
一个进程终止时,内核还对其上仍然打开着的所有有名信号灯自动执行这样的信号灯关闭操作。不论该进程是自愿终止的还是非自愿终止的,这种自动关闭都会发生。
但应注意的是关闭一个信号灯并没有将它从系统中删除。这就是说,Posix有名信号灯至少是随内核持续的:即使当前没有进程打开着某个信号灯,它的值仍然保持。
3.
名称:: |
sem_unlink |
功能: |
从系统中删除信号灯 |
头文件: |
#include <semaphore.h> |
函数原形: |
int sem_unlink(count char *name); |
参数: |
name 信号灯的外部名字 |
返回值: |
若成功则返回0,否则返回-1。 |
有名信号灯使用sem_unlink从系统中删除。
每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。
/*semunlink.c*/ #include <semaphore.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> int 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”); else printf(“success”); exit(0); } |
4.
名称:: |
sem_getvalue |
功能: |
测试信号灯 |
头文件: |
#include <semaphore.h> |
函数原形: |
int sem_getvalue(sem_t *sem,int *valp); |
参数: |
sem 指向信号灯的指针 |
返回值: |
若成功则返回0,否则返回-1。 |
sem_getvalue在由valp指向的正数中返回所指定信号灯的当前值。如果该信号灯当前已上锁,那么返回值或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。
/*semgetvalue.c*/ #include <semaphore.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> 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_getvalue(sem,&val); printf(“getvalue:value=%d\n”,val); exit(0); } |
5.
名称:: |
sem_wait/sem_trywait |
功能: |
等待共享资源 |
头文件: |
#include <semaphore.h> |
函数原形: |
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错误。
下面的程序我们先不去运行,稍后再运行。
/*semwait.c*/ #include <semaphore.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> 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_wait(sem); sem_getvalue(sem,&val); printf(“pid %ld has semaphore,value=%d\n”,(long)getpid(),val); pause(); exit(0); } |
6.
名称:: |
sem_post |
功能: |
挂出共享资源 |
头文件: |
#include <semaphore.h> |
函数原形: |
int sem_post(sem_t *sem); int sem_getvalue(sem_t *sem,int *valp); |
参数: |
sem 指向信号灯的指针 |
返回值: |
若成功则返回0,否则返回-1。 |
当一个线程使用完某个信号灯时,它应该调用sem_post来告诉系统申请的资源已经用完。本函数和sem_wait函数的功能正好相反,它把所指定的信号灯的值加1,然后唤醒正在等待该信号灯值变为正数的任意线程。
下面的程序我们先不去运行,稍后再运行。
/*sempost.c*/ #include <semaphore.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> 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); } |
2.关于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 <semaphore.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <thread.h> 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) /*循环创建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 <semaphore.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> 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) /*循环创建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 <semaphore.h> |
函数原形: |
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 <semaphore.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <pthread.h> #incude <stdlib.h> 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) /*循环创建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 <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <sys/ipc.h> #include <semaphore.h> #include <fcntl.h> #define FIFO "myfifo" #define N 5 int lock_var; time_t end_time; char buf_r[100]; sem_t mutex,full,avail;/*定义3个信号量,full标识缓冲区是否为满,avail标识缓冲区是否为空.*/ int fd; void pthread1(void *arg); void pthread2(void *arg); void consumer(void *arg); void productor(void *arg); int main(int argc, char *argv[]) { pthread_t id1,id2; pthread_t mon_th_id; int ret; end_time = time(NULL)+30; if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("cannot create fifoserver\n"); printf("Preparing for reading bytes...\n"); memset(buf_r,0,sizeof(buf_r)); fd=open(FIFO,O_RDWR|O_NONBLOCK,0); if(fd==-1) { perror("open"); exit(1); } ret=sem_init(&mutex,0,1); ret=sem_init(&avail,0,N); ret=sem_init(&full,0,0); if(ret!=0) { perror("sem_init"); } ret=pthread_create(&id1,NULL,(void *)productor, NULL); if(ret!=0) perror("pthread cread1"); ret=pthread_create(&id2,NULL,(void *)consumer, NULL); if(ret!=0) perror("pthread cread2"); pthread_join(id1,NULL); pthread_join(id2,NULL); exit(0); } void productor(void *arg) { int i,nwrite; while(time(NULL) < end_time){ sem_wait(&avail); sem_wait(&mutex); if((nwrite=write(fd,"hello",5))==-1) { if(errno==EAGAIN) printf("The FIFO has not been read yet.Please try later\n"); } else printf("write hello to the FIFO\n"); sem_post(&full); sem_post(&mutex); sleep(1); } } void consumer(void *arg) { int nolock=0; int ret,nread; while(time(NULL) < end_time){ sem_wait(&full); sem_wait(&mutex); memset(buf_r,0,sizeof(buf_r)); if((nread=read(fd,buf_r,100))==-1){ if(errno==EAGAIN) printf("no data yet\n"); } printf("read %s from FIFO\n",buf_r); sem_post(&avail); sem_post(&mutex); sleep(1); } } |
下面是posix基于内存的信号灯实现各进程间的互斥。但要注意它并不能得到我们想要的结果。
#include <semaphore.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> 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) /*循环创建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出来的子进程通常不共享父进程的内存空间。子进程是在父进程内存空间的拷贝上启动的,它跟共享内存不是一回事。
来源:python脚本自动迁移
Posix 信号的更多相关文章
- POSIX信号
POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEE ...
- POSIX信号和自定义signal函数
一.信号的概念 信号(signal)就是告知某个进程发生了某个事件的通知:信号通常是异步发生的,也就是说接受信号的进程不知道信号的准确 发生时刻:信号可以(1)由一个进程发给另一个进程:(2)由内核发 ...
- <转>Linux环境进程间通信(二): 信号(下)
原文地址为:http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html 原文为: 一.信号生命周期 从信号发送到信号处理函数的 ...
- Linux环境进程间通信(二):信号(下)
linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...
- UNIX网络编程——信号驱动式I/O
信号驱动式I/O是指进程预先告知内核,使得当某个描述符上发生某事时,内核使用信号通知相关进程. 针对一个套接字使用信号驱动式I/O,要求进程执行以下3个步骤: 建立SIGIO信号的信号处理函数. 设置 ...
- UNIX环境高级编程——信号
一.信号生命周期 从信号发送到信号处理函数的执行完毕. 对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生:信号在进 ...
- Linux环境进程间通信(二): 信号--转载
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html http://www.ibm.com/developerworks ...
- 使用信号进行同步 sem_post
使用信号进行同步 信号是 E. W. Dijkstra 在二十世纪六十年代末设计的一种编程架构.Dijkstra 的模型与铁路操作有关:假设某段铁路是单线的,因此一次只允许一列火车通过. 信号将用于同 ...
- Linux进程间通信—信号
三.信号(Signal) 信号是Unix系统中使用的最古老的进程间通信的方法之一.操作系统通过信号来通知某一进程发生了某一种预定好的事件:接收到信号的进程可以选择不同的方式处理该信号,一是可以采用默认 ...
随机推荐
- Capstone CS5263|DP转HDMI 4K60HZ转换芯片|CS5263芯片|替代PS176芯片
CS5263是一款DP转HDMI 4K60HZ音视频转换器芯片,不管在功能特性还是应用上都是可以完全替代兼容PS176.PS176是一个Display Port 1.2a到HDMI 2.0协议转换器, ...
- Vue的安装及使用(Vue的三种安装使用方式)
vue是一个JavaMVVM库,是一套用于构建用户界面的渐进式框架,是初创项目的首选前端框架.它是以数据驱动和组件化的思想构建的,采用自底向上增量开发的设计.它是轻量级的,它有很多独立的功能或库,我们 ...
- TortoiseGit使用ssh-keygen生成的私钥
1.说明 使用TortoiseGit自带的PuTTY Key Generator工具, 把ssh-keygen生成的私钥转换为Putty使用的.ppk文件, 然后在拉取Git代码时, 加载对应的.pp ...
- Zookeeper基础教程(二):Zookeeper安装
上一篇说了,一个Zookeeper集群一般认为至少需要3个节点,所以我们这里安装需要准备三台虚拟机: # 192.168.209.133 test1 # 192.168.209.134 test2 # ...
- SpringBoot 之 扩展 SpringMVC
增加自定义视图解析器: # src/main/java/com/wu/config/MyMvcConfig.java @Configuration // 标注这个类是一个配置类 public clas ...
- Spring @Valid 和 @Validated 的区别和使用
两者区别 @Valid @Validated 标准 标准JSR-303规范 增强JSR-303规范 包 javax.validation org.springframework.validation ...
- js 简单版发布留言 案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- postgresql使用pg_dump工具进行数据库迁移
一.使用管理员身份打开cmd,切换到postgresqlp安装目录下的bin目录 必须要以管理员身份打开cmd,否则报拒绝访问 二.把生成的文件上传到服务器上,同样cd 到postgresql的安装b ...
- Java数据类型 long 与 Long 的区别 和 正确用法
1.区别 (1) long 是 基本类型 [类似于 int] Long 是 对象类型 [类似于Integer] (2) long 默认值是 0 Long 默认值是 null 2.比较方法 (1) ...
- 创客系列教程——认识LED灯
认识LED灯 一.初识LED灯 LED灯是一种能够将电能转化为可见光的固态的半导体器件,它可以直接把电转化为光.LED灯逐步融入到生活中的方方面面:室内外的照明.电子指示牌.酷炫的舞台灯光.车辆的 ...