Linux 系统应用编程——进程间通信(下)

 在前面,我们学习了传统的进程间通信方式——无名管道(pipe)、有名管道(fifo)和信号(signal)。

        下面我们来学习 System V  IPC 对象

1、共享内存(share memory);

2、信号灯(semaohore);

3、消息队列(message queue);

 

       IPC对象是活动在内核级别的一种进程间通信的工具。存在的IPC对象通过它的标识符来引用和访问,这个标识符是一个非负整数,它唯一的标识了一个IPC对象,这个IPC对象可以是消息队列或信号量或共享存储器中的任意一种类型。

     Linux系统中标识符被声明成整数,所以可能存在的最大标识符为65535。当时对于IPC对象,其标识符(id)与文件描述符有所不同,使用open函数打开一个文件时,返回的文件描述符的值为当前进程最小可用的文件描述符数组的下标。IPC对象是系统全局的流水号,其删除或创建时相应的标识符的值会不断增加到最大的值,归零循环分配使用。

     IPC的标识符只解决了内部访问一个IPC对象的问题,如何让多个进程都访问某一个特定的IPC对象还需要一个外部键(key),一个IPC对象都与一个键相关联。这样就解决了多进程在一个IPC对象上汇合的问题。IPC对象时需要指定一个键值,类型为key_t,在<sys/types.h>中定义为一个长整型。键值到标识符的转换是由系统内核来维护的,这里调用IPC对象的创建函数(semget msgget shmget )实现key 值到 id 的转换。

      从上图中我们可以看到得到这个键值 key 有两种方法:

1)通用方法:调用ftok()函数
       函数ftok可以使用两个参数生成一个键值,函数原型如下:

[cpp] view plaincopy
  1. #include <sys/ipc.h>  
  2. key_t ftok( const char *path, int id );  

       函数中参数path是一个文件名。函数中进行的操作是,取该文件的stat结构的st_dev成员和st_ino成员的部分值,然后与参数ID的第八位结合起来生成一个键值。由于只是使用st_dew和st_ino的部分值,所以会丢失信息,不排除两个不同文件使用同一个ID,得到同样键值的情况。
系统为每一个IPC对象保存一个ipc_perm结构体,该结构说明了IPC对象的权限和所有者,每一个版本的内核各有不用的ipc_perm结构成员。

文件<sys/ipc.h> 中对其定义:

[cpp] view plaincopy
  1. struct ipc_perm   
  2. {  
  3.     ey_t key;  
  4.     id_t uid;  
  5.     id_t gid;  
  6.     id_t cuid;  
  7.     id_t cgid;  
  8.     nsigned short mode;  
  9.     nsigned short seq;  
  10. };  

2)父子进程之间:

      Key 为IPC_PRIVATE,父子进程之间key值为IPC_PRIVATE。

      当有了一个IPC对象的键值,如何让多个进程知道这个键,可以有多种实现的办法:

1) 、使用文件来做中间的通道,创建IPC对象进程,使用键IPC_PRIVATE成功建立IPC对象之后,将返回的标识符存储在一个文件中。其他进程通过读取这个标识符来引用IPC对象通信。

2)、定义一个多个进程都认可的键,每个进程使用这个键来引用IPC对象,值得注意的是,创建IPC对象的进程中,创建IPC对象时如果该键值已经与一个IPC对象结合,则应该删除该IPC对象,再创建一个新的IPC对象。

3)、多进程通信中,对于指定键引用一个IPC对象而言,可能不具有拓展性,并且在该键值已经被一个IPC对象结合的情况下。所以必须删除这个存在对象之后再建立一个新的。这有可能影响到其他正在使用这个对象的进程。函数ftok可以在一定程度上解决这个问题。

但IPC对象存在一些问题,主要集中在以下几点:

1)、过于繁杂的编程接口,比起使用其他通信方式,IPC所要求的代码量要明显增多。

2)、IPC不使用通用的文件系统,这也是饱受指责的原因。所以不能使用标准I/O操作函数来读写IPC对象。为此不得不新增加一些函数来支持必要的一些操作(例如msgget msgrev msgctl等)并且对于不同类型的IPC对象都有一系列特定的操作函数。由于IPC不使用文件描述符,所以不能使用多路I/O监控函数select及poll函数来操作IPC对象。

