Chinaunix首页 | 论坛 | 博客
  • 博客访问: 101507
  • 博文数量: 14
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 206
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-28 09:37
个人简介

记录我自己的成长....

文章分类

全部博文(14)

文章存档

2014年(2)

2013年(12)

我的朋友

分类: LINUX

2013-10-11 21:29:58

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如下:

点击(此处)折叠或打开

  1. #compiler
  2. CC = gcc

  3. #include
  4. INCLUDE = .

  5. #option debug
  6. CFLAGS = -g -Wall

  7. #option for release
  8. #CFLAGS = -O2 -Wall

  9. mysem.o: mysem.c mysem.h semun.h
  10.         $(CC) -I$(INCLUDE) $(CFLAGS) -c mysem.c
  11.         cp mysem.o ../

  12. clean:
  13.         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如下:

点击(此处)折叠或打开

  1. #Makefile
  2. #compiler to use
  3. CC = gcc

  4. #include
  5. INCLUDE = .

  6. #sub include
  7. SUBINCLUDE = ./mysem

  8. #option for debug
  9. CFLAGS = -g -Wall

  10. #option for release
  11. #CFLAGS = -O2 -Wall

  12. all: ipcshm.o mysem.o
  13.         $(CC) -o ipcshm ipcshm.o mysem.o

  14. mysem.o:
  15.         cd ./mysem && $(MAKE)
  16.         
  17. ipcshm.o: ipcshm.c
  18.         $(CC) -I$(INCLUDE) -I$(SUBINCLUDE) $(CFLAGS) -c ipcshm.c

  19. clean:
  20.         rm -f $(INCLUDE)/*.o
  21.         rm -f *.o
  22.         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中进行了定
     义,定义如下:
         

点击(此处)折叠或打开

  1. #ifndef __SEMUN_H__
  2. #define __SEMUN_H__

  3. union semun {
  4.   int val;
  5.   struct semid_ds *buf;
  6.   unsigned short *array;
  7.   struct seminfo *__buf;
  8. };

  9. #endif
     在我的程序中,我用这个函数来设定信号量的初值,先要设定semun的val数据域为要设定的初始值,之后将第三个参数cmd射程SETVAL,之后将semun 联合体传送到
     第四个参数,即可。在程序中,我将初值设定成1
               

点击(此处)折叠或打开

  1. int set_semvalue(int sem_id)
  2. {
  3.   union semun sem_union;

  4.   sem_union.val = 1;
  5.   if (semctl(sem_id, 0, SETVAL, sem_union) == -1) {
  6.     perror("set_setvalue fail to setctl:");
  7.     return -1;
  8.    }
  9.   return 0;
  10. }



    以上这些内容我将它们封装在一个函数内,这样便于使用函数代码如下:
   

点击(此处)折叠或打开

  1. //create semaphore and set init value or not
  2. int semaphore_init(int semkey, int svflg)
  3. {
  4.   int sem_id;

  5.   srand(getpid());
  6.   sem_id = semget(semkey, 1, 0666|IPC_CR   EAT);
  7.   if (svflg) {
  8.     if (set_semvalue(sem_id)) {
  9.       printf("semaphore_init: setvalue return error!\n");
  10.       return -1;
  11.     }
  12.   }
  13.   return sem_id;
  14. }
    在使用信号量时要先调用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的函数如下:
    

点击(此处)折叠或打开

  1. //p
  2. int semaphore_p(int sem_id)
  3. {
  4.   struct sembuf sem_b;

  5.   sem_b.sem_num = 0;
  6.   sem_b.sem_op = -1;
  7.   sem_b.sem_flg = SEM_UNDO;
  8.   if (semop(sem_id, &sem_b, 1) < 0) {
  9.     perror("semaphore_p: fail to semop:");
  10.     return -1;
  11.   }
  12.   return 0;
  13. }


  14. //v
  15. int semaphore_v(int sem_id)
  16. {
  17.   struct sembuf sem_b;

  18.   sem_b.sem_num = 0;
  19.   sem_b.sem_op = 1;
  20.   sem_b.sem_flg = SEM_UNDO;
  21.   if (semop(sem_id, &sem_b, 1) < 0) {
  22.     perror("semaphore_v: fail to semop:");
  23.     return -1;
  24.   }
  25.   return 0;
  26. }
     信号量的删除,当不再需要信号量时,就需要将信号量释放。其代码如下
   

点击(此处)折叠或打开

  1. int del_semvalue(int sem_id)
  2. {
  3.   union semun sem_union;

  4.   if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) {
  5.     perror("del_setvalue fail to setctl:");
  6.     return -1;
  7.   }
  8.   return 0;
  9. }
    以上是有关信号量的操作。接下来将描述共享内存的通信方式。里面将穿叉对信号量函数的调用。来实现同步。

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);
    具体的参数可以看例子,在例子中,穿插了一些信号量的同步操作。下面是完整的代码
    

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/ipc.h>
  3. #include <sys/shm.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include <unistd.h>

  7. #include "./mysem/mysem.h"

  8. #define SHM_KEY 1000
  9. #define SEM_KEY 1001

  10. #define SHM_SIZE 1024

  11. int main(void)
  12. {
  13.   int shm_id, sem_id;
  14.   int running = 1;
  15.   void *share_mem = (void*)0;
  16.   char *share_stuff = 0;
  17.   char buffer[SHM_SIZE];
  18.   pid_t pid;
  19.    //creat semaphore
  20.   sem_id = semaphore_init(SEM_KEY, 1);
  21.   if (sem_id < 0) {
  22.     printf("fail to semaphore_init\n");
  23.     return -1;
  24.   }
  25.   //creat share memory
  26.   shm_id = shmget(SHM_KEY, SHM_SIZE, 0666|IPC_CREAT);
  27.   if (shm_id < 0) {
  28.     perror("fail to shmget");
  29.     return -1;
  30.   }
  31.   pid = fork();
  32.   switch (pid) {
  33.   case -1:
  34.     perror("fail to fork");
  35.     return -1;
  36.   case 0: //child
  37.     //attached share memory
  38.     share_mem = shmat(shm_id, (void *)0, 0);
  39.    if (share_mem == (void *)-1) {
  40.       perror("fail to shmat");
  41.       return -1;
  42.     }
  43.     share_stuff = (char*)share_mem;
  44.     memset(share_stuff, sizeof(share_stuff), '\0');
  45.     while (running) {
  46.       if (semaphore_p(sem_id) < 0) {
  47.         printf("fail to semaphore_p\n");
  48.         return -1;
  49.       }
  50.       if (strlen(share_stuff))
  51.         printf("child is read form share memory: %s \n", share_stuff);
  52.       if (semaphore_v(sem_id) < 0) {
  53.         printf("fail to semaphore_v\n");
  54.         return -1;
  55.       }
  56.       if (strncmp(share_stuff, "end", 3) == 0)
  57.         running = 0;
  58.     // memset(share_stuff, sizeof(share_stuff), '\0');
  59.           sleep(5);
  60.     }
  61.     //disattached shared memory
  62.     if (shmdt(share_mem) < 0) {
  63.       perror("fail to shmdt");
  64.       return -1;
  65.     }
  66.     //delete shared memory
  67.     if (shmctl(shm_id, IPC_RMID, 0) == -1) {
  68.       perror("fail to shmctl");
  69.       return -1;
  70.     }
  71.     return 0;
  72.   default: //parent
  73.     //attached share memory
  74.     share_mem = shmat(shm_id, (void *)0, 0);
  75.     if (share_mem == (void *)-1) {
  76.       perror("fail to shmat");
  77.       return -1;
  78.     }
  79.        share_stuff = (char*)share_mem;
  80.     memset(share_stuff, sizeof(share_stuff), '\0');

  81.     while (running) {
  82.       if (semaphore_p(sem_id) < 0) {
  83.         printf("fail to semaphore_p\n");
  84.         return -1;
  85.       }
  86.       printf("parent read from stdin:");
  87.       fgets(buffer, SHM_SIZE, stdin);
  88.       strncpy(share_stuff, buffer, SHM_SIZE);
  89.       if (semaphore_v(sem_id) < 0) {
  90.         printf("fail to semaphore_v\n");
  91.          return -1;
  92.       }
  93.       if (strncmp(buffer, "end", 3) == 0)
  94.         running = 0;

  95.       sleep(5);
  96.     }
  97.     return 0;
  98.   }
  99. }
4、所有代码包放在百度网盘上
      
  名字为ipcshm.tar.bz2



阅读(3257) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

songjyf2013-10-14 13:45:04

mypsl:博主你好,我进入不了你的百度网盘,请你发一份代码包到我的邮箱中 mypsl@163.com 学习,谢谢

连接已经修改了.现在也可以下载了.

回复 | 举报

songjyf2013-10-14 13:32:25

mypsl:博主你好,我进入不了你的百度网盘,请你发一份代码包到我的邮箱中 mypsl@163.com 学习,谢谢

已经发送.

回复 | 举报

mypsl2013-10-12 16:04:34

博主你好,我进入不了你的百度网盘,请你发一份代码包到我的邮箱中 mypsl@163.com 学习,谢谢