LV020-posix无名信号量
一、POSIX 无名信号量
有名信号量和无名信号量的差异在于创建和销毁的形式上,其他的是一样的。无名信号量只能存在于内存中,所以就要求使用信号量的进程必须能访问信号量所在的这一块内存,所以无名信号量只能应用在同一进程内的线程之间(共享进程的内存)。另外,该信号量的使用需要借助于共享内存,打开或者创建之前需要进行以下步骤:
- (1)申请
key; - (2)创建共享内存区域;
- (3)映射内存区域到进程的虚拟地址空间。
1. 初始化无名信号量
1.1 sem_init()
1.1.1 函数说明
在linux下可以使用man 3 sem_init命令查看该函数的帮助手册。
/* Link with -pthread. */
/* 需包含的头文件 */
#include <semaphore.h>
/* 函数声明 */
int sem_init(sem_t *sem, int pshared, unsigned int value);【函数说明】该函数用于初始化一个无名信号量。
【函数参数】
sem:sem_t类型,需要初始化的无名信号量名称。pshared:int类型,表示这个信号量是在进程的线程之间共享,还是在进程之间共享。一般指定为0,表示这个无名信号量只能由初始化这个信号量的进程使用,不能在进程间使用,并且Linux不支持使用无名信号量实现进程间的同步。value:unsigned int类型,表示无名信号量的初始值。该初始不能超过SEM_VALUE_MAX,这个常值必须低于为32767。
【返回值】int类型,成功返回0,失败返回-1,并设置errno表示错误。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
/* 至少应该有的语句 */
sem_t sem_r; /* 定义成全局变量 */
sem_init(&sem_r, 0, 0);【注意事项】none
1.1.2 使用实例
暂无。
2. 销毁无名信号量
2.1 sem_destroy()
2.1.1 函数说明
在linux下可以使用man 3 sem_destroy命令查看该函数的帮助手册。
/* Link with -pthread. */
/* 需包含的头文件 */
#include <semaphore.h>
/* 函数声明 */
int sem_destroy(sem_t *sem);【函数说明】该函数用于销毁一个无名信号量。
【函数参数】
sem:sem_t *类型,需要关闭的信号量的地址。
【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <semaphore.h>
/* 至少应该有的语句 */
sem_t *sem_r;/* 定义成全局变量 */
sem_destroy(sem_r);【注意事项】none
2.1.2 使用实例
暂无。
3. 无名信号量的P操作
与有名信号量的P操作一样。
3.1 sem_wait()
3.1.1 函数说明
在linux下可以使用man 3 sem_wait命令查看该函数的帮助手册。
/* Link with -pthread. */
/* 需包含的头文件 */
#include <semaphore.h>
/* 函数声明 */
int sem_wait(sem_t *sem);【函数说明】该函数用于执行信号量的P操作,也就是获取资源。如果信号量的值大于0,则信号量继续递减,函数立即返回;如果信号量当前的值是0,那么调用就会阻塞,直到可以执行递减操作(即信号量的值升到0以上),或者信号处理程序中断调用。
【函数参数】
sem:sem_t *类型,表示需要等待的信号量。
【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <semaphore.h>
/* 至少应该有的语句 */
sem_t *sem_r;
sem_wait(sem_r);【注意事项】none
3.1.2 使用实例
暂无。
3.2 sem_trywait()
3.2.1 函数说明
在linux下可以使用man 3 sem_trywait命令查看该函数的帮助手册。
/* Link with -pthread. */
/* 需包含的头文件 */
#include <semaphore.h>
/* 函数声明 */
int sem_trywait(sem_t *sem);【函数说明】该函数用于执行信号量的P操作。sem_trywait()与sem_wait()相同,不同之处是如果不能立即执行递减操作而需要等待的话,调用此函数将返回错误(errno设置为EAGAIN)而不是阻塞。
【函数参数】
sem:sem_t *类型,表示需要等待的信号量。
【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <semaphore.h>
/* 至少应该有的语句 */
sem_t *sem_r;
sem_trywait(sem_r);【注意事项】none
3.2.2 使用实例
暂无。
3.3 sem_trywait()
3.3.1 函数说明
在linux下可以使用man 3 sem_trywait命令查看该函数的帮助手册。
/* Link with -pthread. */
/* 需包含的头文件 */
#include <semaphore.h>
/* 函数声明 */
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);【函数说明】该函数用于执行信号量的P操作。sem_timedwait()与sem_wait()相同,不同的是abs_timeout指定了如果不能立即执行递减操作而需要等待的时间,超时将会返回。
【函数参数】
sem:sem_t *类型,表示需要等待的信号量。abs_timeout:struct timespec类型的结构体指针变量,它指向一个时间结构体,该时间结构体指定了自Epoch (1970-01-01 00:00:00 +0000 (UTC))以来的以秒和纳秒为单位的绝对超时。struct timespec 结构体成员如下:
struct timespec
{
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds [0 .. 999999999] */
};【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <semaphore.h>
/* 至少应该有的语句 */
sem_t *sem_r;
struct timespec tout;
sem_timedwait(sem_r, &tout);【注意事项】如果可以立即执行操作,那么sem_timedwait()永远不会出现超时错误,而不管abs_timeout的值是多少。
3.3.2 使用实例
暂无。
4. 无名信号量的V操作
与有名信号量的V操作一样。
4.1 sem_post()
4.1.1 函数说明
在linux下可以使用man 3 sem_post命令查看该函数的帮助手册。
/* Link with -pthread. */
/* 需包含的头文件 */
#include <semaphore.h>
/* 函数声明 */
int sem_post(sem_t *sem);【函数说明】该函数用于执行信号量的V操作,也就是获取资源。
【函数参数】
sem:sem_t *类型,表示需释放的信号量。
【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <semaphore.h>
/* 至少应该有的语句 */
sem_t *sem_r;
sem_post(sem_r);【注意事项】none
4.1.2 使用实例
暂无。
二、使用实例
1. 实例源码
/* 头文件 */
#include <stdio.h> /* perror fgets shmat*/
#include <sys/shm.h> /* shmget shmat*/
#include <sys/ipc.h> /* shmget ftok */
#include <string.h> /* strcpy */
#include <stdlib.h> /* system */
#include <signal.h> /* sigaction sigemptyset */
#include <fcntl.h> /* sem_init For O_* constants */
#include <sys/stat.h> /* sem_init For mode constants */
#include <semaphore.h>/* sem_init sem_post */
#include <pthread.h> /* pthread_create */
sem_t sem_r, sem_w; /* 定义两个信号量 */
char *shmAddr; /* 共享内存首地址 */
void *readSem(void *arg); /* 读取数据线程 */
void deleteSemfile(int sig);/* 删除无名信号量 */
/* 主函数 */
int main(int argc, char *argv[])
{
key_t key;
int shmid;
struct sigaction act;/* 处理信号行为的结构体变量 */
pthread_t tid;
/* 1. 生成key */
key = ftok("./key.txt", 100);
if(key < 0)
{
perror("ftok");
return -1;
}
printf("key=%x\n", key);
/* 2. 创建或者打开共享内存 */
shmid = shmget(key, 512, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
return 0;
}
printf("shmid=%d\n", shmid);
/* 3. 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问*/
shmAddr = shmat(shmid, NULL, 0);
if(shmAddr < 0)
{
perror("shmat");
return 0;
}
printf("shmAddr=%p\n", shmAddr);
/* 4. 信号捕获 */
act.sa_handler = deleteSemfile; /* 指定信号处理函数 */
act.sa_flags = 0; /* sa_flags指定了一组标志,这些标志用于控制信号的处理过程 */
sigemptyset(&act.sa_mask); /* 将信号集初始化为空 */
sigaction(SIGINT, &act, NULL); /* 捕捉信号 */
/* 5. 信号量初始化) */
sem_init(&sem_r, 0, 0);
sem_init(&sem_w, 0, 1);
/* 6. 创建线程 */
pthread_create(&tid, NULL, readSem, NULL);
/* 7. 写入数据 */
while(1)
{
sem_wait(&sem_w);/* 获取资源 */
printf(">");
fgets(shmAddr, 500, stdin);
sem_post(&sem_r);/* 释放资源 */
}
return 0;
}
/* 线程函数 */
void *readSem(void *arg)
{
while(1)
{
sem_wait(&sem_r);
printf("%s\n",shmAddr);
sem_post(&sem_w);
}
}
/* 删除信号量 */
void deleteSemfile(int sig)
{
sem_destroy(&sem_r);
sem_destroy(&sem_w);
printf("Destroy sem_r and sem_w!\n");
exit(0);
}2. 测试效果
在终端执行以下命令编译程序:
gcc test.c -Wall -lpthread # 生成可执行文件 a.out
./a.out # 执行可执行程序然后,终端会有以下信息显示:
key=643d0b38
shmid=18
shmAddr=0x7f421bd53000
>qwert
qwert
>^CDestroy sem_r and sem_w!