信号量相关知识
结构体
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) */
};
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
};
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
struct seminfo {
int semmap; /* Number of entries in semaphore
map; unused within kernel */
int semmni; /* Maximum number of semaphore sets */
int semmns; /* Maximum number of semaphores in all
semaphore sets */
int semmnu; /* System-wide maximum number of undo
structures; unused within kernel */
int semmsl; /* Maximum number of semaphores in a
set */
int semopm; /* Maximum number of operations for
semop(2) */
int semume; /* Maximum number of undo entries per
process; unused within kernel */
int semusz; /* Size of struct sem_undo */
int semvmx; /* Maximum semaphore value */
int semaem; /* Max. value that can be recorded for
semaphore adjustment (SEM_UNDO) */
};
semget
semget()函数用于创建一个新的信号量集合,或者访问现有的集合。其原型如下,其中第1个参数key是ftok生成的键值,第2个参数nsems参数可以指定在新的集合中应该创建的信号量的数目,第3个参数semflsg是打开信号量的方式。
semflsg是打开信号量的方式。
IPC_CREAT:如果内核中不存在这样的信号量集合,则把它创建出来。
IPC_EXCL:当与IPC_CREAT一起使用时,如果信号量集合早已存在,则操作将失败。
如果单独使用IPC_CREAT,semget()或者返回新创建的信号量集合的信号量集合标识符;或者返回早已存在的具有同一个关键字值的集合的标识符。如果同时使用IPC_EXCL和IPC_CREAT,那么将有两种可能的结果:如果集合不存在,则创建一个新的集合;如果集合早已存在,则调用失败,并返回-1。IPC_EXCL本身是没有什么用处的,但当与IPC_CREAT组合使用时,它可以用于防止为了访问而打开现有的信号量集合。
semget - get a System V semaphore set identifier
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
semop
信号量的P、V操作是通过向已经建立好的信号量(使用semget()函数),发送命令来完成的。向信号量发送命令的函数是semop(),这个函数的原型如下:
NAME
semop, semtimedop - System V semaphore operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
const struct timespec *timeout);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
semtimedop(): _GNU_SOURCE
semop()函数第2个参数(sops)是一个指针,指向将要在信号量集合上执行操作的一个数组,而第3个参数(nsops)则是该数组中操作的个数。sops参数指向的是类型为sembuf结构的一个数组。sembuf结构是在linux/sem.h中定义的,如下所示。
struct sembuf{
ushort sem_num;
short sem_op;
short sem_flag;
};
sem_num:用户要处理的信号量的编号。
sem_op:将要执行的操作(正、负,或者零)。
sem_flg:信号量操作的标志。如果sem_op为负,则从信号量中减掉一个值。如果sem_op为正,则从信号量中加上值。如果sem_op为0,则将进程设置为睡眠状态,直到信号量的值为0为止。
例如“struct sembuf sem={0,+1,NOWAIT};”表示对信号量0,进行加1的操作。用函数semop()可以构建基本的P、V操作,代码如下所示。Sem_P构建{0,+1,NOWAIT}的sembuf结构来进行增加1个信号量值的操作;Sem_V构建{0,-1,NOWAIT}的sembuf结构来进行减少1个信号量的操作,所对应的信号量由函数传入(semid)。
semctl
NAME
semctl - System V semaphore control operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
测试一:测试信号量的创建、加、减、取值、设置值操作
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
typedef int sem_t;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int sem_p(sem_t semid){
struct sembuf sops = {0,+1,IPC_NOWAIT};
return semop(semid,&sops,1);
}
int sem_v(sem_t semid){
//struct sembuf sops = {0,+1,IPC_NOWAIT};
struct sembuf sops = {0,-1,IPC_NOWAIT};
return semop(semid,&sops,1);
}
void set_sem_value(sem_t semid, int value){
union semun sem;
sem.val = value;
semctl(semid,0,SETVAL,sem);
}
void get_sem_value(sem_t semid, int *value){
union semun sem;
*value = semctl(semid,0,GETVAL,sem);
}
void delete_sem(sem_t semid){
union semun sem;
sem.val;
semctl(semid,0,IPC_RMID,sem);
}
sem_t create_sem(key_t key,int value){
union semun sem;
sem_t semid;
sem.val = value;
semid = semget(key,1,IPC_CREAT|0666);
if(semid == -1){
perror("semget");
exit(-1);
}
semctl(semid,0,SETVAL,sem);
return semid;
}
int main(int argc, char **argv){
key_t key;
int semid;
int value = 0;
key = ftok("/home/lkmao",'a');
if(key == -1){
perror("ftok");
exit(-1);
}
DEBUG_INFO("key = %d",key);
DEBUG_INFO("system V sem");
semid = create_sem(key,0);
get_sem_value(semid,&value);
DEBUG_INFO("value = %d",value);
sem_p(semid);
get_sem_value(semid,&value);
DEBUG_INFO("value = %d",value);
sem_v(semid);
get_sem_value(semid,&value);
DEBUG_INFO("value = %d",value);
set_sem_value(semid,5);
get_sem_value(semid,&value);
DEBUG_INFO("value = %d",value);
delete_sem(semid);
return 0;
}
测试结果:
main:65 -- key = 1627463214
main:67 -- system V sem
main:70 -- value = 0
main:74 -- value = 1
main:78 -- value = 0
main:82 -- value = 5
这肯定不是我们期待的用法,我们期待的是,让它实现进程间的通信,点解?
测试二:信号量实现进程间的通信
共享内存相关知识
shmget函数
NAME
shmget - allocates a System V shared memory segment
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
shmat函数
NAME
shmat, shmdt - System V shared memory operations
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
shmdt用于删除共享内存
shmctl 函数
NAME
shmctl - System V shared memory control
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
测试二:
子进程和父进程连接到同一块共享内存。子进程修改共享内存。父进程采用阻塞模式等待信号量。
子进程休眠5秒,证明父进程确实是在阻塞,5秒后,子进程对信号量执行P操作,父进程被欢迎,读取并打印共享内存的内容。
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <string.h>
#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
typedef int sem_t;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int sem_p(sem_t semid){
struct sembuf sops = {0,+1,IPC_NOWAIT};
return semop(semid,&sops,1);
}
int sem_v(sem_t semid){
//struct sembuf sops = {0,+1,IPC_NOWAIT};//
struct sembuf sops = {0,-1,0};//阻塞模式,无信号则阻塞
return semop(semid,&sops,1);
}
void set_sem_value(sem_t semid, int value){
union semun sem;
sem.val = value;
semctl(semid,0,SETVAL,sem);
}
void get_sem_value(sem_t semid, int *value){
union semun sem;
*value = semctl(semid,0,GETVAL,sem);
}
void delete_sem(sem_t semid){
union semun sem;
sem.val;
semctl(semid,0,IPC_RMID,sem);
}
sem_t create_sem(key_t key,int value){
union semun sem;
sem_t semid;
sem.val = value;
semid = semget(key,1,IPC_CREAT|0666);
if(semid == -1){
perror("semget");
exit(-1);
}
semctl(semid,0,SETVAL,sem);
return semid;
}
static char msg[] = "hello moka moka\n";
int main(int argc, char **argv){
key_t key;
int semid,shmid;
int value = 0;
pid_t pid;
key = ftok("/home/lkmao",'a');
if(key == -1){
perror("ftok");
exit(-1);
}
DEBUG_INFO("key = %d",key);
//创建共享内存
shmid = shmget(key,1024,IPC_CREAT|0666);
if(shmid == -1){
perror("shmget");
exit(-1);
}
//创建信号量
semid = create_sem(key,0);
pid = fork();
if(pid == -1){
perror("fork");
exit(-1);
}
if(pid == 0){
DEBUG_INFO("child %u",getpid());
char *shm = (char*)shmat(shmid,0,0);
memcpy(shm,msg,sizeof(msg));
sleep(5);
sem_p(semid);
shmdt(shm);
}
if(pid > 0){
DEBUG_INFO("parent:%u",getppid());
char *shm = (char*)shmat(shmid,0,0);
memcpy(shm,msg,sizeof(msg));
sem_v(semid);
DEBUG_INFO("shm = %s",shm);
shmdt(shm);
}
//delete_sem(semid);
return 0;
}
执行结果:
main:70 -- key = 1627463214
main:93 -- parent:68538
main:85 -- child 68637
main:97 -- shm = hello moka moka
小结
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)