linux信号和信号量区别:Linux信号量,2-POSIX

时间:2024-01-05 00:40:33/人气:443 ℃

上一章,讲述了SYSTEM V信号量,主要运行于进程之间,本章主要介绍POSIX信号量:有名信号量、无名信号量。

POSIX信号量

POSIX信号量进程是3种 IPC(Inter-Process Communication) 机制之一,3种 IPC 机制源于 POSIX.1 的实时扩展。Single UNIX Specification 将3种机制(消息队列,信号量和共享存储)置于可选部分中。在 SUSv4 之前,POSIX 信号量接口已经被包含在信号量选项中。在 SUSv4 中,这些接口被移至了基本规范,而消息队列和共享存储接口依然是可选的。

POSIX 信号量接口意在解决 XSI 信号量接口的几个缺陷。

  1. 相比于 XSI 接口,POSIX 信号量接口考虑了更高性能的实现。
  2. POSIX 信号量使用更简单:没有信号量集,在熟悉的文件系统操作后一些接口被模式化了。尽管没有要求一定要在文件系统中实现,但是一些系统的确是这么实现的。
  3. POSIX 信号量在删除时表现更完美。回忆一下,当一个 XSI 信号量被删除时,使用这个信号量标识符的操作会失败,并将 errno 设置成 EIDRM。使用 POSIX 信号量时,操作能继续正常工作直到该信号量的最后一次引用被释放。
分类

POSIX信号量是一个sem_t类型的变量,但POSIX有两种信号量的实现机制:无名信号量和命名信号量。

无名信号量只可以在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步,因此无名信号量也被称作基于内存的信号量;命名信号量通常用于不共享内存的情况下,比如进程间通信。

同时,在创建信号量时,根据信号量取值的不同,POSIX信号量还可以分为:

区别

有名信号量和无名信号量的差异在于创建和销毁的形式上,但是其他工作一样。

无名信号量只能存在于内存中,要求使用信号量的进程必须能访问信号量所在的这一块内存,所以无名信号量只能应用在同一进程内的线程之间(共享进程的内存),或者不同进程中已经映射相同内存内容到它们的地址空间中的线程(即信号量所在内存被通信的进程共享)。意思是说无名信号量只能通过共享内存访问。

相反,有名信号量可以通过名字访问,因此可以被任何知道它们名字的进程中的线程使用。

单个进程中使用 POSIX 信号量时,无名信号量更简单。

多个进程间使用 POSIX 信号量时,有名信号量更简单。

联系

无论是有名信号量还是无名信号量,都可以通过以下函数进行信号量值操作。

wait(P)

wait 为信号量值减一操作,总共有三个函数,函数原型如下:

#include <semaphore.h>int sem_wait(sem_t *sem);int sem_trywait(sem_t *sem);int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);Link with -pthread.这一句表示 gcc 编译时,要加 -pthread.返回值: 若成功,返回 0 ;若出错,返回-1

结构体定义如下:

struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds [0 .. 999999999] */};

如果指定的阻塞时间到了,但是 sem 仍然小于 0 ,则会返回一个错误 (错误设置为 ETIMEDOUT )。

post(V)

post 为信号量值加一操作,函数原型如下:

#include <semaphore.h>int sem_post(sem_t *sem);Link with -pthread.返回值: 若成功,返回 0 ;若出错,返回-1无名信号量接口函数

信号量的函数都以sem_开头,线程中使用的基本信号函数有4个,他们都声明在头文件semaphore.h中,该头文件定义了用于信号量操作的sem_t类型:

sem_init

该函数用于创建信号量,原型如下:

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

功能:

该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享,value为sem的初始值。

返回值:

该函数调用成功返回0,失败返回-1。

sem_destroy

该函数用于对用完的信号量进行清理,其原型如下:

int sem_destroy(sem_t *sem);

返回值:

成功返回0,失败返回-1。

sem_getvalue函数

该函数返回当前信号量的值,通过restrict输出参数返回。如果当前信号量已经上锁(即同步对象不可用),那么返回值为0,或为负数,其绝对值就是等待该信号量解锁的线程数。

int sem_getvalue(sem_t *restrict, int *restrict);

使用实例

【实例1】:

