Unix环境进程间通信(一)

原创 2006年05月26日 08:46:00

1.1        信号量

SystemV的信号量使用方法

1.1.1   数据结构

include <sys/types.h>

include <sys/ipc.h>

include <sys/sem.h>

1.

struct semid_ds{     /* 通道结构 */

struct ipc_perm  sem_perm;

       struct sem  *sem_base;      /* 指向信号量数组,系统内部使用,用户不能直接访问*/

       ushort      sem_nsems;     /* 信号量个数 */

time_t      sem_otime;     /* 最后semop时间 */

time_t      sem_ctime;     /* 最后修改时间 */

}

2.

struct sem {               /* 信号量的成分 */

ushort semval;                   /*非负值*/

ushort sempid;                   /*最后semoppid*/

ushort semncnt;                  /*等待semval增加的进程数*/

ushort semzcnt;                  /*等待semval0的进程数*/        

}

3.

semctl函数需要的联合体:

 

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)

       /* union semun is defined by including <sys/sem.h> */

#else

       /* according to X/OPEN we have to define it ourselves */

       union semun {

             int val;                    /* 用于 SETVAL 命令*/

             struct semid_ds *buf;     /* 用于 IPC_STAT, IPC_SET */

             unsigned short *array;    /* 用于 GETALL, SETALL 命令,存放所获得的或要设置的信号量集合中所有信号量的值*/

                    /* Linux specific part: */

             struct seminfo *__buf;    /* 用于 IPC_INFO */

       };

#endif

4.

struct sembuf {      /* 操作数数组成员结构,用于semop函数*/

              ushort sem_num;        /* 信号量编号 */

              short   sem_op;        /* 操作数 */

              short   sem_flag;      /* 操作标志 */

}

 

1.1.2   API

4.1 #include <semaphore.h>

信号量集合初始化,建立信号量集合标识及相连的数据结构

int semget(key_t key, int nsems, int semflg);

返回:成功返回信号量集合id,错误返回-1并设置errno

第一个参数keyIPC关键字。它有特殊值IPC_PRIVATE表示总是创建一个私有信号量集合。

第二个参数指明信号量集合中信号量的个数。nsems必须大于0小于系统在头文件<sys/sem.h>中定义的信号量最大值宏SEMMSL。当信号量集合已经存在时,此参数必须合法,即为大于0小于该信号量集合的个数,或简单设为0。信号量的编号,第一个是0最后一个是nsems-1

key值不等于IPC_PRIVATE时,semget动作取决于最后一个参数semflag标志:

IPC_CREAT 单独设置此标志,当系统中不存在相同key时,创建一个新的信号量集合,否则返回已存在的信号量集合标识。

IPC_EXCL 单独设置不起作用。与 IPC_CREAT同时设置时,当系统中存在相同key时,错误返回。保证不会打开一个已存在的信号量集合。

此函数只能创建一个信号量集合,集合中的每个信号量没有初始化,这要用4.2函数。

可重用包装函数:

//打开或创建信号量集合,权限参数必须是型如"066"的字符串

int open_semaphore_set(key_t key_val, int num_sems, char* mode_str) {

       int sid;
       int mode;

       if (mode_str != NULL) {
              sscanf(mode_str, "%o", &mode);
       } else {
              mode = 0660;
       }

       if(!num_sems) {
              return (-1);
       }

       if ((sid = semget(key_val, num_sems, mode | IPC_CREAT)) == -1)
              return (-1);
       else
              return sid;
}

 

 

4.2 #include <sys/types.h>

   #include <sys/ipc.h>

   #include <sys/sem.h>

信号量控制函数

int semctl(int semid, int semnum, int cmd, ...);

返回:失败-1, 成功对于命令GETVAL,GETPID,GETNCNT,GETZCNT返回相应值,其他命令返回0

1个参数semid必须是一个合法的信号量集合标识。

2个参数semnum选择集合中一个特定的信号量,

3个参数给出操作命令,第4 参数给出相应命令所需的参数,此参数可选:

CMD

说明

参数

SETVAL

设置semid信号量集合中的semnum信号量semvalarg.val。同时更新semid_ds中的sem_ctime成员。