3)、缺少的资源回收机制。由于IPC对象在使用过程中并不保存引用计数,所以当出现一个进程创建了IPC对象然后退出时,则这个对象只有在出现后面几种情况才会被释放或者删除,即由某一个进程读出消息,或者IPC的所有者或超级用户删除了这个对象。这也是IPC相对于管道或FIFO所欠缺的资源回收机制。

下面是文件对象和IPC对象的对比

 

一、共享内存

       共享内存可以说是Linux 下最快速、最有效的进程间通信方式。两个不同进程A 、B 共享内存的意思是,同一块物理内存被映射到进程A 、B 各自的进程地址空间,进程A 可以即时看到进程B 对共享内存中数据的更新;反之,进程B 也可以即时看到进程A对共享内存中数据的更新。

       这里简单说下映射的概念:

       Linux系统会为每个进程分配 4GB 的虚拟地址空间,一定情况下,需要将虚拟内存转换成物理内存,这就需要内存映射。为什么我们需要使用虚拟地址呢?最主要的原因是不同PC的物理内存会不一样,如果直接使用物理地址,会造成程序的移植性很差,另外虚拟地址访问内存有以下优势:

1、程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。

2、程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

3、不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。

      进程可用的虚拟地址范围称为该进程的“虚拟地址空间”。每个用户模式进程都有其各自的专用虚拟地址空间。对于 32 位进程,虚拟地址空间通常为 4 GB,范围从 0x00000000 至 0xFFFFFFFF。

1、共享内存的概念

      共享内存从字面意义解释就是多个进程可以把一段内存映射到自己的进程空间,以此来实现数据的共享及传输,这也是所有进程间通信方式最快的一种,共享内存是存在于内核级别的一种资源。

      在Shell 中可以使用ipcs 命令查看当前系统IPC中的状态,在文件系统中/proc目录下有对其描述的相应文件

 ipcs  -m ,其中 -m 是 memory 的意思 。

      在系统内核为一个进程分配内存地址时,通过分页机制可以让一个进程的物理地址不连续,同时也可以让一段内存同时分配给不同的进程。共享内存机制就是通过该原理实现的,共享内存机制只是提供数据的传送,如何控制服务器端和客户端的读写操作互斥,这就需要一些其他的辅助工具,例如信号量。

      采用共享内存通信的一个显而易见的好处就是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户控件进行四次数据的拷贝,而共享内存只拷贝两次数据:一次从输入文件到共享区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,知道通信完毕为止,这样,数据内同一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在接触映射时才写回文件的。因此,采用共享内存的通信方式效率是最高的。

     共享内存最大不足之处在意,由于多个进程对同一块内存区域具有访问的权限,各个进程之间的同步问题显得尤为重要。必须控制同一时刻只有一个进程对共享内存区域写入数据,否则会造成数据的混乱。同步控制问题可以由信号量来解决;

     对于每一个共享存储段,内核会为其维护一个shmid_ds类型的结构体,其定义在头文件<sys/shm.h>中,其定义如下:

[cpp] view plaincopy
  1. struct shmid_ds  
  2. {  
  3.     struct ipc_perm shm_perm   //Operation permission structure.  
  4.     size_t shm_segsz  //Size of segment in bytes.  
  5.     pid_t  shm_lpid   //Process ID of last shared memory operation.  
  6.     pid_t  shm_cpid   //Process ID of creator.  
  7.     shmatt_t shm_nattch //Number of current attaches.  
  8.     time_t shm_atime  //Time of last shmat                
  9.     time_t shm_dtime  //Time of last shmdt  
  10.     time_t shm_ctime  //Time of last change by shmctl  
  11.  }  

 

2、共享内存的相关操作

1)创建或打开共享内存

     要使用共享内存,首先要创建一个共享内存区域,创建共享内存的函数调用如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函数原型int shmget(key_t, int size ,int shmflg );
函数参数

Key:IPC_PRIVATE 或 ftok 的返回值

size:共享内存区大小

shmflag :同open函数的权限位,也可用8进制表示法

函数返回值

成功:共享内存段标识符

出错:-1

        shmget函数除了可以用于创建一个新的共享内存外,也可以用于打开一个已存在的共享内存。其中,参数key表示索要创建或打开的共享内存的键值。size表示共享内存区域的大小,只在创建一个新的共享内存时生效。参数shmflag 表示调用函数的操作类型,也可用于设置共享内存的访问权限,两者通过逻辑或表示.参数key 和 flag 决定了调用函数 shmget 的作用,相应的约定如下:

