消息队列
1、消息队列是什么?
消息队列是在内核空间内部的一个链表。通过Linux内核在各个进程间传递消息。
要获取对应的消息队列,每一个消息队列都有一个唯一的表示符来区分。
2、怎么样使用消息队列进行进程间通讯?
1)先产生一个标示符
SYNOPSIS
#include
#include
key_t ftok(const char *pathname, int proj_id);//两个随机值产生一个唯一的随机的key值
pathname (which must refer to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件 /home/gec
proj_id (which must be nonzero) to generate a key_t 必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。 随机非0数字
返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。
2)通过这个key去创建消息队列
SYNOPSIS
#include
#include
#include
int msgget(key_t key, int msgflg);
第一个参数,第一个参数就是我们第一步通过ftok产生的key值,用来标识唯一的消息队列。
第二个参数,分为有两种
第一种情况:创建 IPC_CREAT | 权限位 例如: IPC_CREAT | 0664 (一般用这个,0777也可以,注意可以有空格)
第二种情况:打开 0
返回值:
成功返回的是消息队列的ID,如果失败了,返回-1,并且错误码可以通过perror打印出来。
3)通过收发的函数,实现收发,实现进程间通讯。
SYNOPSIS
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。
msgp:这个是指针,指向我们所需要发送的消息,消息我们可以自定义。
struct msgbuf {
long mtype; /* message type, must be > 0 消息的类型,这个参数是自己定义 */
char mtext[1]; /* message data 这个是我们需要发送的消息的具体数据 */
};
msgsz: 消息内容的长度,是对应mtext的实际大小。//一般用数组大小
msgflg:
1)IPC_NOWAIT 不阻塞的模式。
2)0 阻塞模式发送(一般都是用这个)
返回值:成功返回0,失败的话返回-1,并且可以把对应的错误码打印出来。
这个函数的作用,就是在指定的消息队列里面,把消息摘下来,拷贝到msgp所指定buffer里面。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。
msgp:这个是指针,指向我们所需要接收的消息的buffer,
msgsz:指的是消息的最大的长度,消息内容的长度,是对应mtext的实际大小。
msgtyp:指的是消息的类型,0代表读取的是队列内第一个消息。如果是大于零的时候,队列里面第一个msgtyp类型的消息就会被读回来。(一般用888)
msgflg:
1)IPC_NOWAIT 不阻塞的模式。
2)0 阻塞模式发送(一般都是用这个)
返回值:成功的话,返回的是,对应mtext的实际大小(实际消息的长度), 失败的话返回-1,并且可以把对应的错误码打印出来。
4)用于控制这个消息队列,比如说删除队列。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid:这个参数就是消息队列的ID
cmd: 一些命令,常用的有删除队列的命令:IPC_RMID
msqid_ds *buf ,关于队列的一些信息的描述。
Msg-sever.c
#include
#include
#include
#include
#include
struct msgbuf
{
long mtype;
char mtext[256];
};
int main()
{
int msgid;
key_t key;
struct msgbuf msg,msgr;
key=ftok("/home/gec/caobo",111);
if(key<0)
{
perror("fork fail");
exit(-1);
}
printf("key=%d\n",key);
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid<0)
{
perror("msgget fail");
exit(-1);
}
msg.mtype=888;
// scanf("%s",msg.mtext);
while(1)
{
msgrcv(msgid,&msgr,256,888,0);//通过没有赋值消息类型的结构体来读,赋值了的来写,下面程序也是;
printf("曹博:%s\n",msgr.mtext);
// printf("sever recv msg type:%ld\n",msgr.mtype);
printf("陈素:\n");
scanf("%s",msg.mtext);
msgsnd(msgid,&msg,256,0);//注意msgr和msg为同种结构体类型的两个结构体,用来辅助读写操作
sleep(3);
}
return 0;
}
Msg-client.c
#include
#include
#include
#include
#include
struct msgbuf
{
long mtype;
char mtext[256];
};
int main()
{
int msgid;
key_t key;
struct msgbuf msg,msgr;
key=ftok("/home/gec/caobo",111);
if(key<0)
{
perror("fork fail");
exit(-1);
}
printf("key=%d\n",key);
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid<0)
{
perror("msgget fail");
exit(-1);
}
msg.mtype=888;
//scanf("%s",msg.mtext);
while(1)
{
printf("曹博:\n");
scanf("%s",msg.mtext);
msgsnd(msgid,&msg,256,0);
msgrcv(msgid,&msgr,256,888,0);
printf("陈素:%s\n",msgr.mtext);
// printf("client recv msg type:%ld\n",msgr.mtype);
sleep(3);
}
return 0;
}
3、信号量
1)为什么需要使用信号量?
因为我们的系统资源是有限的,但是系统是多任务的,所以存在多进程、多线程,可能同时需要对某一个资源进行访问,用来保证资源的有序访问。
否则会产生不可预计的结果。
2)信号量是什么东西?
信号量是一个计数值,这个值表示当前可用的资源数,这个值也是可用是一个负数,负数的话,表示的是等待的进程数量。还有一个指针,这个指针指向等待该信号量的进程
P操作,减一操作。
V操作,加一操作。
操作属于原子操作,是不可被打断一种操作,一定要等待这个操作的完成。要包含的代码要尽量短,而且不要有循环。
3)如何使用信号量?
(1)、产生一个key值。
SYNOPSIS
#include
#include
key_t ftok(const char *pathname, int proj_id);
pathname (which must refer to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件
proj_id (which must be nonzero) to generate a key_t 必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。
返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。
(2)、根据这个key值,去创建一个信号量。不同进程就可以根据这个信号量的ID去找到这个信号量,然后操作它。
SYNOPSIS
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
key: 就是我们第一步通过ftok产生的key值。
nsems:信号量的个数。//一般为1
semflg:
创建 IPC_CREAT | 权限位 例如: IPC_CREAT | 0664
(3)、要对这个信号量,进行初始化。//初始化信号量值
SYNOPSIS
#include
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
semid:是创建的信号量的ID
semnum:表示是对第几个信号量来进行设置。//一般取值为0
cmd:SETVAL 设置信号量的初始值。
第四个参数,取决于第三个参数,如果有第四个参数的话,它是一个联合体,这个联合体是这样定义的//第四个参数为这个联合体,主要是为了设置联合体的val值
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) */
};
调用P操作(-1),用完之后,调用V操作。(+1)//当信号值为负数的时候就会卡在那里等待,当使用v操作会+1变成0,就不会阻塞了;所以当信号量初始值为0时,进行p操作(-1)会卡在那里等待,当用v操作时就会被疏通;可以理解为停车场,当信号量为1时,有一个位置你可以进去,当为0时,你正好在里面,但是没有了位置多,当为-1时,表明你要在外面等待,卡在那了
SYNOPSIS
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid:这个信号量的ID
struct sembuf, containing the following members:
unsigned short sem_num; /* semaphore number操作第几个信号量 */
short sem_op; /* semaphore operation P还是V操作,如果是互斥式的信号量,P操作,这个就等于-1,如果是v操作就等于1 */
short sem_flg; /* operation flags SEM_UNDO */
成功返回0,失败返回-1;
Sem.c
#include
#include
#include
#include
#include
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_p(int semid)
{
struct sembuf sbuf;
sbuf.sem_num=0;//表示操作第几个信号量,与下面要统一;
sbuf.sem_op=-1;//表示进行p操作;
sbuf.sem_flg=SEM_UNDO;//默认使用这个标志位
if((semop(semid,&sbuf,1))==-1)//去设置这个信号量
{
perror("p op fail");
return -1;
}
return 0;
}
int sem_v(int semid)
{
struct sembuf sbuf;
sbuf.sem_num=0;
sbuf.sem_op=1;
sbuf.sem_flg=SEM_UNDO;
if((semop(semid,&sbuf,1))==-1)
{
perror("p ov fail");
return -1;
}
return 0;
}
int main()
{
key_t key;
int semid,ret;
pid_t pid;
union semun tmp;
key=ftok("/home/gec",2);//产生一个key值,用来创建一个信号量
printf("key=%d\n",key);
if(key<0)
{
perror("ftok fail");
exit(-1);
}
semid=semget(key,1,IPC_CREAT | 0777); //创建这个信号量 1代表信号量的个数,返回信号量的ID
if(semid<0)
{
perror("semget fail");
exit(-1);
}
tmp.val=0;//初始化信号量的初始值为0,所以下面的操作就要先用v操作来释放这个信号量
ret=semctl(semid,0,SETVAL,tmp);//初始化信号量
if(ret<0)
{
perror("semctl fail");
exit(-1);
}
pid=fork();
if(pid<0)
{
perror("fork fail");
exit(-1);
}
if(pid==0)
{
// sem_p(semid);
//因为信号量的初始值为0,所以不用p操作,如果这里也用p操作大家都会锁死
sleep(3);
printf("child pid :%d\n",getpid());
sem_v(semid);//完了之后用V操作,回覆信号量初始值为1
}
else
{
sem_p(semid);//0
printf("parent pid :%d\n",getppid());
sem_v(semid);//1
printf("*****\n");
sem_p(semid);//0
sem_p(semid);//-1会在这卡住
printf("*********\n");
semctl(semid,0,IPC_RMID,tmp);
}
return 0;
}
老师的代码:sem.c
#include
#include
#include
// 联合体,用于semctl初始化
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf;
unsigned short *array;
};
// 初始化信号量
int init_sem(int sem_id, int value)
{
union semun tmp;
tmp.val = value;
控制信号量的相关信息 int semctl(int semid, int sem_num, int cmd, ...);
//SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
//IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。
if(semctl(sem_id, 0, SETVAL, tmp) == -1)
{
perror("Init Semaphore Error");
return -1;
}
return 0;
}
// P操作:
// 若信号量值为1,获取资源并将信号量值-1
// 若信号量值为0,进程挂起等待
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序号 标识信号量集中的第几个信号量 // 信号量组中对应的序号,0~sem_nums-1 */
sbuf.sem_op = -1; /*P操作 对信号量的操作 信号量值在一次操作中的改变量 */
sbuf.sem_flg = 0;//SEM_UNDO;//sem_flg 指定IPC_NOWAIT 则semop函数出错返回EAGAIN 如果为SEM_UNDO,那么将维护进程对信号量的调整值,以便进程结束时恢复信号量的状态
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
// int semop(int semid, struct sembuf semoparray[], size_t numops)
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作:
// 释放资源并将信号量值+1
// 如果有进程正在挂起等待,则唤醒它们
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序号*/
sbuf.sem_op = 1; /*V操作*/
sbuf.sem_flg = 0;//SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("V operation Error");
return -1;
}
return 0;
}
// 删除信号量集
int del_sem(int sem_id)
{
union semun tmp;
if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
int main()
{
int sem_id; // 信号量集ID
key_t key;
pid_t pid;
// 获取key值
if((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 创建信号量集,其中只有一个信号量
//当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1; 如果是引用一个现有的集合,则将num_sems指定为 0 int semget(key_t key, int num_sems, int sem_flags);
if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
{
perror("semget error");
exit(1);
}
// 初始化:初值设为0资源被占用
init_sem(sem_id, 0);
if((pid = fork()) == -1)
perror("Fork Error");
else if(pid == 0) /*子进程*/
{
sleep(2);
printf("Process child: pid=%d\n", getpid());
sem_v(sem_id); /*释放资源*/
}
else /*父进程*/
{
sem_p(sem_id); /*等待资源*/
printf("Process father: pid=%d\n", getpid());
sem_v(sem_id); /*释放资源*/
del_sem(sem_id); /*删除信号量集*/
}
return 0;
}
进程的状态
消息队列
1、消息队列是什么?
消息队列是在内核空间内部的一个链表。通过Linux内核在各个进程间传递消息。
要获取对应的消息队列,每一个消息队列都有一个唯一的表示符来区分。
2、怎么样使用消息队列进行进程间通讯?
1)先产生一个标示符
SYNOPSIS
#include
#include
key_t ftok(const char *pathname, int proj_id);//两个随机值产生一个唯一的随机的key值
pathname (which must refer to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件 /home/gec
proj_id (which must be nonzero) to generate a key_t 必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。 随机非0数字
返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。
2)通过这个key去创建消息队列
SYNOPSIS
#include
#include
#include
int msgget(key_t key, int msgflg);
第一个参数,第一个参数就是我们第一步通过ftok产生的key值,用来标识唯一的消息队列。
第二个参数,分为有两种
第一种情况:创建 IPC_CREAT | 权限位 例如: IPC_CREAT | 0664 (一般用这个,0777也可以,注意可以有空格)
第二种情况:打开 0
返回值:
成功返回的是消息队列的ID,如果失败了,返回-1,并且错误码可以通过perror打印出来。
3)通过收发的函数,实现收发,实现进程间通讯。
SYNOPSIS
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。
msgp:这个是指针,指向我们所需要发送的消息,消息我们可以自定义。
struct msgbuf {
long mtype; /* message type, must be > 0 消息的类型,这个参数是自己定义 */
char mtext[1]; /* message data 这个是我们需要发送的消息的具体数据 */
};
msgsz: 消息内容的长度,是对应mtext的实际大小。//一般用数组大小
msgflg:
1)IPC_NOWAIT 不阻塞的模式。
2)0 阻塞模式发送(一般都是用这个)
返回值:成功返回0,失败的话返回-1,并且可以把对应的错误码打印出来。
这个函数的作用,就是在指定的消息队列里面,把消息摘下来,拷贝到msgp所指定buffer里面。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。
msgp:这个是指针,指向我们所需要接收的消息的buffer,
msgsz:指的是消息的最大的长度,消息内容的长度,是对应mtext的实际大小。
msgtyp:指的是消息的类型,0代表读取的是队列内第一个消息。如果是大于零的时候,队列里面第一个msgtyp类型的消息就会被读回来。(一般用888)
msgflg:
1)IPC_NOWAIT 不阻塞的模式。
2)0 阻塞模式发送(一般都是用这个)
返回值:成功的话,返回的是,对应mtext的实际大小(实际消息的长度), 失败的话返回-1,并且可以把对应的错误码打印出来。
4)用于控制这个消息队列,比如说删除队列。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid:这个参数就是消息队列的ID
cmd: 一些命令,常用的有删除队列的命令:IPC_RMID
msqid_ds *buf ,关于队列的一些信息的描述。
Msg-sever.c
#include
#include
#include
#include
#include
struct msgbuf
{
long mtype;
char mtext[256];
};
int main()
{
int msgid;
key_t key;
struct msgbuf msg,msgr;
key=ftok("/home/gec/caobo",111);
if(key<0)
{
perror("fork fail");
exit(-1);
}
printf("key=%d\n",key);
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid<0)
{
perror("msgget fail");
exit(-1);
}
msg.mtype=888;
// scanf("%s",msg.mtext);
while(1)
{
msgrcv(msgid,&msgr,256,888,0);//通过没有赋值消息类型的结构体来读,赋值了的来写,下面程序也是;
printf("曹博:%s\n",msgr.mtext);
// printf("sever recv msg type:%ld\n",msgr.mtype);
printf("陈素:\n");
scanf("%s",msg.mtext);
msgsnd(msgid,&msg,256,0);//注意msgr和msg为同种结构体类型的两个结构体,用来辅助读写操作
sleep(3);
}
return 0;
}
Msg-client.c
#include
#include
#include
#include
#include
struct msgbuf
{
long mtype;
char mtext[256];
};
int main()
{
int msgid;
key_t key;
struct msgbuf msg,msgr;
key=ftok("/home/gec/caobo",111);
if(key<0)
{
perror("fork fail");
exit(-1);
}
printf("key=%d\n",key);
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid<0)
{
perror("msgget fail");
exit(-1);
}
msg.mtype=888;
//scanf("%s",msg.mtext);
while(1)
{
printf("曹博:\n");
scanf("%s",msg.mtext);
msgsnd(msgid,&msg,256,0);
msgrcv(msgid,&msgr,256,888,0);
printf("陈素:%s\n",msgr.mtext);
// printf("client recv msg type:%ld\n",msgr.mtype);
sleep(3);
}
return 0;
}
3、信号量
1)为什么需要使用信号量?
因为我们的系统资源是有限的,但是系统是多任务的,所以存在多进程、多线程,可能同时需要对某一个资源进行访问,用来保证资源的有序访问。
否则会产生不可预计的结果。
2)信号量是什么东西?
信号量是一个计数值,这个值表示当前可用的资源数,这个值也是可用是一个负数,负数的话,表示的是等待的进程数量。还有一个指针,这个指针指向等待该信号量的进程
P操作,减一操作。
V操作,加一操作。
操作属于原子操作,是不可被打断一种操作,一定要等待这个操作的完成。要包含的代码要尽量短,而且不要有循环。
3)如何使用信号量?
(1)、产生一个key值。
SYNOPSIS
#include
#include
key_t ftok(const char *pathname, int proj_id);
pathname (which must refer to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件
proj_id (which must be nonzero) to generate a key_t 必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。
返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。
(2)、根据这个key值,去创建一个信号量。不同进程就可以根据这个信号量的ID去找到这个信号量,然后操作它。
SYNOPSIS
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
key: 就是我们第一步通过ftok产生的key值。
nsems:信号量的个数。//一般为1
semflg:
创建 IPC_CREAT | 权限位 例如: IPC_CREAT | 0664
(3)、要对这个信号量,进行初始化。//初始化信号量值
SYNOPSIS
#include
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
semid:是创建的信号量的ID
semnum:表示是对第几个信号量来进行设置。//一般取值为0
cmd:SETVAL 设置信号量的初始值。
第四个参数,取决于第三个参数,如果有第四个参数的话,它是一个联合体,这个联合体是这样定义的//第四个参数为这个联合体,主要是为了设置联合体的val值
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) */
};
调用P操作(-1),用完之后,调用V操作。(+1)//当信号值为负数的时候就会卡在那里等待,当使用v操作会+1变成0,就不会阻塞了;所以当信号量初始值为0时,进行p操作(-1)会卡在那里等待,当用v操作时就会被疏通;可以理解为停车场,当信号量为1时,有一个位置你可以进去,当为0时,你正好在里面,但是没有了位置多,当为-1时,表明你要在外面等待,卡在那了
SYNOPSIS
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid:这个信号量的ID
struct sembuf, containing the following members:
unsigned short sem_num; /* semaphore number操作第几个信号量 */
short sem_op; /* semaphore operation P还是V操作,如果是互斥式的信号量,P操作,这个就等于-1,如果是v操作就等于1 */
short sem_flg; /* operation flags SEM_UNDO */
成功返回0,失败返回-1;
Sem.c
#include
#include
#include
#include
#include
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_p(int semid)
{
struct sembuf sbuf;
sbuf.sem_num=0;//表示操作第几个信号量,与下面要统一;
sbuf.sem_op=-1;//表示进行p操作;
sbuf.sem_flg=SEM_UNDO;//默认使用这个标志位
if((semop(semid,&sbuf,1))==-1)//去设置这个信号量
{
perror("p op fail");
return -1;
}
return 0;
}
int sem_v(int semid)
{
struct sembuf sbuf;
sbuf.sem_num=0;
sbuf.sem_op=1;
sbuf.sem_flg=SEM_UNDO;
if((semop(semid,&sbuf,1))==-1)
{
perror("p ov fail");
return -1;
}
return 0;
}
int main()
{
key_t key;
int semid,ret;
pid_t pid;
union semun tmp;
key=ftok("/home/gec",2);//产生一个key值,用来创建一个信号量
printf("key=%d\n",key);
if(key<0)
{
perror("ftok fail");
exit(-1);
}
semid=semget(key,1,IPC_CREAT | 0777); //创建这个信号量 1代表信号量的个数,返回信号量的ID
if(semid<0)
{
perror("semget fail");
exit(-1);
}
tmp.val=0;//初始化信号量的初始值为0,所以下面的操作就要先用v操作来释放这个信号量
ret=semctl(semid,0,SETVAL,tmp);//初始化信号量
if(ret<0)
{
perror("semctl fail");
exit(-1);
}
pid=fork();
if(pid<0)
{
perror("fork fail");
exit(-1);
}
if(pid==0)
{
// sem_p(semid);
//因为信号量的初始值为0,所以不用p操作,如果这里也用p操作大家都会锁死
sleep(3);
printf("child pid :%d\n",getpid());
sem_v(semid);//完了之后用V操作,回覆信号量初始值为1
}
else
{
sem_p(semid);//0
printf("parent pid :%d\n",getppid());
sem_v(semid);//1
printf("*****\n");
sem_p(semid);//0
sem_p(semid);//-1会在这卡住
printf("*********\n");
semctl(semid,0,IPC_RMID,tmp);
}
return 0;
}
老师的代码:sem.c
#include
#include
#include
// 联合体,用于semctl初始化
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf;
unsigned short *array;
};
// 初始化信号量
int init_sem(int sem_id, int value)
{
union semun tmp;
tmp.val = value;
控制信号量的相关信息 int semctl(int semid, int sem_num, int cmd, ...);
//SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
//IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。
if(semctl(sem_id, 0, SETVAL, tmp) == -1)
{
perror("Init Semaphore Error");
return -1;
}
return 0;
}
// P操作:
// 若信号量值为1,获取资源并将信号量值-1
// 若信号量值为0,进程挂起等待
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序号 标识信号量集中的第几个信号量 // 信号量组中对应的序号,0~sem_nums-1 */
sbuf.sem_op = -1; /*P操作 对信号量的操作 信号量值在一次操作中的改变量 */
sbuf.sem_flg = 0;//SEM_UNDO;//sem_flg 指定IPC_NOWAIT 则semop函数出错返回EAGAIN 如果为SEM_UNDO,那么将维护进程对信号量的调整值,以便进程结束时恢复信号量的状态
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
// int semop(int semid, struct sembuf semoparray[], size_t numops)
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作:
// 释放资源并将信号量值+1
// 如果有进程正在挂起等待,则唤醒它们
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序号*/
sbuf.sem_op = 1; /*V操作*/
sbuf.sem_flg = 0;//SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("V operation Error");
return -1;
}
return 0;
}
// 删除信号量集
int del_sem(int sem_id)
{
union semun tmp;
if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
int main()
{
int sem_id; // 信号量集ID
key_t key;
pid_t pid;
// 获取key值
if((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 创建信号量集,其中只有一个信号量
//当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1; 如果是引用一个现有的集合,则将num_sems指定为 0 int semget(key_t key, int num_sems, int sem_flags);
if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
{
perror("semget error");
exit(1);
}
// 初始化:初值设为0资源被占用
init_sem(sem_id, 0);
if((pid = fork()) == -1)
perror("Fork Error");
else if(pid == 0) /*子进程*/
{
sleep(2);
printf("Process child: pid=%d\n", getpid());
sem_v(sem_id); /*释放资源*/
}
else /*父进程*/
{
sem_p(sem_id); /*等待资源*/
printf("Process father: pid=%d\n", getpid());
sem_v(sem_id); /*释放资源*/
del_sem(sem_id); /*删除信号量集*/
}
return 0;
}