arg.val

GETALL

返回信号量集合中所有信号量的值

arg.array

SETALL

设置信号量集合中所有信号量的值

arg.array

IPC_STAT

放置与信号量集合相连的semid_ds结构当前值于arg.buf指定的缓冲区

arg.buf

IPC_SET

arg.buf指定的结构值代替与信号量集合相连的semid_ds结构值

arg.buf

GETVAL

系统调用,返回信号量集合重的semnum信号量的值semval

 

GETPID

返回最后一个操作该信号量集合的进程ID

 

GETNCNT

返回semncnt的值

 

GETZCNT

返回semzcnt的值

 

IPC_RMID

删除制定的信号量集合

 

 

可重用包装函数:

//获得指定的信号量的值

int get_sem_val(int sid, int semnum) {

       return semctl(sid, semnum, GETVAL, 0);

}

 

//给指定的信号量赋初值

void init_a_semaphore(int sid, int semnum, int initval) {

       union semun semopts;
       semopts.val= initval;
       semctl(sid, semnum, SETVAL, semopts);
}

 

//给指定的信号量集合赋初值

void init_all_semaphore(int sid, int val_array[]) {

       union semun semopts;
       semopts.array= (short unsigned int *)val_array;
       semctl(sid, 0, SETALL, semopts.array);
}

 

//改变信号量集合的访问权限,权限参数必须是型如"066"的字符串

