Unix环境进程间通信(一)[转]

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);
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值