1)当 key  为 IPC_PRIVATE 时,创建一个新的共享内存,此时参数 flag 的取值无效;

2)当 key 不为 IPC_PRIVATE时,且flag 设置了IPC_CREAT 位,而没有设置 IPC_EXCL 位,则执行操作由key取值决定。如果key 为内核中每个已存在的共享内存的键值。则执行打开这个键的操作;反之,则执行创建共享内存的操作;

3)当 key 不为 IPC_PRIVATE时,且flag 设置了IPC_CREAT 位和 IPC_EXCL 位,则只执行创建共享内存的操作。参数key的取值应与内核中已存在的任何共享内存的键值都不相同,否则函数调用失败,返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开共享内存的函数就可以了(即将flag 设置为 IPC_CREAT,而不设置IPC_EXCL);

 

2)附加

    当一个共享内存创建或打开后,某个进程如果要使用该共享内存则必须将此内存区附加到它的地址空间,附加操作的系统调用函数如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函数原型void *shmat (int shmid, const void *shaddr, int shmflg);
函数参数

shmid :要映射的共享内存区标示符

shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)

shmflg:默认0:共享内存只读

函数返回值

成功:映射后的地址

出错:-1

        参数shmid 指定要引入的共享内存,参数 addr 和 flag 组合说明要引入的地址值,通常将 shmaddr 设置为NULL ,shmflag为0;

 

3)分离

      当进程对共享内存段的操作完成后,应调用 shmdt 函数,作用是将指定的共享内存段从当前进程空间中脱离出去,函数原型如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函数原型int shmdt(const void *shmaddr);
函数参数

shmaddr:共享内存映射后的地址

函数返回值

成功:0

出错:-1

此函数仅用于将共享内存区域与进程的地址空间分离,并不删除共享内存本身。参数addr是调用 shmat 函数时的返回值。

 

4)共享内存的控制

     由于共享内存这一特殊的资源类型,使它不同于普通的文件,因此,系统需要为其提供专有的操作函数,其函数原型如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函数原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数参数

shmid:要操作的共享内存标示符

cmd: IPC_STAT (获取对象属性)

              IPC_SET(设置对象属性)

              IPC_RMID(删除对象)

buf:指定IPC_STAT/ IPC_SET 时用以保存/设置属性

函数返回值

成功:0

出错:-1

 

下面是一个实例,两个进程间实现共享内存进行通信:

[cpp] view plaincopy
  1. #include <sys/types.h>  
  2. #include <sys/ipc.h>  
  3. #include <sys/shm.h>  
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <string.h>  
  7. #include <unistd.h>  
  8. #define BUFFER_SIZE 2048  
  9.   
  10. int main()  
  11. {  
  12.     pid_t pid;  
  13.     int shmid;  
  14.     char *shm_addr;  
  15.     char flag[] = "WROTE";  
  16.     char buffer[BUFFER_SIZE];  
  17.   
  18.     if((shmid = shmget(IPC_PRIVATE,BUFFER_SIZE,0666)) < 0)  
  19.     {  
  20.         perror("shmget");  
  21.         exit(-1);  
  22.     }  
  23.     else  
  24.     {  
  25.         printf("Create shared-memory:%d\n",shmid);  
  26.     }  
  27.   
  28.     system("ipcs -m");  
  29.     pid = fork();  
  30.     if(pid < 0)  
  31.     {  
  32.         perror("fork error");  
  33.         exit(-1);  
  34.     }  
  35.     else if(pid == 0)  
  36.     {  
  37.         shm_addr = shmat(shmid,0,0);  
  38.         if(shm_addr == (void *)-1)  
  39.         {  
  40.             perror("Child:shmat");  
  41.             exit(-1);  
  42.         }  
  43.         else  
  44.         {  
  45.             printf("Child:Attach shared-memory:%p \n",shm_addr);  
  46.         }  
  47.   
  48.         system("ipcs -m");  
  49.   
  50.         while(strncmp(shm_addr,flag,strlen(flag)))  
  51.         {  
  52.             printf("Child:wait for enable data...\n");  
  53.             sleep(5);  
  54.         }  
  55.   
  56.         strcpy(buffer,shm_addr + strlen(flag));  
  57.         printf("Child:shared-memory:%s\n",buffer);  
  58.   
  59.         if((shmdt(shm_addr)) < 0)  
  60.         {  
  61.             perror("shmdt");  
  62.             exit(-1);  
  63.         }  
  64.         else  
  65.         {  
  66.             printf("Child: Deattach shared-memory\n");  
  67.         }  
  68.         system("ipcs -m");  
  69.   
  70.         if(shmctl(shmid,IPC_RMID,NULL) == -1)  
  71.         {  
  72.             perror("Child:shmctl(IPC_RMID)");  
  73.             exit(-1);  
  74.         }  
  75.         else  
  76.         {  
  77.             printf("Delete shmared-memory\n");  
  78.         }  
  79.   
  80.         system("ipcs -m");  
  81.     }  
  82.     else  
  83.     {  
  84.         if((shm_addr = shmat(shmid,0,0)) == (void *)-1)  
  85.         {  
  86.             perror("parent:shmat");  
  87.             exit(-1);  
  88.         }  
  89.         else  
  90.         {  
  91.             printf("Parent:Attach shmared-memory:%p\n",shm_addr);  
  92.         }  
  93.   
  94.         sleep(1);  
  95.         printf("\nInput some string:\n");  
  96.         fgets(buffer,BUFFER_SIZE,stdin);  
  97.         strncpy(shm_addr + strlen(flag),buffer,strlen(buffer));  
  98.         strncpy(shm_addr,flag,strlen(flag));  
  99.   
  100.         if((shmdt(shm_addr)) < 0)  
  101.         {  
  102.             perror("Parent:shmdt");  
  103.             exit(-1);  
  104.         }  
  105.         else  
  106.         {  
  107.             printf("Parent : Deattach shared-memory\n");  
  108.         }  
  109.   
  110.         system("ipcs -m");  
  111.         waitpid(pid,NULL,0);  
  112.         printf("Finsihed\n");  
  113.     }  
  114.       
  115.     return 0;  
  116. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/shm$ ./shm   
  2. Create shared-memory:196613  
  3.   
  4. ------ Shared Memory Segments --------  
  5. key        shmid      owner      perms      bytes      nattch     status        
  6. 0x00000000 0          fs         700        76800      2          dest           
  7. 0x00000000 32769      fs         700        1843200    2          dest           
  8. 0x00000000 65538      fs         700        20196      2          dest           
  9. 0x00000000 98307      fs         700        17028      2          dest           
  10. 0x00000000 131076     fs         700        19800      2          dest           
  11. 0x00000000 196613     fs         666        2048       0                         
  12.   
  13. Parent:Attach shmared-memory:0xb773d000  
  14. Child:Attach shared-memory:0xb773d000   
  15.   
  16. ------ Shared Memory Segments --------  
  17. key        shmid      owner      perms      bytes      nattch     status        
  18. 0x00000000 0          fs         700        76800      2          dest           
  19. 0x00000000 32769      fs         700        1843200    2          dest           
  20. 0x00000000 65538      fs         700        20196      2          dest           
  21. 0x00000000 98307      fs         700        17028      2          dest           
  22. 0x00000000 131076     fs         700        19800      2          dest           
  23. 0x00000000 196613     fs         666        2048       2                         
  24.   
  25. Child:wait for enable data...  
  26.   
  27. Input some string:  
  28. xiaoqiang  
  29. Parent : Deattach shared-memory  
  30.   
  31. ------ Shared Memory Segments --------  
  32. key        shmid      owner      perms      bytes      nattch     status        
  33. 0x00000000 0          fs         700        76800      2          dest           
  34. 0x00000000 32769      fs         700        1843200    2          dest           
  35. 0x00000000 65538      fs         700        20196      2          dest           
  36. 0x00000000 98307      fs         700        17028      2          dest           
  37. 0x00000000 131076     fs         700        19800      2          dest           
  38. 0x00000000 196613     fs         666        2048       1                         
  39.   
  40. Child:shared-memory:xiaoqiang  
  41.   
  42. Child: Deattach shared-memory  
  43.   
  44. ------ Shared Memory Segments --------  
  45. key        shmid      owner      perms      bytes      nattch     status        
  46. 0x00000000 0          fs         700        76800      2          dest           
  47. 0x00000000 32769      fs         700        1843200    2          dest           
  48. 0x00000000 65538      fs         700        20196      2          dest           
  49. 0x00000000 98307      fs         700        17028      2          dest           
  50. 0x00000000 131076     fs         700        19800      2          dest           
  51. 0x00000000 196613     fs         666        2048       0                         
  52.   
  53. Delete shmared-memory  
  54.   
  55. ------ Shared Memory Segments --------  
  56. key        shmid      owner      perms      bytes      nattch     status        
  57. 0x00000000 0          fs         700        76800      2          dest           
  58. 0x00000000 32769      fs         700        1843200    2          dest           
  59. 0x00000000 65538      fs         700        20196      2          dest           
  60. 0x00000000 98307      fs         700        17028      2          dest           
  61. 0x00000000 131076     fs         700        19800      2          dest           
  62.   
  63. Finsihed  
  64. fs@ubuntu:~/qiang/shm$  

通过结果,不断的调用 system ,可以看到共享内存区的变化;

 

二、信号量

       信号灯(semaphore),也叫信号量,它是不同进程间或一个给定进程内部不同线程间同步的机制。

信号灯种类:1)posix 有名信号灯 2)posix 基于内存的信号灯(无名信号灯) 3)System V 信号灯 (IPC对象);