int changemode(int sid, char *mode) {

       int rc;
       union semun semopts;
       struct semid_ds mysemds;

       semopts.buf= &mysemds;

       if ((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1)
              return -1;
       sscanf(mode, "%o", &semopts.buf->sem_perm.mode);
       return semctl(sid, 0, IPC_SET, semopts);
}

 

//删除指定的信号量集合

int rm_semaphore(int sid) {

       return semctl(sid, 0, IPC_RMID, 0);

}

 

 

4.3 #include <sys/types.h>

   #include <sys/ipc.h>

   #include <sys/sem.h>

信号量操作函数

int semop(int semid, struct sembuf *sops, unsigned nsops);

返回:失败-1,

1个参数指明信号量集合标识,

2个参数sops指向一个类型为sembuf的结构数组

3个参数nsops给出该数组元素个数。取值范围0SEMOP

此函数对制定的信号量集合进行操作,操作既可以针对单个信号量,也可一对整个信号量集合进行操作。操作的信号量对象和操作动作由sops指向的结构体指出:

struct sembuf {      /* 操作数数组成员结构,用于semop函数*/

              ushort sem_num;        /* 信号量编号 */

              short   sem_op;        /* 操作数 */

              short   sem_flag;      /* 操作标志 */

}

sem_num指出信号量编号。

sem_op给出操作的类型:

1.< 0 减少一个信号量的值,减少为abs(sem_op)。这相当与请求信号量所控制的资源。当为-1时, 相当于P操作。当信号量当前值>=abs(sem_op),操作成功,否则当没有设置标志IPC_NOWAIT时,进程将等待直到信号量的当前值>=abs(sem_op)

2.>0 增加一个信号量的值,增加为sem_op。相当于返回信号量控制的资源。当为1时,相当于V操作。

3.=0 等待信号量变为0。对应于等待信号量控制的所有资源均可以用。如果信号量已经为0,调用立即返回;否则没有设置IPC_NOWAIT时,调用被阻塞。

sem_flag对操作进行适当控制:

       1. IPC_NOWAIT 如果设置,当指定的操作不能完成时,进程将不等待立即返回-1,且设置errnoEAGAIN

       2.SEM_UNDO 如果设置,当进程退出时,进行信号量解除(UNDO)操作。注意,设置此值后,对信号量进行相反操作的动作也要设置(UNDO)操作。

可重用包装函数:

int semaphore_P(int sem_id) {

       struct sembuf sb;

       sb.sem_num = 0;
       sb.sem_op = -1;

       sb.sem_flg = SEM_UNDO;

       if (semop(sem_id, &sb, 1) == -1) {

              fprintf(stderr, "semaphore_p failed/n");

              return (0);
       }

       return 1;

}

 

int semaphore_V(int sem_id) {

       struct sembuf sb;

       sb.sem_num = 0;
       sb.sem_op = 1;

       sb.sem_flg = SEM_UNDO;

       if (semop(sem_id, &sb, 1) == -1) {

              fprintf(stderr, "semaphore_v failed/n");
              return (0);
       }

       return 1;
}

1.1.3   Example

int main(int argc, char** argv) {

       int sem_id;

       int i, creat = 0;
       int pause_time;
       char* cp;

       //以进程id作为随机数种子
       srand((unsigned int)getpid());

      
       //打开/创建仅一个信号量的初值为1

       sem_id = open_semaphore_set((key_t)1234, 1, “0660”);

       if (argc > 1 && strcmp(argv[1], "1")) {
              init_a_semaphore(sem_id, 0, 1);
              creat = 1;
              sleep(2);
       }

       for(i = 0; i < argc; i++) {
              cp = argv[i];
              if (!semaphore_p(sem_id))
                     exit(EXIT_FAILURE);
              printf("process %d", getpid());
              fflush(stdout);
              while(*cp) {
                     printf("%c", *cp);
                     fflush(stdout);
                     pause_time = rand() %3;
                     sleep(pause_time);
                     cp++;
              }
       }
       printf("/n%d -finished/n", getpid());

       if (creat == 1) {
              sleep(10);
              rm_semaphore(sem_id);
       }
       exit(EXIT_SUCCESS);
}

相关文章推荐

《UNIX环境高级编程》第17章 高级进程间通信

17.1 引言前面两章讨论了UNIX系统提供的各种IPC,其中包括管道和套接字。 本章介绍一个高级IPC-UNIX域套接字,并说明它的使用方法。 这种形式的IPC可以在同一计算机上运行的两个进程之...

UNIX环境高级编程(第15章 进程间通信)

进程间通信的方式包括管道、消息队列、信号量和共享存储。通过这些机制,同一台计算机上运行的进程可以互相通信。...

《UNIX环境高级编程》第15章 进程间通信

15.1 引言第8章说明的进程控制原语,并且观察了如何调用多个进程。但是这些进程间交互信息的唯一途径就是传送打开的文件,可以经由fork或exec来传送,也可以通过文件系统来传送。 本章讨论经典IP...

UNIX环境高级编程-第15章- 进程间通信 - 二

15.6 XSI IPC 有三种IPC我们称作XSI IPC,即消息队列,信号量和共享存储器,他们之间有很多相似之处。 15.6.1 标识符和键 每个内核中的IPC结构(消息队列,信号量或共享存...

Unix环境进程间通信

  新一篇: Unix环境进程间通信(二)1.1        信号量SystemV的信号量使用方法1.1.1   数据结构#include #include #include 1.struct se...
  • haisi05
  • haisi05
  • 2011年06月22日 00:05
  • 175

《UNIX环境高级编程》十五进程间通信读书笔记

1、管道 管道的局限性: (1)历史上,它们是半双工的(即数据只能在一个方向上流动)。 (2)管道只能在具有公共祖先的两个进程之间使用。 半双工管道是最常用的IPC形式。每当在管道中键入一个命...

UNIX环境高级编程笔记——高级IO,进程间通信,套接字,高级进程间通信

14 高级IO非阻塞IO:这里我要抽时间看看AIO的使用。记录锁:运行一个进程修改一个文件的一部分的时候,禁止其进程修改统一区域。流在用户经常和设备驱动程序间提供了一个全双工通道。readv和writ...

《unix高级环境编程》进程间通信——信号量

这里所介绍的信号量是一种

《unix高级环境编程》进程间通信——共享内存

共享内存是允许两个或多个进程共享同一块内存区域

《unix高级环境编程》进程间通信——消息队列

消息队列是消息的链接表,保存在内核,通过消息队列的引用标识符来访问消息,消息队列对每个消息指定了特定的消息类型,接收消息的进程可以请求接收下一条消息,也可以请求接收下一条特定类型的消息。...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Unix环境进程间通信(一)
举报原因:
原因补充:

(最多只允许输入30个字)