17.信号量同步编程
进程同步:
一组并发进程进行互相合作、互相等待,使得各进程按一定的顺序执行的过程称为进程间的同步。
生产者:1.创建文件。2.往文件写内容。(中间有sleep)。
进程同步中经典的实例:生产者与消费者的问题。
Producer.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
void main(){
int fd;
//create product
fd = open("/home/zhu",O_RDWR | O_CREAT,0777);
sleep(20);
//completing product
write(fd,"the product is complete!",24);
close(fd);
}
consumer.c:
#include <stdlib.h>
#include <stdio.h>
void main(){
//take away product
system("cp /home/zhu ./");
}
运行的结果:
我们看到产品是生产出来了,可是一个半成品,里面啥内容都没。这是因为我们的生产者,在生产东西的时候,还没完成,中途有事出去,然后消费者不知道产品还没完成就取走了。
这个问题和前面的信号量互斥有点相似:我们把信号量的初始值设置为0(与互斥不同),然后我们生产者不用获取信号量,他需要的是释放信号量,就是他完成生产一件产品之后要释放信号量。切记是在生产好了一件产品后释放信号量。就是生产者不获取信号量,只释放信号量。由于我们信号量的初始值是0,无法获取信号量,就是消费者不能拿走产品,因为信号量为0,这就确保了产品只有完成才能让消费者拿走,注意的是,我们的消费者不用释放信号量。
运行的结果:
Productor1.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void main(){
int fd;
key_t key;
int semid;
struct sembuf sops;
key = ftok("/home",2);
//create semaphore
semid = semget(key,1,IPC_CREAT);
semctl(semid,0,SETVAL,0);
//create product
fd = open("/home/zhu",O_RDWR | O_CREAT,0777);
sleep(20);
//completing product
write(fd,"the product is complete!",24);
//free
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
close(fd);
}
consumer1.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/sem.h>
void main(){
key_t key;
int semid;
struct sembuf sops;
key = ftok("/home",2);
//create semaphore
semid = semget(key,1,IPC_CREAT);
//get semaphore
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;//free
semop(semid,&sops,1);
//take product
system("cp /home/zhu ./");
}
运行的结果:
拿走成功:
我们就实现了只有生产者生产好了产品,消费者才能拿走的机制。
我们在消费者程序中用到system函数:
man 3 system:的信息:
NAME
system - execute a shell command
SYNOPSIS
#include <stdlib.h>
int system(const char *command);
DESCRIPTION
system() executes a command specified in command by calling /bin/sh -c command,
and returns after the command has been completed. During execution of the com-
mand, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.
RETURN VALUE
The value returned is -1 on error (e.g. fork(2) failed), and the return status
of the command otherwise. This latter return status is in the format specified
in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In
case /bin/sh could not be executed, the exit status will be that of a command
that does exit(127).
If the value of command is NULL, system() returns non-zero if the shell is
available, and zero if not.
system() does not affect the wait status of any other children.
CONFORMING TO
C89, C99, POSIX.1-2001.
NOTES
If the _XOPEN_SOURCE feature test macro is defined, then the macros described in
wait(2) (WEXITSTATUS(), etc.) are made available when including <stdlib.h>.
As mentioned, system() ignores SIGINT and SIGQUIT. This may make programs that
call it from a loop uninterruptible, unless they take care themselves to check
the exit status of the child. E.g.
while (something) {
int ret = system("foo");
if (WIFSIGNALED(ret) &&
(WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT))
break;
}
Do not use system() from a program with set-user-ID or set-group-ID privileges,
because strange values for some environment variables might be used to subvert
system integrity. Use the exec(3) family of functions instead, but not exe-
clp(3) or execvp(3). system() will not, in fact, work properly from programs
with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash
version 2, since bash 2 drops privileges on startup. (Debian uses a modified
bash which does not do this when invoked as sh.)
In versions of glibc before 2.1.3, the check for the availability of /bin/sh was
not actually performed if command was NULL; instead it was always assumed to be
available, and system() always returned 1 in this case. Since glibc 2.1.3, this
check is performed because, even though POSIX.1-2001 requires a conforming
implementation to provide a shell, that shell may not be available or executable
if the calling program has previously called chroot(2) (which is not specified
by POSIX.1-2001).
It is possible for the shell command to return 127, so that code is not a sure
indication that the execve(2) call failed.
SEE ALSO
sh(1), signal(2), wait(2), exec(3)
COLOPHON
This page is part of release 3.22 of the Linux man-pages project. A description
of the project, and information about reporting bugs, can be found at