信号灯:

1)二值信号灯:值为 0 或 1。与互斥锁类似,资源可用时值为1,不可用时值为 0;

2)计数信号灯:值在 0 到 n 之间。用来统计资源,其值代表可用资源数;

等待操作时等待信号灯的值变为大于0,然后将其减一;而释放操作则相反,用来唤醒等待资源的进程或者线程;

 

事实上,在信号量的实际应用中,是不能单独定义一个信号量的,而只能定义一个信号量集,其中包含一组信号量,同一信号量集中的信号量使用同一个引用ID,这样的设置是为了多个资源和同步操作的需要。每个信号量集都有一个与之对应结构,一种记录了信号量集的各种信息,该结构的定义如下:

[cpp] view plaincopy
  1. struct semid_ds  
  2. {             
  3.     struct ipc_perm  sem_perm  //Operation permission structure.  
  4.     unsigned short   sem_nsems //Number of semaphores in set.  
  5.     time_t           sem_otime //Last semop              
  6.     time_t           sem_ctime //Last time changed by semctl  
  7. }  

sem结构记录一个信号量的信息,其定义如下:

[cpp] view plaincopy
  1. struct sem  
  2. {            
  3.     unsigned short  semval   //Semaphore value.  
  4.     pid_t           sempid   //Process ID of last operation.  
  5.     unsigned short  semncnt  //Number of processes waiting for semval to become greater than current value.  
  6.     unsigned short  semzcnt  //Number of processes waiting for semval to become 0.  
  7. }  

 

下面是信号量操作有关的函数调用:

函数说明:

在Linux 系统中,使用信号量通常分为以下几个步骤:

1)创建信号量或获得系统已存在的信号量,此时需要调用 semget() 函数。不同进程通过使用同一个信号键值来获得同一个信号量;

2)初始化信号量,此时使用 senctl() 函数 SETVAL 操作。当使用二维信号量时,通常将信号量初始化为1;

3)进行信号量的PV操作,此时调用 semop() 函数。这一步是实现进程之间的同步和互斥的核心工作部分;

4)如果不需要信号量,则从系统中删除它,此时使用semctl() 函数的IPC_RMID 操作。此时需要注意,在程序中不应该出现对已经被删除的信号量的操作;


下面是具体说明:

1、创建或打开信号量集

使用函数 semget 可以创建或者获得一个信号量集ID,函数原型如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函数原型int semget(key_t key, int nsems, int semflg );
函数参数

key :和信号灯集关联的key 值

nsems:信号灯集中包含的信号灯数目

semflg:信号灯集的访问权限,通常为IPC_CREAT|0666

函数返回值

成功:信号灯集ID

出错:-1