#include <time.h>#include <stdio.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <semaphore.h>sem_t sem;#define handle_error(msg) do { \ perror(msg); \ exit(EXIT_FAILURE); \ }while (0)static void handler(int sig){write(STDOUT_FILENO, "sem_post() from handler\n", 24);if(sem_post(&sem) == -1){ write(STDERR_FILENO, "sem_post() failed\n", 18); _exit(EXIT_FAILURE);}}int main(int argc, char *argv[]){ int s; struct timespec ts; struct sigaction sa; if (argc != 3) { fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>\n", argv[0]); exit(EXIT_FAILURE); } if (sem_init(&sem, 0, 0) == -1) handle_error("sem_init"); /* Establish SIGALRM handler; set alarm timer using argv[1] */ sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGALRM, &sa, NULL) == -1) handle_error("sigaction"); alarm(atoi(argv[1])); /* Calculate relative interval as current time plus number of seconds given argv[2] */ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) handle_error("clock_gettime"); ts.tv_sec = atoi(argv[2]); printf("main() about to call sem_timedwait()\n"); while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR) continue; /* Restart if interrupted by handler */ /* Check what happened */ if (s == -1) { if (errno == ETIMEDOUT) printf("sem_timedwait() timed out\n"); else perror("sem_timedwait"); } else { printf("sem_timedwait() succeeded\n"); } exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);}

【实例2】:

#include <time.h>#include <stdio.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <semaphore.h>sem_t sem;void *func1(void *arg){ sem_wait(&sem); int *running = (int *)arg; printf("thread func1 running : %d\n", *running); pthread_exit(NULL);}void *func2(void *arg){ printf("thread func2 running.\n"); sem_post(&sem); pthread_exit(NULL);}int main(void){ int a = 3; sem_init(&sem, 0, 0); pthread_t thread_id[2]; pthread_create(&thread_id[0], NULL, func1, (void *)&a); printf("main thread running.\n"); sleep(10); pthread_create(&thread_id[1], NULL, func2, (void *)&a); printf("main thread still running.\n"); pthread_join(thread_id[0], NULL); pthread_join(thread_id[1], NULL); sem_destroy(&sem); return 0;}有名信号量

有时候也叫命名信号量,之所以称为命名信号量,是因为它有一个名字、一个用户ID、一个组ID和权限。这些是提供给不共享内存的那些进程使用命名信号量的接口。命名信号量的名字是一个遵守路径名构造规则的字符串。

接口函数

sem_open函数

该函数用于创建或打开一个命名信号量,其原型如下:

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

参数

sem_close函数

该函数用于关闭命名信号量:

int sem_close(sem_t *);

功能:

单个程序可以用sem_close函数关闭命名信号量,但是这样做并不能将信号量从系统中删除,因为命名信号量在单个程序执行之外是具有持久性的。当进程调用_exit、exit、exec或从main返回时,进程打开的命名信号量同样会被关闭。

sem_unlink函数

功能:

sem_unlink函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除:

int sem_unlink(const char *name);

信号量操作函数

与无名信号量一样。

使用实例

#include <time.h>#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <semaphore.h>#define SEM_NAME " /sem_name"sem_t *p_sem;void *testThread(void *ptr){ sem_wait(p_sem); sleep(2); pthread_exit(NULL);} int main(void){ int i = 0; pthread_t pid; int sem_val = 0; p_sem = sem_open(SEM_NAME, O_CREAT, 0555, 5); if(p_sem == NULL) { printf("sem_open %s failed!\n", SEM_NAME); sem_unlink(SEM_NAME); return -1; } for(i = 0; i < 7; i ) { pthread_create(&pid, NULL, testThread, NULL); sleep(1); // pthread_join(pid, NULL); // not needed, or loop sem_getvalue(p_sem, &sem_val); printf("semaphore value : %d\n", sem_val); } sem_close(p_sem); sem_unlink(SEM_NAME); return 0;}命名和无名信号量的持续性

命名信号量是随内核持续的。当命名信号量创建后,即使当前没有进程打开某个信号量,它的值依然保持,直到内核重新自举或调用sem_unlink()删除该信号量。

无名信号量的持续性要根据信号量在内存中的位置确定:

如果无名信号量是在单个进程内部的数据空间中,即信号量只能在进程内部的各个线程间共享,那么信号量是随进程的持续性,当进程终止时他也就消失了;

如果无名信号量位于不同进程的共享内存区,因此只要该共享内存区仍然存在,该信号量就会一直存在;所以此时无名信号量是随内核的持续性。

信号量-互斥量-条件变量

很多时候信号量、互斥量和条件变量都可以在某种应用中使用,那这三者的差异有哪些呢?下面列出了这三者之间的差异:

更多信息请关注:一口Linux

推荐

  • 1做安全人走平安路220
  • 2消防安全重点单位实行管理工作单位安全管理工作暨业务培训会议欢迎辞383
  • 3安装空调的协议书例文468
  • 4技术员工辞职报告198
  • 5高考状元的学习方法和学习心态297
  • 6写教师节的日记485
  • 7液晶显示器点不亮怎么回事294
  • 8地区科技创新论文467
  • 924伏浮球液位开关接线图:浮球液位开关接线464
  • 10excel怎么把隐藏的工作表显示出来?点击取消隐藏工作表却没有反应311
  • 首页/电脑版/地图
    © 2024 OONiu.Com All Rights Reserved.