1,Mikefile
在以上的实验中都只有一个文件,所以编译时输入的命令比较简单,gcc -o xx xx.c,就能满足需求了.但是当文件比较多的时候,又在调试时,很可能需要一遍一遍的输入命令,
并且还要记住这些文件名,如果文件过多,那么这将是灾难。这个时候你就需要掌握make这个强大的工具,以及Makefile的写法。和以前一样用到什么学习什么,而不是
把所有的内容一次学完,仍然和以前一样,我并不能说太多的原理,因为我说不太明白。我再写程序的时候我喜欢将不同的功能模块放到不同的目录中,这就牵扯到
Makefile的嵌套调用.结合这次实验,我使用到了信号量来同步对共享内存的读写,我将信号量的相关的代码放到了一个目录中----mysem
在这个目录中包含一下文件
mysem-
Makefile
mysem.c
mysem.h
semun.h
先说Makefile,在Makefile文件,我运用了宏定义,如:
CC = gcc
这里定义了采用了点编译器,这个宏定义有一个好处,就是在你的Makefile文件中在该宏定义之后,采用的编译器都是CC,如果你要将程序移植到另外一个系统中,需要修改
编译器,只需要修改这个宏定义即可,例如,就这个程序而言,是在x86系统下进行创作的,要编译成x86系统下的可执行文件,那么定义CC = gcc。如果我要就这个程序
移植到arm系统下,而arm系统的交叉编译器是arm-linix-gcc,于是只要简单的修改CC = arm-linux-gcc,再make一次,就可以。而不用在Makefile文件中将所有的gcc
修改成arm-linux-gcc。这个其实和C语言中的宏定义非常类似。
在Makefile中我还定义了另外两个宏分别是
INCLUDE = .
CFLAGS = -g -Wall
INCLUDE定义了一个包含的路径,用于指定Makefile中一些命令搜索头文件的路径。
CFLAGS = -g -Wall
指定在编译时产生警告,并在目标文件中生产debug信息。
接下来就是Makefile中的依赖关系的定义
在mysem目录中的Makefile有两条依赖关系
mysem.o: mysem.c mysem.h semun.h
clean: rm *.o
依赖关系告诉make程序,“:”前面的部分依赖“:”后面的部分,如果“:”后面的文件有一个改变,那么“:”前面的部分就需要改变,改变的方式将根据Makefile中定义
的规则
Makefile的规则,通俗来讲Makefile的规则就是一条一条的命令,对于一条依赖关系的规则是
$(CC) -I$(INCLUDE) $(CFLAGS) -c mysem.c
cp mysem.o ../
对于第二条依赖的规则是
rm *.o
对于第一条依赖的规则需要进一步说明,对宏的引用方式,$(宏).对于第一条规则,其实就是用gcc用-c选项来编译mysem.c。即gcc -I. -g -Wall -c mysem.c,这条
规则产生的结果是生成了一个mysem.o的目标文件。第二条命令是将上一条命令产生的结果拷贝到父目录中,我用这条命令来实现Makefile的嵌套,这样父目录中的
Makefile就可以引用mysem.o了。
mysem目录中的完整的Makefile如下:
-
#compiler
-
CC = gcc
-
-
#include
-
INCLUDE = .
-
-
#option debug
-
CFLAGS = -g -Wall
-
-
#option for release
-
#CFLAGS = -O2 -Wall
-
-
mysem.o: mysem.c mysem.h semun.h
-
$(CC) -I$(INCLUDE) $(CFLAGS) -c mysem.c
-
cp mysem.o ../
-
-
clean:
-
rm *.o
以上是子目录中的Makefile,接下来就是父目录中的Makefile了。这里只讲有差别的地方,
定义了一个子目录宏
SUBINCLUDE = ./mysem
对于宏就不再多说,
接下来就是依赖和规则了,第一个依赖,以及其规则如下
all: ipcshm.o mysem.o
$(CC) -o ipcshm ipcshm.o mysem.o
可以看出,这条规则将会产生可执行文件,可执行文件的名字为ipcshm。依赖ipcshm.o 和子目录Makefile产生的mysem.o。Makefile后面的部分将会产生ipcshm.o
和mysem.o.
mysem.o:
cd ./mysem && $(MAKE)
这条规则以及依赖将产生mysem.o。虽然在子目录中的Makefile有拷贝mysem.o到目录中,但是在父目录中总还是要对这条命令进行调用的地方,那么这条规则就是
调用子目录中Makefile的方法。
ipcshm.o: ipcshm.c
$(CC) -I$(INCLUDE) -I$(SUBINCLUDE) $(CFLAGS) -c ipcshm.c
这条规则和依赖并没有什么特殊的地方。仅仅是将ipcshm.c编译成ipcshm.o。其中多了一个包含文件,这是因为ipcshm.c需要用的mysem目录中的头文件。
最后一项依赖和规则就是清理产生的中间文件。不再累述。
父目录中完整的Makefile如下:
-
#Makefile
-
#compiler to use
-
CC = gcc
-
-
#include
-
INCLUDE = .
-
-
#sub include
-
SUBINCLUDE = ./mysem
-
-
#option for debug
-
CFLAGS = -g -Wall
-
-
#option for release
-
#CFLAGS = -O2 -Wall
-
-
all: ipcshm.o mysem.o
-
$(CC) -o ipcshm ipcshm.o mysem.o
-
-
mysem.o:
-
cd ./mysem && $(MAKE)
-
-
ipcshm.o: ipcshm.c
-
$(CC) -I$(INCLUDE) -I$(SUBINCLUDE) $(CFLAGS) -c ipcshm.c
-
-
clean:
-
rm -f $(INCLUDE)/*.o
-
rm -f *.o
-
rm -f $(SUBINCLUDE)/*.o
2.信号量
信号量在操作系统课中的另外一个说法是pv原语。
在使用信号量时要建立一个信号量应用的系统调用是semget其原型如下
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
函数成功返回一个信号量的id,在后续对信号量的操作就是通过这个id来进行。函数的第一参数key是与信号量管理的键值,有点像windows中的句柄,在程序中我把它
定义成了一个常数。第二个参数指出函数创建的是一组信号量,我在程序中传递的值是1.第三个参数我传递的是0666|IPC_CREAT.
创建完信号量还需要对信号量赋予初值,我在程序中将初值赋为1,只用来同步。
这将用到另外一个系统调用semctl,其原型如下:
#include
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
这个函数是参数可变的函数,第四个参数根据第三个参数来确定,当是四个参数时,第四个参数需要自己定义,它的类型union semun.我在头文件semun.h中进行了定
义,定义如下:
-
#ifndef __SEMUN_H__
-
#define __SEMUN_H__
-
-
union semun {
-
int val;
-
struct semid_ds *buf;
-
unsigned short *array;
-
struct seminfo *__buf;
-
};
-
-
#endif
在我的程序中,我用这个函数来设定信号量的初值,先要设定semun的val数据域为要设定的初始值,之后将第三个参数cmd射程SETVAL,之后将semun 联合体传送到
第四个参数,即可。在程序中,我将初值设定成1
-
int set_semvalue(int sem_id)
-
{
-
union semun sem_union;
-
-
sem_union.val = 1;
-
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) {
-
perror("set_setvalue fail to setctl:");
-
return -1;
-
}
-
return 0;
-
}
以上这些内容我将它们封装在一个函数内,这样便于使用函数代码如下:
-
//create semaphore and set init value or not
-
int semaphore_init(int semkey, int svflg)
-
{
-
int sem_id;
-
-
srand(getpid());
-
sem_id = semget(semkey, 1, 0666|IPC_CR EAT);
-
if (svflg) {
-
if (set_semvalue(sem_id)) {
-
printf("semaphore_init: setvalue return error!\n");
-
return -1;
-
}
-
}
-
return sem_id;
-
}
在使用信号量时要先调用semaphore_init来初始化一个信号量,它的第二个参数是用来标记是否对信号量进行赋值。当然也有不赋值的情况,那就是在不同的进程间应用
同一个信号量时,只需要一个进程来赋值就可以了。
p和v
现在已经建立了一个信号量那么我们就要应用它,也就是进行p和v操作,p是对信号量执行减1动作,如果结果非零就继续执行,如果为0进程挂起,直到信号变成非0.
v与p的操作相反,对信号执行加1动作,如果有其他进程执行p操作而挂起,这需要唤醒这些被挂起的进程,使其继续执行。
需要用到一个系统调用是 semop.其原型如下:
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned nsops);
结构struct sembuf 指明了要对信号量进行操作,p和v的函数如下:
-
//p
-
int semaphore_p(int sem_id)
-
{
-
struct sembuf sem_b;
-
-
sem_b.sem_num = 0;
-
sem_b.sem_op = -1;
-
sem_b.sem_flg = SEM_UNDO;
-
if (semop(sem_id, &sem_b, 1) < 0) {
-
perror("semaphore_p: fail to semop:");
-
return -1;
-
}
-
return 0;
-
}
-
-
-
//v
-
int semaphore_v(int sem_id)
-
{
-
struct sembuf sem_b;
-
-
sem_b.sem_num = 0;
-
sem_b.sem_op = 1;
-
sem_b.sem_flg = SEM_UNDO;
-
if (semop(sem_id, &sem_b, 1) < 0) {
-
perror("semaphore_v: fail to semop:");
-
return -1;
-
}
-
return 0;
-
}
信号量的删除,当不再需要信号量时,就需要将信号量释放。其代码如下
-
int del_semvalue(int sem_id)
-
{
-
union semun sem_union;
-
-
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) {
-
perror("del_setvalue fail to setctl:");
-
return -1;
-
}
-
return 0;
-
}
以上是有关信号量的操作。接下来将描述共享内存的通信方式。里面将穿叉对信号量函数的调用。来实现同步。
3.共享内存
实验程序是通过共享内存,父进程读取标准输入的输入数据,将其存放在共享内存中,子进程读取共享内存,并将内容显示到标准输出上。
共享内存和信号量一样,也需要先创建,用到的系统调用是
#include
#include
int shmget(key_t key, size_t size, int shmflg);
当建立玩共享内存时,需要和进程进行绑定,需要用的系统调用是shmat,其原型如下:
#include
#include
void *shmat(int shmid, const void* shmaddr, int shmflg);
绑定之后,就可以想操作数组一样操作这快内存。
当共享内存不再使用时,需要与进程解除绑定,需要的系统调用是
#include
#include
int shmdt(const void* shmaddr);
具体的参数可以看例子,在例子中,穿插了一些信号量的同步操作。下面是完整的代码
-
#include <stdio.h>
-
#include <sys/ipc.h>
-
#include <sys/shm.h>
-
#include <string.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
-
#include "./mysem/mysem.h"
-
-
#define SHM_KEY 1000
-
#define SEM_KEY 1001
-
-
#define SHM_SIZE 1024
-
-
int main(void)
-
{
-
int shm_id, sem_id;
-
int running = 1;
-
void *share_mem = (void*)0;
-
char *share_stuff = 0;
-
char buffer[SHM_SIZE];
-
pid_t pid;
-
//creat semaphore
-
sem_id = semaphore_init(SEM_KEY, 1);
-
if (sem_id < 0) {
-
printf("fail to semaphore_init\n");
-
return -1;
-
}
-
//creat share memory
-
shm_id = shmget(SHM_KEY, SHM_SIZE, 0666|IPC_CREAT);
-
if (shm_id < 0) {
-
perror("fail to shmget");
-
return -1;
-
}
-
pid = fork();
-
switch (pid) {
-
case -1:
-
perror("fail to fork");
-
return -1;
-
case 0: //child
-
//attached share memory
-
share_mem = shmat(shm_id, (void *)0, 0);
-
if (share_mem == (void *)-1) {
-
perror("fail to shmat");
-
return -1;
-
}
-
share_stuff = (char*)share_mem;
-
memset(share_stuff, sizeof(share_stuff), '\0');
-
while (running) {
-
if (semaphore_p(sem_id) < 0) {
-
printf("fail to semaphore_p\n");
-
return -1;
-
}
-
if (strlen(share_stuff))
-
printf("child is read form share memory: %s \n", share_stuff);
-
if (semaphore_v(sem_id) < 0) {
-
printf("fail to semaphore_v\n");
-
return -1;
-
}
-
if (strncmp(share_stuff, "end", 3) == 0)
-
running = 0;
-
// memset(share_stuff, sizeof(share_stuff), '\0');
-
sleep(5);
-
}
-
//disattached shared memory
-
if (shmdt(share_mem) < 0) {
-
perror("fail to shmdt");
-
return -1;
-
}
-
//delete shared memory
-
if (shmctl(shm_id, IPC_RMID, 0) == -1) {
-
perror("fail to shmctl");
-
return -1;
-
}
-
return 0;
-
default: //parent
-
//attached share memory
-
share_mem = shmat(shm_id, (void *)0, 0);
-
if (share_mem == (void *)-1) {
-
perror("fail to shmat");
-
return -1;
-
}
-
share_stuff = (char*)share_mem;
-
memset(share_stuff, sizeof(share_stuff), '\0');
-
-
while (running) {
-
if (semaphore_p(sem_id) < 0) {
-
printf("fail to semaphore_p\n");
-
return -1;
-
}
-
printf("parent read from stdin:");
-
fgets(buffer, SHM_SIZE, stdin);
-
strncpy(share_stuff, buffer, SHM_SIZE);
-
if (semaphore_v(sem_id) < 0) {
-
printf("fail to semaphore_v\n");
-
return -1;
-
}
-
if (strncmp(buffer, "end", 3) == 0)
-
running = 0;
-
-
sleep(5);
-
}
-
return 0;
-
}
-
}
4、所有代码包放在百度网盘上
名字为ipcshm.tar.bz2
阅读(3257) | 评论(3) | 转发(0) |