转自http://www.cnblogs.com/lycheng/archive/2011/11/23/2260656.html
有两个生产者,一个写入大写字母,另一个写入小写。
有三个消费者,一个消费大写字母,一个消费小写字母,还有一个不分大小写消费。
#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <linux/sem.h>
#include <sys/types.h>
#define MAX_PRODUCTS 10
struct sembuf buf;
int down(int sem_id)
{
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
if (semop(sem_id, &buf, 1) == -1)
{
perror("Down failed!/n");
return 0;
}
return 1;
}
int up(int sem_id)
{
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
if (semop(sem_id, &buf, 1) == -1)
{
perror("Up failed!/n");
return 0;
}
return 1;
}
void main()
{
union semun arg;
pid_t producer1, producer2;
pid_t consumer1, consumer2, consumer3;
int mutex;
int full;
int empty;
char *buffer;
// 共享内存
buffer = (char*) mmap(NULL, sizeof(char) * MAX_PRODUCTS,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
int i;
for (i = 0; i < MAX_PRODUCTS; i++)
buffer[i] = '';
// 创建信号量
mutex = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
full = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
empty = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
arg.val = 1;
if (semctl(mutex, 0, SETVAL, arg) == -1)
{
perror("set the mutex error\n");
return;
}
arg.val = 0;
if (semctl(full, 0, SETVAL, arg) == -1)
{
perror("set the full error\n");
return;
}
arg.val = MAX_PRODUCTS;
if (semctl(empty, 0, SETVAL, arg) == -1)
{
perror("set the empty error/n");
return;
}
if ((producer1 = fork()) == 0)
{
// 写入大写字母
while (1)
{
down(empty);
down(mutex);
srand(time(NULL));
int offset = rand() % 26;
char item = 'A' + offset;
int j;
for (j = 0; j < MAX_PRODUCTS; j++)
{
if (buffer[j] == '')
{
buffer[j] = item;
printf("Producer A insert letter: %c\n", item);
break;
}
}
up(mutex);
up(full);
sleep(1);
}
}
else if ((producer2 = fork()) == 0)
{
// 写入小写字母
while (1)
{
down(empty);
down(mutex);
srand(time(NULL));
int offset = rand() % 26;
char item = 'a' + offset;
int j;
for (j = 0; j < MAX_PRODUCTS; j++)
{
if (buffer[j] == '')
{
buffer[j] = item;
printf("Producer B insert letter: %c\n", item);
break;
}
}
up(mutex);
up(full);
sleep(1);
}
}
else if ((consumer1 = fork()) == 0)
{
// 消费大写字母
while (1)
{
down(full);
down(mutex);
char item;
int j;
for (j = 0; j < MAX_PRODUCTS; j++)
{
if (buffer[j] >= 'A' && buffer[j] <= 'Z')
{
item = buffer[j];
buffer[j] = '';
break;
}
}
printf("Consumer A removes letter: %c\n", item);
up(mutex);
up(empty);
sleep(2);
}
}
else if ((consumer2 = fork()) == 0)
{
// 消费小写字母
while (1)
{
down(full);
down(mutex);
char item;
int j;
for (j = 0; j < MAX_PRODUCTS; j++)
{
if (buffer[j] >= 'a' && buffer[j] <= 'z')
{
item = buffer[j];
buffer[j] = '';
break;
}
}
printf("Consumer B removes letter: %c\n", item);
up(mutex);
up(empty);
sleep(2);
}
}
else if ((consumer3 = fork()) == 0)
{
// 大小写字母
while (1)
{
down(full);
down(mutex);
char item;
int j;
for (j = 0; j < MAX_PRODUCTS; j++)
{
if (buffer[j] != '')
{
item = buffer[j];
buffer[j] = '';
break;
}
}
printf("Consumer C removes letter: %c\n", item);
up(mutex);
up(empty);
sleep(2);
}
}
else
{
// 主进程
while (1)
{
int j;
printf("Buffer: ");
for (j = 0; j < MAX_PRODUCTS; j++)
{
printf("%c ", buffer[j]);
}
printf(" \n");
sleep(10);
}
}
}
参考: http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index1.html#3 共享内存
这个生产者消费者问题用线程实现起来会容易很多,进程间通信的话涉及到共享内存的问题。还有生成随机数的问题还是很纠结的,不知道为什么大小写生产者都是生成同一个字母。
linux的进程通信:信号量实例(C语言)
转自http://hector.blog.51cto.com/4229131/758930
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- union semun
- {
- int val;
- struct semid_ds *buf;
- unsigned short int *array;
- struct seminfo *__buf;
- };
- int main(void)
- {
- char* buf_child[]={"this", "is", "the", "child", "process"};
- char* buf_father[]={"father", "say", "hello", "to", "child"};
- int i = 0, semid, fd;
- pid_t pid;
- struct sembuf sb; //信号量操作
- union semun sem;
- semid = semget(1000, 2, 0666 | IPC_CREAT); //申请信号量组,包含2个信号量
- sem.val = 0;
- semctl(semid, 0, SETVAL, sem); //初始化0号信号量为0
- sem.val = 1;
- semctl(semid, 1, SETVAL, sem); //初始化1号信号量为1
- fd=open("tmp",O_CREAT|O_TRUNC|O_WRONLY,0666);
- pid = fork();
- switch (pid) {
- case -1:
- perror("fork fail");
- break;
- case 0: /* child consume */
- srand((unsigned int)getpid());
- while (i < 5) {
- sb.sem_num = 1; //将1号信号量
- sb.sem_op = -1; //减1
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1);
- write(fd,buf_child[i], strlen(buf_child[i]));
- sleep(rand());
- write(fd,&" ", 1);
- i++;
- sb.sem_num = 0; //将0号信号量
- sb.sem_op = 1; //加1
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1); //操作信号量
- }
- break;
- default:/* parent production */
- srand((unsigned int)getpid());
- while (i < 5) {
- sb.sem_num = 0; //将0号信号量
- sb.sem_op = -1; //减1
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1); //操作信号量
- write(fd,buf_father[i], strlen(buf_father[i]));
- sleep(rand());
- write(fd,&" ", 1);
- i++;
- sb.sem_num = 1;
- sb.sem_op = 1;
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1);
- }
- break;
- }
- return 0;
- }
Linux 并发控制的几个例子
(生产者与消费者,哲学家进餐,读者写者)
今天重新开始项目,查阅资料的时候发现了几个很典型有意思的并发控制的问题,自己学习了一下,也整理了一下。
1、生产者和消费者问题:
a、单缓冲区问题描述:生产者向消费者提供产品,它们共享一个有界缓冲区,生产者向其中投放产品,消费者从中取得产品。同时,每个进程都互斥的占用CPU。假定生产者和消费者是互相等效的,只要缓冲区未满,生产者就可以把产品送入缓冲区,类似的,只要缓冲区未空,消费者便可以从缓冲区中取走产品并消费它。生产者—消费者的同步关系将禁止生产者向已满的缓冲区中放入产品,也禁止消费者从空的缓冲区中获取产品
问题分析:需要定义两个信号量,一个用于互斥访问缓冲区,另一个用于生产者与消费者之间的同步。s1=1; s2=0;
伪代码:
生产者进程 |
消费者进程 |
while(1) { printf(“I'm producing!\n”); down_interruptible(&s1); printf(“I'm putting a product into buffer\n”); up(&s1); up(&s2); } |
while(1) { down_interruptible(&s2); down_interruptible(&s1); printf(“I'm getting a product\n”); up(&s1); printf(“I'm consuming a product\n”); } |
理解:
s1用来控制互斥访问,即在同一个时刻只能有读或者写对缓冲区进行操作。
s2用来控制缓冲区的商品量,当s2的量不为0时,即生产者进行生产之后,才能进行消费,与s1不同的是,s1只能控制对产品的访问,即不能同时访问,但是对产品的量没有规定,而s2规定其必须有生产后才能进行消费。
问题:
s2只规定了在产品为0时不能进行消费,即对消费的最低值进行规定,但是没有规定生产的最高值,即生产者—消费者的同步关系将禁止生产者向已满的缓冲区中放入产品这个问题没有解决。
也可以直接将s2用变量buffer代替,设置上下限,即可弥补以上功能
生产者进程 |
消费者进程 |
while(1) { If(buffer<max) printf(“I'm producing!\n”); down_interruptible(&s1); printf(“I'm putting a product into buffer\n”); up(&s1); buffer++; } |
while(1) { if(buffer>0) down_interruptible(&s1); printf(“I'm getting a product\n”); up(&s1); printf(“I'm consuming a product\n”); buffer--; } |
与上一个的区别在于当flag满足不了条件时,进程不会睡眠(也可在else中设置睡眠功能)。
b. 多生产者、多消费者、n个缓冲区问题描述:有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程并发执行,在两者之间设置了n个缓冲区,生产者将产品放入一个缓冲区中,消费者可以从一个缓冲区中取走产品去消费。要求生产者进程与消费者进程必须保持同步,即不允许生产者进程向一个满的缓冲区放产品,也不允许消费者从一个空的缓冲区取产品。
问题分析:该问题貌似比a问题复杂的多,首先我们定义一个数组buffer[n],来表示n个缓冲区,还需要定义两个变量:in 表示要存入的缓冲区的下标,out表示要取产品的缓冲区的下标。定义三个信号量:s1用于实现对缓冲池的互斥操作,empty表示空缓冲区的个数,full表示满缓冲区的个数。初值:in=out=0; s1=1; full=0; empty=n;
生产者进程 |
消费者进程 |
while(1) { printf(“I'm producing!\n”); // 生成数据 down(&empty); // 将空槽数目减 1 down(&s1); // 进入临界区 printf(“I'm putting a product into buffer[in]\n”); // 将新数据放入缓冲区 in = (in+1)%n; //主要目的是没有n,只有0 up(&mutex); // 离开临界区 up(&full); // 将满槽的数目加 1 } |
while(1) { down_interruptible(&full); down_interruptible(&s1); printf(“I'm getting a product from buffer[out]\n”); out = (out+1)%n; up(&s1); up(&empty); printf(“I'm consuming a product\n”); } |
理解:这个问题和第一个实质是一样的,唯一的区别在于empty的使用上,加入了empty的信号量,实质上就是增加了缓冲区上限,实现了上限的功能。
即当缓冲区满了之后,empty此时为0,此时生产者进程进入休眠,等待消费者进程消耗产品。
full则是实现了下限的功能,当full为0时,即没有产品,消费者必须进入休眠,等待生产。
2.哲学家进餐问题
问题描述:五个哲学家共用一个圆桌,分别坐在周围的五张椅子上,在圆桌上有五只碗和五只筷子,他们交替进行思考和进餐。哲学家饥饿时便试图取最靠近他的两只筷子,当同时获得两只筷子时便可用餐,用餐完毕后放下筷子。
问题分析:五只筷子为临界资源,定义包含五个元素的信号量数组来实现对筷子的互斥使用。chopstick[5],五个信号量的初值都为1。
伪代码:
第i(i=0,1,2,3,4)个哲学家进程:
while(1)
{
down_interruptible(&chopstick[i]);
down_interruptible(&chopstick[(i+1)%5]);
printf(“I'm eating!\n”);
up(&chopstick[i]);
up(&chopstick[(i+1)%5);
}
理解:
其本质是资源的互斥占用。这个资源有两个。
3.读者写者问题:
问题描述:一个文件可被多个进程共享,reader进程读取该文件,而writer进程负责写文件,允许多个reader进程同时读取文件,但不允许一个writer进程和其他reader进程或writer进程同时访问文件。
问题分析:进程对文件互斥访问的实现可借助一个信号量就可以搞定,但是我们需要引入一个count变量来记录reader进程的个数,对这个变量的访问也是互斥的,所以也需要引入一个信号量。定义信号量rs实现对count的互斥访问,定义ws实现对文件的互斥访问。两信号量初值都为1
伪代码:
读者进程 |
写者进程 |
while(1) { down(&rs); down(&ws); count++; up(&rs); printf(“I’m reading”); read(); down(&rs); count--; if(count==0) {up(ws);} up(rs); } |
while(1) { down(&ws); if (count == 0) { printf(“I’m writing”); write(); up(&ws;) } } |
rs用来保护count在reader之间是独立访问的,count的作用是来保证读者没有了之后才能进行写。
ws用来保证文件在reader与writer之间是独立访问的。
参考http://blog.sina.com.cn/s/blog_4770ef020101gjyx.html
Linux 并发控制
在linux驱动开发中,由于驱动模块有可能被多个线程同时访问,会导致驱动模块中的变量互相影响,则需要对驱动模块中的资源进行访问控制。
在常用的linux驱动开发中,常用的并发控制有自旋锁和信号量。Linux内核中大部分用到的信号量都是互斥锁,所以主要总结自旋锁和互斥锁。
1、 信号量
信号量是一个整型值,结合一对函数,一个将进入临界区的进程将调用相关信号量的sem,如果信号量的值大于0,这个值将减一并且继续进程。相反,如果信号量的值是0(或者更小),进程必须等待别的进程释放信号量,解锁信号量通过调用相应函数完成,这个函数会增加信号量的值,并且。如果需要,可以唤醒等待的进程。
信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。
头文件:<asm/semaphore.h>
1) 定义信号量:
2) 初始化信号量 :
定义和初始化可以一步完成:
3) 获取信号量:
获取信号量,如果信号量为0(即无法获得信号量)则该线程睡眠,否则获取信号量,信号量减1.
与down()功能类似,但是前者不能被信号打断,后者能被信号打断(暂还不懂,后续再研究)
与down()功能类似,后者能被kill信号打断。
尝试获得信号量,如果获得则返回0,如果不能获得则返回非0值,不会导致线程睡眠,可用在中断上下文中
4) 释放信号量
由于信号量能够导致调用者睡眠,因此不能在中断上下文使用。(详细的再研究)
2、 互斥锁
互斥锁也就是信号量只能被一个线程所拥有,即信号量的值只能使0和1。
如1所示,定义与信号量方式相同
初始化互斥锁
其他操作与信号量操作相同。
3、 自旋锁
自旋锁与互斥锁一样,都是让锁只能在一个调用者手上,区别在于如果得不到锁,互斥锁会让调用者睡眠,而自旋锁调用者则会一直循环查看能否得到锁,即查看锁的拥有者是否释放了锁。类似于原地旋转,因此被称为自旋锁。
由于自旋锁不能使调用者睡眠,因此自旋锁不能被归为信号量。不会导致睡眠,因此自旋锁可以用在中断上下文以及进程上下文中。
由于自旋锁是不停循环,该线程依旧在运行,因此自旋锁适合资源只需保持很短时间的情况。
1) 定义自旋锁
2) 初始化自旋锁
3) 获得自旋锁
//成功获得自旋锁立即返回,否则自旋在那里直到该自旋锁的保持者释放
//成功获得自旋锁立即返回真,否则返回假,而不是像上一个那样"在原地打转"
4) 释放自旋锁
5) 使用代码
自旋锁不能递归使用,即只有一个线程使用锁,自己释放自己的锁。而不是不同线程互相释放锁。这样会可能会导致线程永远得不到锁。
二、进程、线程之间的同步
1。生产者消费者问题(信号量+mutex)
参考教材中的生产者消费者算法,创建5个进程,其中两个进程为生产者进程,3个进程为消费者进程。一个生产者进程试图不断地在一个缓冲中写入大写字母,另一个生产者进程试图不断地在缓冲中写入小写字母。3个消费者不断地从缓冲中读取一个字符并输出。为了使得程序的输出易于看到结果,仿照阅读材料中的实例程序,分别在生产者和消费者进程的合适的位置加入一些随机睡眠时间。
可选的实验:在上面实验的基础上实现部分消费者有选择地消费某些产品。例如一个消费者只消费小写字符,一个消费者只消费大写字母,而另一个消费者则无选择地消费任何产品。消费者要消费的产品没有时,消费者进程被阻塞。注意缓冲的管理。
2。用信号量和mutex方式实现睡觉的理发师问题
3。读者写者问题
教材和相关的阅读材料中对读者写者问题算法均有描述,但这个算法在不断地有读者流的情况下,写者会被阻塞。编写一个写者优先解决读者写者问题的程序,其中读者和写者均是多个进程,用信号量作为同步互斥机制。
转自//http://blog.csdn.net/homking/article/details/5027569
理发店里有一位理发师、一把理发椅和n把供等候理发的顾客坐的椅子。如果没有顾客,理发师在理发椅上睡觉。一个顾客来到时,叫醒理发师,如果理发师正在理发时有顾客到来,则如果有空椅子可以坐,就坐下来等待,否则离开。
利用三个信号量和一个控制变量来协调理发师、理发椅和顾客之间的活动。
1.信号量customers 用来记录等候理发的顾客数,并用坐阻塞理发师进程,初值为0
2.信号量barbers 记录正在等候顾客的理发师数
3.信号量waiting 用来记录等候理发的顾客数
4.信号量mutex 用于互斥,初值为1