一.进程的同步与互斥
进程间的两种关系:同步和互斥。
同步就是把异步环境下的一组并发进程,因直接制约而互相发送消息,二者进行互相合作、互相等待,使得各进程按一定的速度执行的过程。
互斥是指不允许两个以上的共享该资源的并发进程同时进入临界区。其中直接制约是指一组在异步环境下的并发进程,各自的执行结果互为对方的执行条件,从而限制各进程的执行速度的过程。由于共享某一共有资源而引起的在临界区内不允许并发进程交叉执行的现象,由共享共有资源而造成的对并发进程执行速度的间接制约简称为间接制约。受间接制约的类中各程序段在执行顺序上是任意的。
我们将不允许多个并发进程交叉执行的一段程序称为临界区。临界区是由属于不同并发进程的程序段共享公用数据或公用数据变量而引起的
二. 用信号量实现进程互斥
实现如下程序:
父进程
p操作
打印字母O
sleep
打印字母O
V操作
sleep()
子进程
p操作
打印字母X
sleep
打印字母X
V操作
sleep()
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define ERR_EXIT(m)\
do\
{ \
perror(m); \
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) */
};
// 创建信号量集
int sem_create(key_t key)
{
int semid = semget(key,1,0666 | IPC_CREAT | IPC_EXCL);
if(semid < 0)
ERR_EXIT("semget err");
return semid;
}
// P操作
int sem_p(int semid)
{
struct sembuf sb = {0,-1,0};
int ret = semop(semid,&sb,1);//nops指出opsptr指向的sembuf结构数组中元素的数目
if(ret < 0)
ERR_EXIT("semop err");
return ret;
}
// V操作
int sem_v(int semid)
{
struct sembuf sb = {0,1,0};
int ret = semop(semid,&sb,1);
if(ret < 0)
ERR_EXIT("semop err");
return ret;
}
// 删除信号量
int sem_d(int semid)
{
int ret = semctl(semid,0,IPC_RMID,0);
if(ret < 0)
ERR_EXIT("semctl err");
return ret;
}
// 设置信号量集中的信号量的计数值
int sem_setval(int semid,int val )
{
union semun su;
su.val = val;
//int semctl(int semid 标识, int semnum (0,1,...,nsems-1), int cmd(GETVAL,SETVAL,), ...);
int ret = semctl(semid,0,SETVAL,su); // su作为值传送的,不是按引用传送的,
if(ret < 0)
ERR_EXIT("semctl err");
printf("value updated ....\n");
return ret;
}
int semid;
void print(char op_char)
{
int pause_time;
srand(getpid());
int i=0;
for(;i<10;i++)
{
sem_p(semid); // P操作
printf("%c",op_char);
fflush(stdout); // 清空缓冲区
pause_time = rand()%3; // 睡眠时间为0,1,2 中一个
sleep(pause_time);
printf("%c",op_char);
fflush(stdout);
sem_v(semid); // V操作
// PV操作之外的
pause_time = rand()%3;
sleep(pause_time);
}
//printf("\n");
}
int main(int argc,char* argv[])
{
semid = sem_create(IPC_PRIVATE);
sem_setval(semid,0); // 开始信号量计数值为0
pid_t pid;
pid = fork();
if(pid < 0)
ERR_EXIT("fork err");
if(pid > 0) // 父进程
{
sem_setval(semid,1); //设置信号量集中的信号量的计数值为1
print('O'); // 调用上面的print函数
}
else // 子进程
{
print('X');
}
// 有一个进程早退出,会先调用sem_d(semid),导致另外一个进程执行pv操作出现错误.所有先注释掉下行代码
// sem_d(semid);
return 0;
}
分析: 父子两个进程调用同一个函数,但是函数内部用的是PV操作,调用代码段执行的是原子操作,打印的字符必须是成对出现的.最好sem_d(semld); 代码注释掉了,因为有一个进程肯定会早一步退出循环,执行这个语句,导致另一个进程执行PV操作出现错误.暂时没有处理信号量的删除操作.