此函数可以用于创建或打开一个信号量集。其中,参数key 表示要创建或打开的信号量集对于的键值。参数 nsems 表示创建的信号量集中包含的信号量的个数,此参数只在创建一个新的信号量集时有效。参数flag表示调用函数的操作类型,也可以用于设置信号量集的访问权限,两者通过逻辑或表示。调用函数semget 的作用由参数key和flag 决定。

     另外,当semget 成功创建一个新的信号量集时,它相应的semid_ds结构被初始化。ipc_perm 结构中成员被设置成相应的值 ,sem_nsems设置为函数参数nsems的值,sem_otime被设置为0,sem_ctime 设置为系统当前时间。

 

2、对信号量集的操作

函数semop 用以操作一个信号量集,函数原型如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函数原型int semop(int semid,struct sembuf  *opsptr,size_t nops);
函数参数

semid:信号灯集ID

struct sembuf 结构体每一个元素表示一个操作;

nops:要操作的信号灯的个数

函数返回值

成功:0

出错:-1


此函数是一个原子操作,一旦执行就将执行数组中所有的操作;

结构体sembuf 用来说明所要执行的操作,其定义如下:

[cpp] view plaincopy
  1. struct sembuf  
  2. {  
  3.     unsigned short sem_num; //要操作的信号灯的编号  
  4.     short sem_op;   //  0: 等待,知道信号灯的值变为0  
  5.                         //  1: 释放资源,V操作   
  6.                         // -1: 分配资源,P操作  
  7.     short sem_flg; //0,IPC_NOWAIT,SEM_UNDO  
  8. }  


3、信号量集的控制

和共享内存的控制一样,信号量集也有自己的专属控制函数 semctl ,函数原型如下:

所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函数原型int semctl(int semid, int semnum, int cmd, union semun arg);
函数参数

semid:信号灯集ID

semnum:要修改的信号灯编号

cmd :GETVAL:获取信号灯的值

             SETVAL:设置信号灯的值

             IPC_RMID:从系统中删除信号灯集合

函数返回值

成功:0

出错:-1

参数cmd 定义函数所要进行的操作,其取值及表达的意义与参数arg 的设置有关,最后一个参数arg 是一个联合体(union),其定义如下:

[cpp] view plaincopy
  1. union semun {  
  2.        int              val;    /* Value for SETVAL */  
  3.        struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */  
  4.        unsigned short  *array;  /* Array for GETALL, SETALL */  
  5.        struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */  
  6. };  

