信号量
OVERVIEW
- 信号量
- 1.mmap内存映射
- 2.semget信号量获取
- 3.semctl信号量控制
- 4.semop信号量操作
- 5.使用案例
1.mmap内存映射
mmap映射的是磁盘中的文件,而共享内存是将内存映射到用户的进程空间中
原型:void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
- addr:映射的地址空间(如果为空则内核将会自动选择映射的位置)
- length:映射的长度
- prot:对映射内容的保护方式
- flag:标记位
- fd:文件描述符
- offset:偏移量
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/**
* 打开一个文件 将文件映射到进程的地址空间中
* 然后通过设定一些偏移量 来拿到文件的一些内容
*/
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int main(int argc, char *argv[]) {
struct stat sb;
off_t offset, pa_offset;
size_t length;
//1.命令解析
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
//2.打开文件
int fd = open(argv[1], O_RDONLY);
if (fd == -1) handle_error("open");
//3.确定偏移量及其错误处理
if (fstat(fd, &sb) == -1) handle_error("fstat");//获取文件长度
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);//做页对齐的机制 _SC_PAGE_SIZE页面的大小
//偏移量大于最大文件长度
if (offset >= sb.st_size) {
fprintf(stderr, "offset is past end of file\n");
exit(EXIT_FAILURE);
}
//偏移量+length大于最大文件长度
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size) length = sb.st_size - offset;
} else {
length = sb.st_size - offset;
}
//4.调用mmap进行内存映射
char *addr = mmap(NULL, length + offset - pa_offset, PROT_READ, MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED) handle_error("mmap");
//5.对需要输出的字符进行打印
ssize_t s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1) handle_error("write");
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
//6.解开映射关系
munmap(addr, length + offset - pa_offset);
close(fd);
exit(EXIT_SUCCESS);
}
2.semget信号量获取
3.semctl信号量控制
4.semop信号量操作
5.使用案例
- 创建一个信号量,初始化设置其val为2(每次只有2个进程能够拿到资源)
- 对临界资源进行PV操作
- 模拟多个非亲缘关系的进程,对该临界资源访问时的情况
- 当每个进程访问该临界资源时输出自己是第几次访问即可
#include "head.h"
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
//1.创建信号量
int create_sem(const char pathname[], int proj_id, int nsmes) {
key_t key = ftok(pathname, proj_id);
int sem_id;
if ((sem_id = semget(key, nsmes, IPC_CREAT | 0600)) < 0) return -1;//获取信号量集合的识别号
return sem_id;
}
//2.初始化信号量
int init_sem(int sem_id, int sem_num, int val) {
union semun arg;
arg.val = val;//可选操作
return semctl(sem_id, sem_num, SETVAL, arg);//操作设置为SETVAL
}
//3.定义PV操作
//struct sembuf, containing the following members:
// unsigned short sem_num; //semaphore number
// short sem_op; //semaphore operation
// short sem_flg; //operation flags
int P(int sem_id, int sem_num) {//用于判定PV操作是否成功
struct sembuf sembuff;
sembuff.sem_num = sem_num;//要操作的是第几个信号量
sembuff.sem_op = -1;//P操作为-1
sembuff.sem_flg = SEM_UNDO;//SEM_UNDO当进程结束时 该操作也会自动的将资源释放
if (semop(sem_id, &sembuff, 1) < 0) return -1;//调用semop进行P-1操作
return 0;
}
int V(int sem_id, int sem_num) {//用于判定PV操作是否成功
struct sembuf sembuff;
sembuff.sem_num = sem_num;//要操作的是第几个信号量
sembuff.sem_op = 1;//P操作为-1
sembuff.sem_flg = SEM_UNDO;
if (semop(sem_id, &sembuff, 1) < 0) return -1;//调用semop进行V+1操作
return 0;
}
int main(int argc, char* argv[]) {
//1.创建一个信号量 只有首个进程进行信号量初始化操作
int sem_id;
if ((sem_id = create_sem("1.sem.c", 123, 1)) < 0) handle_error("create_sem");
if (argc > 1) {//并不是所有的进程都在需要进行初始化操作
if ((init_sem(sem_id, 0, 2) < 0)) handle_error("init_sem");//val设置为2 每次有2个进程能够拿到资源
}
//2.pv操作 让多个非亲缘关系的进程拿到资源
int times;
while (1) {
if (P(sem_id, 0) < 0) handle_error("P");
sleep(3);
times++;
if (V(sem_id, 0) < 0) handle_error("V");
//3.输出我时第几次拿到资源
printf("this is %dth time i got the resource!\n", times);
}
return 0;
}