头文件mysem.h
#ifndef __MYSEM_H__
#define __MYSEM_H__
//信号灯集的申请,初始化信号灯,并返回信号灯集的id
int create_sem(int semcount);
//申请信号灯资源操作 p操作
int P(int semid, int semno);
//释放信号灯资源操作 v操作
int V(int semid, int semno);
//信号灯集的删除
int del_sem(int semid);
#endif
mysem.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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) */
};
//初始化函数
int init_sem(int semid, int semno){
int val = -1;
printf("请输入编号为%d的灯的初始值:", semno);
scanf("%d", &val);
while(getchar() != '\n');
//对当前灯进行赋初始化值
//定义一个共用体变量
union semun us;
us.val = val;
if(semctl(semid, semno, SETVAL, us) == -1){
perror("set val error");
return -1;
}
return 0;
}
//信号灯集的申请,初始化信号灯,并返回信号灯集的id
int create_sem(int semcount){
//1、创建key值
key_t key = ftok("/", 'y');
if(key == -1){
perror("ftok error");
return -1;
}
//2、通过key值创建一个信号灯集
int semid = semget(key, semcount, IPC_CREAT | IPC_EXCL | 0664);
if(semid == -1){
if(errno == EEXIST){
//说明信号灯集已经存在,无需创建直接打开即可
semid = semget(key, semcount, IPC_CREAT);
return semid; //之后的进程打开该信号灯集只需要返回该信号灯集ID即可
}
perror("semget error");
return -1;
}
//3、给信号灯集中的信号灯进行初始化操作
for(int i=0; i<semcount; i++){
init_sem(semid, i); //初始化编号为i的灯value值
}
//4、返回值为信号灯集的id
return semid;
}
//申请信号灯资源操作 p操作
int P(int semid, int semno){
//定义一个操作的结构体变量
struct sembuf buf;
buf.sem_num = semno; //要操作的灯的编号
buf.sem_op = -1; //表示申请资源操作
buf.sem_flg = 0; //如果没有资源,则阻塞等待
//调用semop函数完成p操作
if(semop(semid, &buf, 1) == -1){
perror("P error");
return -1;
}
return 0;
}
//释放信号灯资源操作 v操作
int V(int semid, int semno){
//定义一个操作的结构体变量
struct sembuf buf;
buf.sem_num = semno; //要操作的灯的编号
buf.sem_op = 1; //表示释放资源操作
buf.sem_flg = 0; //如果没有资源,则阻塞等待
//调用semop函数完成V操作
if(semop(semid, &buf, 1) == -1){
perror("V error");
return -1;
}
return 0;
}
//信号灯集的删除
int del_sem(int semid){
//调用semc函数完成信号灯集的删除
if(semctl(semid, 0, IPC_RMID, 0) == -1){
perror("semctl error");
return -1;
}
return 0;
}
main.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mysem.h"
#define PAGE_SIZE 4096
/*
* function: 使用共享内存和信号量, 循环打印ABC
* @param [ in]
* p:申请资源的信号灯的编号,v:释放资源的信号灯的编号,c:打印的字符,semid:共享内存段id
* @param [out]
* @return
*/
int task(int p, int v, char c, int semid){
//创建一个key值
key_t key = ftok("/", 't');
if(key == -1){
perror("ftok error");
return -1;
}
//通过key值创建共享内存段
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT | 0664);
if(shmid == -1){
perror("shmget error");
return -1;
}
//将共享内存段映射到用户空间
char *addr = (char *)shmat(shmid, NULL, 0);
if(addr == (void *)-1){
perror("shmat error");
return -1;
}
//使用共享内存段,每个字符只打印5次
int i = 5;
while(i > 0){
//P操作
P(semid, p);
//写入字符并打印
*addr = c;
printf("%c", *addr);
fflush(stdout);
sleep(1);
//V操作
V(semid, v);
i--;
}
//取消映射
if(shmdt(addr) == -1){
perror("shmdt error");
return -1;
}
return 0;
}
int main(int argc, const char *argv[])
{
//创建信号灯集,获取信号灯集ID
int semid = create_sem(3);
//开启第一个子进程
int pid1 = fork();
if(pid1 > 0){
//开启第二个子进程
int pid2 = fork();
if(pid2 > 0){
//主进程
task(2, 0, 'A', semid);
}else if(pid2 == 0){
//第二个子进程
task(0, 1, 'B', semid);
exit(EXIT_SUCCESS);
}else{
perror("fork error");
return -1;
}
}else if(pid1 == 0){
//第一个子进程
task(1, 2, 'C', semid);
exit(EXIT_SUCCESS);
}else{
perror("fork error");
return -1;
}
//回收子进程资源
wait(NULL);
wait(NULL);
//在最后打印换行
putchar(10);
//删除信号灯集
del_sem(semid);
return 0;
}
1、左值和右值
答:左值(lvalue)和右值(rvalue)是以赋值运算符作为区分,左值一般是指有地址空间的量(如变量、malloc申请的堆内存空间),而右值一般是指没有地址空间的量(常量、表达式结果、将亡值)。左值既可以放在赋值运算符的左边,也可以放在赋值运算符的右边。
补充:
(1)程序在编译时,编译器会为每个变量分配一个地址(左值),这个地址在编译是即可知,左值标志存储结果的一个地方,也可以理解为左值就是一块空间。与左值相反,变量中存储的那个值(右值),只有在运行时才可知,且只有要用到变量中存储的值时,编译器才会发出指令从指定的地址读入变量的值,并将它存于寄存器中,即右值就是一个数字或一个字面值或一个常量,它并不标识任何位置;
(2)const关键字修饰变量,那么该变量也可以是左值,它是不可修改左值,对于数组名也是如此。C语言标准写明了左值分为可修改左值和不可修改左值。
2、两个指针指向同一个地址,第一个指针已经释放该空间,第二个指针如何确认该空间是否已经释放