下面是个应用实例:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/ipc.h>  
  7. #include <sys/sem.h>  
  8. #define DELAY_TIME 3  
  9.   
  10. union semun   
  11. {  
  12.     int              val;    /* Value for SETVAL */  
  13.     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */  
  14.     unsigned short  *array;  /* Array for GETALL, SETALL */  
  15.     struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */  
  16. };  
  17.   
  18. int init_sem(int sem_id,int init_value);  
  19. int del_sem(int sem_id);  
  20. int sem_p(int sem_id);  
  21. int sem_v(int sem_id);  
  22.   
  23. int init_sem(int sem_id,int init_value)  
  24. {  
  25.     union semun sem_union;  
  26.     sem_union.val = init_value;  
  27.     if(semctl(sem_id, 0, SETVAL, sem_union) == -1)  
  28.     {  
  29.         perror("Initialize semaphore");  
  30.         exit(-1);  
  31.     }  
  32.   
  33.     return 0;  
  34. }  
  35.   
  36. int del_sem(int sem_id)  
  37. {  
  38.     union semun sem_union;  
  39.     if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)  
  40.     {  
  41.         perror("Delete semaphore");  
  42.         return -1;  
  43.     }  
  44. }  
  45.   
  46. int sem_p(int sem_id)  
  47. {  
  48.     struct sembuf sem_b;  
  49.     sem_b.sem_num = 0;  
  50.     sem_b.sem_op = -1;  
  51.     sem_b.sem_flg = SEM_UNDO;  
  52.   
  53.     if(semop(sem_id,&sem_b,1) == -1)  
  54.     {  
  55.         perror("P operation");  
  56.         return -1;  
  57.     }  
  58.   
  59.     return 0;  
  60. }  
  61.   
  62. int sem_v(int sem_id)  
  63. {  
  64.     struct sembuf sem_b;  
  65.     sem_b.sem_num = 0;  
  66.     sem_b.sem_op = 1;  
  67.     sem_b.sem_flg = SEM_UNDO;  
  68.   
  69.     if(semop(sem_id,&sem_b,1) == -1)  
  70.     {  
  71.         perror("V operation");  
  72.         return -1;  
  73.     }  
  74.   
  75.     return 0;  
  76. }  
  77.   
  78. int main()  
  79. {  
  80.     pid_t pid;  
  81.     int sem_id;  
  82.   
  83.     sem_id = semget(ftok(".",'a'), 1, 0666|IPC_CREAT);  
  84.     init_sem(sem_id,0);  
  85.   
  86.     pid = fork();  
  87.     if(pid < 0)  
  88.     {  
  89.         perror("fork fails");  
  90.         exit(-1);  
  91.     }  
  92.     else if(pid == 0)  
  93.     {  
  94.         printf("Child process will wait for some seconds...\n");  
  95.         sleep(DELAY_TIME);  
  96.         printf("The returned value is %d in the child process(PID = %d)\n",  
  97.                 pid,getpid());  
  98.         sem_v(sem_id);  
  99.     }  
  100.     else  
  101.     {  
  102.         sem_p(sem_id);  
  103.         printf("The returned value is %d in the father process(PID = %d)\n",  
  104.                 pid,getpid());  
  105.         sem_v(sem_id);  
  106.         del_sem(sem_id);  
  107.     }  
  108.   
  109.     return 0;  
  110. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/sem$ ./sem   
  2. Child process will wait for some seconds...  
  3. The returned value is 0 in the child process(PID = 2882)  
  4. The returned value is 2882 in the father process(PID = 2880)  
  5. fs@ubuntu:~/qiang/sem$

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.balignl 16,0xdeadbeef浅析

http://zqwt.012.blog.163.com/blog/static/12044684201031102956976/ 最近在分析u-boot的源代码&#xff0c;看到这一行&#xff1a; .balignl 16, 0xdeadbeef不知道为什么要这样写&#xff0c;0xdeadbeef&#xff0c;明显是个单词组&#xff0c;写在这里有何意义呢&am…

使用maven导入任意jar包

http://mvnrepository.com/ 我这里&#xff0c;因为是spark1.5.2版本。 保存&#xff0c;maven会自动下载jar包到本地仓库。 转载于:https://www.cnblogs.com/lchzls/p/6281764.html

Linux下静态IP地址的设置及TFTP服务的搭建

TFTP&#xff08;Trivial File Transfer Protocol,简单文件传输协议&#xff09;是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。TFTP承载在UDP上&#xff0c;提供不可靠的数据流传输服务&#xff0c;…

bzoj 3924 幻想乡战略游戏

题目大意&#xff1a; 有边权点权的树&#xff0c;动态修改点权 每次修改后求带权重心x (\(minimize\) \(S\sum_i val[i]*dist[x][i]\)) 分析&#xff1a; 从暴力找突破口&#xff1a; 对于边x,y&#xff0c;设长度为len&#xff0c;切断后x半边树权值和为\(w_1\)&#xff0c;y…

Linux 系统应用编程——网络编程(基础篇)

一、网络体系结构 1、OSI模型和TCP/IP 模型 网络体系结构指的是网络的分层结构以及每层使用的协议的集合。其中最著名的就是OSI协议参考模型&#xff0c;他是基于国际标准化组织&#xff08;OSI&#xff09;的建议发展起来的。它分为7个层次&#xff1a;应用层、表示层、会话层…

C++中函数的默认参数

使用方法&#xff1a; &#xff08;1&#xff09;在函数声明或定义时&#xff0c;直接对参数赋值&#xff0c;该参数就是默认参数。&#xff08;2&#xff09;在函数调用时&#xff0c;省略部分或全部参数&#xff0c;这时就会使用默认参数进行代替。注意事项&#xff1a; &…

Linux 系统应用编程——网络编程(socket编程)

二、网络编程基础 1、套接字概述 套接字就是网络编程的ID。网络通信&#xff0c;归根到底还是进程间的通信&#xff08;不同计算机上的进程间的通信&#xff09;。在网络中&#xff0c;每一个节点&#xff08;计算机或路由器&#xff09;都有一个网络地址&#xff0c;也就是IP地…

php curl拉取远程图片

<?php $url "图片绝对地址/thumbnail.jpg"; $filename curl.jpg; getImg($url, $filename); /**通过curl方式获取制定的图片到本地* 完整的图片地址* 要存储的文件名*/ function getImg($url "", $filename "") {if(is_dir(basename($fi…

利用indent格式化源文件的脚本

脚本一&#xff1a;格式化指定目录下的源文件(*.h, *.cpp...) #!/bin/sh# 格式化某目录下所有*.h, *.c, *.cpp, *.hh文件, 并将文件换行符转换成Linux下的格式if [ $# -lt 1 ]; thenecho "Usage: $0 <dir>"exit 1elsedir$1fi# format a source file(*.c, *.h,…

Struts入门(三)深入Struts用法讲解

访问Servlet APIAction搜索顺序动态方法调用指定多个配置文件默认ActionStruts 后缀接收参数处理结果类型1.访问Servlet API 首先我们了解什么是Servlet API httpRequest、httpResponse、servletContext  3个api对应jsp面向对象&#xff1a;request、response、application …

Linux ALSA声卡驱动之四:Control设备的创建

声明&#xff1a;本博内容均由http://blog.csdn.net/droidphone原创&#xff0c;转载请注明出处&#xff0c;谢谢&#xff01; Control接口 Control接口主要让用户空间的应用程序&#xff08;alsa-lib&#xff09;可以访问和控制音频codec芯片中的多路开关&#xff0c;滑动控件…

jQuery 入门教程(5): 显示/隐藏内容

2019独角兽企业重金招聘Python工程师标准>>> jQuery的hide()和show()可以用来显示和隐藏内容。比如下面的例子&#xff1a;jQuery的hide()和show() 可以用来显示和隐藏内容。比如下面的例子&#xff1a; [html] view plain copy print ? <!doctype html> …

键盘键值表

键盘键值表 值 描述 0x1 鼠标左键 0x2 鼠标右键 0x3 CANCEL 键 0x4 鼠标中键 0x8 BACKSPACE 键 0x9 TAB 键 0xC CLEAR 键 0xD ENTER 键 0x10 SHIFT 键 0x11 CTRL 键 0x12 MENU 键 0x13 PAUSE 键 0x14 CAPS LOCK 键 0x1B ESC 键 0x20 SPACEBAR 键 0x21 PAGE UP 键 0x22 PAGE DOW…

Spring自动扫描配置及使用方法

2019独角兽企业重金招聘Python工程师标准>>> 首先&#xff0c;检查一下你lib下有没有 common-annotations.jar 这个jar包 没有的话要导入工程。 下一步配置spring的配置文件applicationContex.xml&#xff0c;加入命名空间 红色为需要添加的内容 <beans xmlns…

Linux下ln命令使用

n是linux中又一个非常重要命令&#xff0c;它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录&#xff0c;用到相同的文件时&#xff0c;我们不需要在每一个需要的目录下都放一个必须相同的文件&#xff0c;我们只要在某个固定的目录&#xff0c…

DPM 2012 SP1---安装并部署DPM 2012 SP1服务器

实验拓扑图&#xff1a;一、前提条件&#xff1a;需要在DPM2012 SP1服务器上完成以下操作&#xff1a;1.DPM服务器加入域&#xff08;使用域管理员登陆DPM2012 SP1服务器&#xff09;2.准备存储磁盘&#xff08;新添加一块硬盘&#xff09;3.关闭防火墙服务&#xff08;DPM服务…

Linux下test命令使用

test命令格式&#xff1a; [cpp] view plain copy test condition 通常&#xff0c;在if-then-else语句中&#xff0c;用[]代替&#xff0c;即[ condition ]。注意&#xff1a;方括号两边都要用空格。 1、数值比较 比 较 描 述 ---------------------------------------…

用Mysql网页式管理工具安全地访问数据库的方法

2019独角兽企业重金招聘Python工程师标准>>> 用Mysql网页式管理工具安全地访问数据库的方法 在使用通达OA系统时很多用户需要借助Mysql网页式管理工具进入后台数据库去查看数据&#xff0c;进行一些相应的操作。但是大多数时候用户安装完该工具后都是直接进入后台数…

Linux 下的DMA浅析

DMA是一种无需CPU的参与就可以让外设和系统内存之间进行双向数据传输的硬件机制。使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来&#xff0c;从而大大提高系统的吞吐率。DMA经常与硬件体系结构特别是外设的总线技术密切相关。 一、DMA控制器硬件结构 DMA允许外围设备…