消息队列和信号(进程间通信)

 

消息队列

1、消息队列是什么?

消息队列是在内核空间内部的一个链表。通过Linux内核在各个进程间传递消息。

要获取对应的消息队列,每一个消息队列都有一个唯一的表示符来区分。

 

 

 

2、怎么样使用消息队列进行进程间通讯?

 

1)先产生一个标示符

SYNOPSIS

       #include <sys/types.h>

       #include <sys/ipc.h>

 

       key_t ftok(const char *pathname, int proj_id);//两个随机值产生一个唯一的随机的key值

pathname  (which  must  refer  to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件 /home/gec

proj_id (which must be nonzero) to generate a  key_t  必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。 随机非0数字

 

返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。

 

 

2)通过这个key去创建消息队列

 

SYNOPSIS

       #include <sys/types.h>

       #include <sys/ipc.h>

       #include <sys/msg.h>

 

       int msgget(key_t key, int msgflg);

第一个参数,第一个参数就是我们第一步通过ftok产生的key值,用来标识唯一的消息队列。

第二个参数,分为有两种

第一种情况:创建  IPC_CREAT | 权限位  例如: IPC_CREAT | 0664 (一般用这个,0777也可以,注意可以有空格)

第二种情况:打开 0

 

返回值:

成功返回的是消息队列的ID,如果失败了,返回-1,并且错误码可以通过perror打印出来。

 

 

3)通过收发的函数,实现收发,实现进程间通讯。

 

SYNOPSIS

       #include <sys/types.h>

       #include <sys/ipc.h>

       #include <sys/msg.h>

 

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。

msgp:这个是指针,指向我们所需要发送的消息,消息我们可以自定义。

           struct msgbuf {

                long mtype;       /* message type, must be > 0  消息的类型,这个参数是自己定义 */

               char mtext[1];    /* message data  这个是我们需要发送的消息的具体数据 */

            };

msgsz: 消息内容的长度,是对应mtext的实际大小。//一般用数组大小

msgflg:

1)IPC_NOWAIT 不阻塞的模式。

2)0 阻塞模式发送(一般都是用这个)

 

返回值:成功返回0,失败的话返回-1,并且可以把对应的错误码打印出来。

 

 

这个函数的作用,就是在指定的消息队列里面,把消息摘下来,拷贝到msgp所指定buffer里面。

       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。

msgp:这个是指针,指向我们所需要接收的消息的buffer,

msgsz:指的是消息的最大的长度,消息内容的长度,是对应mtext的实际大小。

msgtyp:指的是消息的类型,0代表读取的是队列内第一个消息。如果是大于零的时候,队列里面第一个msgtyp类型的消息就会被读回来。(一般用888)

msgflg:

1)IPC_NOWAIT 不阻塞的模式。

2)0 阻塞模式发送(一般都是用这个)

 

 

返回值:成功的话,返回的是,对应mtext的实际大小(实际消息的长度), 失败的话返回-1,并且可以把对应的错误码打印出来。

 

 

4)用于控制这个消息队列,比如说删除队列。

 

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msqid:这个参数就是消息队列的ID

cmd: 一些命令,常用的有删除队列的命令:IPC_RMID

msqid_ds *buf ,关于队列的一些信息的描述。

Msg-sever.c

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#include<stdlib.h>

struct msgbuf

{

long mtype;

char mtext[256];

};

int main()

{

int msgid;

key_t key;

struct msgbuf msg,msgr;

key=ftok("/home/gec/caobo",111);

if(key<0)

{

perror("fork fail");

exit(-1);

}

printf("key=%d\n",key);

msgid=msgget(key,IPC_CREAT | 0777);

if(msgid<0)

{

perror("msgget fail");

exit(-1);

}

msg.mtype=888;

// scanf("%s",msg.mtext);

while(1)

{

msgrcv(msgid,&msgr,256,888,0);//通过没有赋值消息类型的结构体来读,赋值了的来写,下面程序也是;

printf("曹博:%s\n",msgr.mtext);

// printf("sever recv msg type:%ld\n",msgr.mtype);

printf("陈素:\n");

scanf("%s",msg.mtext);

msgsnd(msgid,&msg,256,0);//注意msgr和msg为同种结构体类型的两个结构体,用来辅助读写操作

sleep(3);

}

return 0;

}

Msg-client.c

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#include<stdlib.h>

struct msgbuf

{

long mtype;

char mtext[256];

};

int main()

{

int msgid;

key_t key;

struct msgbuf msg,msgr;

key=ftok("/home/gec/caobo",111);

if(key<0)

{

perror("fork fail");

exit(-1);

}

printf("key=%d\n",key);

msgid=msgget(key,IPC_CREAT | 0777);

if(msgid<0)

{

perror("msgget fail");

exit(-1);

}

msg.mtype=888;

//scanf("%s",msg.mtext);

while(1)

{

printf("曹博:\n");

scanf("%s",msg.mtext);

msgsnd(msgid,&msg,256,0);

msgrcv(msgid,&msgr,256,888,0);

printf("陈素:%s\n",msgr.mtext);

// printf("client recv msg type:%ld\n",msgr.mtype);

sleep(3);

}

return 0;

}

 

 

 

3、信号量

 

1)为什么需要使用信号量?

因为我们的系统资源是有限的,但是系统是多任务的,所以存在多进程、多线程,可能同时需要对某一个资源进行访问,用来保证资源的有序访问。

否则会产生不可预计的结果。

 

 

2)信号量是什么东西?

信号量是一个计数值,这个值表示当前可用的资源数,这个值也是可用是一个负数,负数的话,表示的是等待的进程数量。还有一个指针,这个指针指向等待该信号量的进程

P操作,减一操作。

V操作,加一操作。

操作属于原子操作,是不可被打断一种操作,一定要等待这个操作的完成。要包含的代码要尽量短,而且不要有循环。

 

3)如何使用信号量?

(1)、产生一个key值。

SYNOPSIS

        #include <sys/types.h>

        #include <sys/ipc.h>

 

        key_t ftok(const char *pathname, int proj_id);

pathname  (which  must  refer  to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件

proj_id (which must be nonzero) to generate a  key_t  必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。

 

返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。

 

(2)、根据这个key值,去创建一个信号量。不同进程就可以根据这个信号量的ID去找到这个信号量,然后操作它。

SYNOPSIS

      #include <sys/types.h>

        #include <sys/ipc.h>

        #include <sys/sem.h>

 

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

key: 就是我们第一步通过ftok产生的key值。

nsems:信号量的个数。//一般为1

semflg:

创建  IPC_CREAT | 权限位  例如: IPC_CREAT | 0664

 

 

(3)、要对这个信号量,进行初始化。//初始化信号量值

SYNOPSIS

        #include <sys/types.h>

        #include <sys/ipc.h>

        #include <sys/sem.h>

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

semid:是创建的信号量的ID

semnum:表示是对第几个信号量来进行设置。//一般取值为0

cmd:SETVAL 设置信号量的初始值。

 

第四个参数,取决于第三个参数,如果有第四个参数的话,它是一个联合体,这个联合体是这样定义的//第四个参数为这个联合体,主要是为了设置联合体的val值

           union semun {

                int              val;    /* Value for SETVAL */

                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */

                unsigned short  *array;  /* Array for GETALL, SETALL */

                struct seminfo  *__buf;  /* Buffer for IPC_INFO

                                            (Linux-specific) */

            };

 

  1. 、调用这个信号量去保护临界资源。//如何理解信号量?

调用P操作(-1),用完之后,调用V操作。(+1)//当信号值为负数的时候就会卡在那里等待,当使用v操作会+1变成0,就不会阻塞了;所以当信号量初始值为0时,进行p操作(-1)会卡在那里等待,当用v操作时就会被疏通;可以理解为停车场,当信号量为1时,有一个位置你可以进去,当为0时,你正好在里面,但是没有了位置多,当为-1时,表明你要在外面等待,卡在那了

 

SYNOPSIS

        #include <sys/types.h>

        #include <sys/ipc.h>

        #include <sys/sem.h>

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

semid:这个信号量的ID

struct sembuf, containing the following members:

 

            unsigned short sem_num;  /* semaphore number操作第几个信号量 */

            short          sem_op;   /* semaphore operation  P还是V操作,如果是互斥式的信号量,P操作,这个就等于-1,如果是v操作就等于1 */

            short          sem_flg;  /* operation flags  SEM_UNDO */

 

成功返回0,失败返回-1;

Sem.c

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

#include<stdlib.h>

union semun

{

int val;

struct semid_ds *buf;

unsigned short *array;

};

int sem_p(int semid)

{

struct sembuf sbuf;

sbuf.sem_num=0;//表示操作第几个信号量,与下面要统一;

sbuf.sem_op=-1;//表示进行p操作;

sbuf.sem_flg=SEM_UNDO;//默认使用这个标志位

if((semop(semid,&sbuf,1))==-1)//去设置这个信号量

{

perror("p op fail");

return -1;

}

return 0;

}

int sem_v(int semid)

{

struct sembuf sbuf;

sbuf.sem_num=0;

sbuf.sem_op=1;

sbuf.sem_flg=SEM_UNDO;

if((semop(semid,&sbuf,1))==-1)

{

perror("p ov fail");

return -1;

}

return 0;

}

int main()

{

key_t key;

int semid,ret;

pid_t pid;

union semun tmp;

key=ftok("/home/gec",2);//产生一个key值,用来创建一个信号量

printf("key=%d\n",key);

if(key<0)

{

perror("ftok fail");

exit(-1);

}

semid=semget(key,1,IPC_CREAT | 0777); //创建这个信号量 1代表信号量的个数,返回信号量的ID

if(semid<0)

{

perror("semget fail");

exit(-1);

}

tmp.val=0;//初始化信号量的初始值为0,所以下面的操作就要先用v操作来释放这个信号量

ret=semctl(semid,0,SETVAL,tmp);//初始化信号量

if(ret<0)

{

perror("semctl fail");

exit(-1);

 

}

pid=fork();

if(pid<0)

{

perror("fork fail");

exit(-1);

}

if(pid==0)

{

// sem_p(semid);

 //因为信号量的初始值为0,所以不用p操作,如果这里也用p操作大家都会锁死

sleep(3);

printf("child pid :%d\n",getpid());

sem_v(semid);//完了之后用V操作,回覆信号量初始值为1

}

else

{

sem_p(semid);//0

printf("parent pid :%d\n",getppid());

sem_v(semid);//1

printf("*****\n");

sem_p(semid);//0

sem_p(semid);//-1会在这卡住

printf("*********\n");

semctl(semid,0,IPC_RMID,tmp);

}

return 0;

 

}

 

 

老师的代码:sem.c

#include<stdio.h>

#include<stdlib.h>

#include<sys/sem.h>

 

// 联合体,用于semctl初始化

union semun

{

    int              val; /*for SETVAL*/

    struct semid_ds *buf;

    unsigned short  *array;

};

 

// 初始化信号量

int init_sem(int sem_id, int value)

{

    union semun tmp;

    tmp.val = value;

//// 控制信号量的相关信息 int semctl(int semid, int sem_num, int cmd, ...);

//SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。

//IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

    if(semctl(sem_id, 0, SETVAL, tmp) == -1)

    {

        perror("Init Semaphore Error");

        return -1;

    }

    return 0;

}

 

// P操作:

//    若信号量值为1,获取资源并将信号量值-1

//    若信号量值为0,进程挂起等待

int sem_p(int sem_id)

{

    struct sembuf sbuf;

    sbuf.sem_num = 0; /*序号 标识信号量集中的第几个信号量  // 信号量组中对应的序号,0~sem_nums-1    */

    sbuf.sem_op = -1; /*P操作  对信号量的操作  信号量值在一次操作中的改变量 */

    sbuf.sem_flg = 0;//SEM_UNDO;//sem_flg 指定IPC_NOWAIT 则semop函数出错返回EAGAIN  如果为SEM_UNDO,那么将维护进程对信号量的调整值,以便进程结束时恢复信号量的状态

// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1

// int semop(int semid, struct sembuf semoparray[], size_t numops)

    if(semop(sem_id, &sbuf, 1) == -1)

    {

        perror("P operation Error");

        return -1;

    }

    return 0;

}

 

// V操作:

//    释放资源并将信号量值+1

//    如果有进程正在挂起等待,则唤醒它们

int sem_v(int sem_id)

{

    struct sembuf sbuf;

    sbuf.sem_num = 0; /*序号*/

    sbuf.sem_op = 1;  /*V操作*/

    sbuf.sem_flg = 0;//SEM_UNDO;

 

    if(semop(sem_id, &sbuf, 1) == -1)

    {

        perror("V operation Error");

        return -1;

    }

    return 0;

}

 

// 删除信号量集

int del_sem(int sem_id)

{

    union semun tmp;

    if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)

    {

        perror("Delete Semaphore Error");

        return -1;

    }

    return 0;

}

 

 

int main()

{

    int sem_id;  // 信号量集ID

    key_t key;  

    pid_t pid;

 

    // 获取key值

    if((key = ftok(".", 'z')) < 0)

    {

        perror("ftok error");

        exit(1);

    }

 

    // 创建信号量集,其中只有一个信号量

//当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1; 如果是引用一个现有的集合,则将num_sems指定为 0 int semget(key_t key, int num_sems, int sem_flags);

    if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)

    {

        perror("semget error");

        exit(1);

    }

 

    // 初始化:初值设为0资源被占用

    init_sem(sem_id, 0);

 

    if((pid = fork()) == -1)

        perror("Fork Error");

    else if(pid == 0) /*子进程*/

    {

        sleep(2);

        printf("Process child: pid=%d\n", getpid());

        sem_v(sem_id);  /*释放资源*/

    }

    else  /*父进程*/

    {

        sem_p(sem_id);   /*等待资源*/

        printf("Process father: pid=%d\n", getpid());

        sem_v(sem_id);   /*释放资源*/

        del_sem(sem_id); /*删除信号量集*/

    }

    return 0;

}

 

 

 

 

 

 

 

 

进程的状态

消息队列

1、消息队列是什么?

消息队列是在内核空间内部的一个链表。通过Linux内核在各个进程间传递消息。

要获取对应的消息队列,每一个消息队列都有一个唯一的表示符来区分。

 

 

 

2、怎么样使用消息队列进行进程间通讯?

 

1)先产生一个标示符

SYNOPSIS

       #include <sys/types.h>

       #include <sys/ipc.h>

 

       key_t ftok(const char *pathname, int proj_id);//两个随机值产生一个唯一的随机的key值

pathname  (which  must  refer  to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件 /home/gec

proj_id (which must be nonzero) to generate a  key_t  必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。 随机非0数字

 

返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。

 

 

2)通过这个key去创建消息队列

 

SYNOPSIS

       #include <sys/types.h>

       #include <sys/ipc.h>

       #include <sys/msg.h>

 

       int msgget(key_t key, int msgflg);

第一个参数,第一个参数就是我们第一步通过ftok产生的key值,用来标识唯一的消息队列。

第二个参数,分为有两种

第一种情况:创建  IPC_CREAT | 权限位  例如: IPC_CREAT | 0664 (一般用这个,0777也可以,注意可以有空格)

第二种情况:打开 0

 

返回值:

成功返回的是消息队列的ID,如果失败了,返回-1,并且错误码可以通过perror打印出来。

 

 

3)通过收发的函数,实现收发,实现进程间通讯。

 

SYNOPSIS

       #include <sys/types.h>

       #include <sys/ipc.h>

       #include <sys/msg.h>

 

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。

msgp:这个是指针,指向我们所需要发送的消息,消息我们可以自定义。

           struct msgbuf {

                long mtype;       /* message type, must be > 0  消息的类型,这个参数是自己定义 */

               char mtext[1];    /* message data  这个是我们需要发送的消息的具体数据 */

            };

msgsz: 消息内容的长度,是对应mtext的实际大小。//一般用数组大小

msgflg:

1)IPC_NOWAIT 不阻塞的模式。

2)0 阻塞模式发送(一般都是用这个)

 

返回值:成功返回0,失败的话返回-1,并且可以把对应的错误码打印出来。

 

 

这个函数的作用,就是在指定的消息队列里面,把消息摘下来,拷贝到msgp所指定buffer里面。

       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

msqid:这个参数就是消息队列的ID,表示我们要把消息发送到哪个队列,或者从哪个队列来接受消息。

msgp:这个是指针,指向我们所需要接收的消息的buffer,

msgsz:指的是消息的最大的长度,消息内容的长度,是对应mtext的实际大小。

msgtyp:指的是消息的类型,0代表读取的是队列内第一个消息。如果是大于零的时候,队列里面第一个msgtyp类型的消息就会被读回来。(一般用888)

msgflg:

1)IPC_NOWAIT 不阻塞的模式。

2)0 阻塞模式发送(一般都是用这个)

 

 

返回值:成功的话,返回的是,对应mtext的实际大小(实际消息的长度), 失败的话返回-1,并且可以把对应的错误码打印出来。

 

 

4)用于控制这个消息队列,比如说删除队列。

 

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msqid:这个参数就是消息队列的ID

cmd: 一些命令,常用的有删除队列的命令:IPC_RMID

msqid_ds *buf ,关于队列的一些信息的描述。

Msg-sever.c

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#include<stdlib.h>

struct msgbuf

{

long mtype;

char mtext[256];

};

int main()

{

int msgid;

key_t key;

struct msgbuf msg,msgr;

key=ftok("/home/gec/caobo",111);

if(key<0)

{

perror("fork fail");

exit(-1);

}

printf("key=%d\n",key);

msgid=msgget(key,IPC_CREAT | 0777);

if(msgid<0)

{

perror("msgget fail");

exit(-1);

}

msg.mtype=888;

// scanf("%s",msg.mtext);

while(1)

{

msgrcv(msgid,&msgr,256,888,0);//通过没有赋值消息类型的结构体来读,赋值了的来写,下面程序也是;

printf("曹博:%s\n",msgr.mtext);

// printf("sever recv msg type:%ld\n",msgr.mtype);

printf("陈素:\n");

scanf("%s",msg.mtext);

msgsnd(msgid,&msg,256,0);//注意msgr和msg为同种结构体类型的两个结构体,用来辅助读写操作

sleep(3);

}

return 0;

}

Msg-client.c

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#include<stdlib.h>

struct msgbuf

{

long mtype;

char mtext[256];

};

int main()

{

int msgid;

key_t key;

struct msgbuf msg,msgr;

key=ftok("/home/gec/caobo",111);

if(key<0)

{

perror("fork fail");

exit(-1);

}

printf("key=%d\n",key);

msgid=msgget(key,IPC_CREAT | 0777);

if(msgid<0)

{

perror("msgget fail");

exit(-1);

}

msg.mtype=888;

//scanf("%s",msg.mtext);

while(1)

{

printf("曹博:\n");

scanf("%s",msg.mtext);

msgsnd(msgid,&msg,256,0);

msgrcv(msgid,&msgr,256,888,0);

printf("陈素:%s\n",msgr.mtext);

// printf("client recv msg type:%ld\n",msgr.mtype);

sleep(3);

}

return 0;

}

 

 

 

3、信号量

 

1)为什么需要使用信号量?

因为我们的系统资源是有限的,但是系统是多任务的,所以存在多进程、多线程,可能同时需要对某一个资源进行访问,用来保证资源的有序访问。

否则会产生不可预计的结果。

 

 

2)信号量是什么东西?

信号量是一个计数值,这个值表示当前可用的资源数,这个值也是可用是一个负数,负数的话,表示的是等待的进程数量。还有一个指针,这个指针指向等待该信号量的进程

P操作,减一操作。

V操作,加一操作。

操作属于原子操作,是不可被打断一种操作,一定要等待这个操作的完成。要包含的代码要尽量短,而且不要有循环。

 

3)如何使用信号量?

(1)、产生一个key值。

SYNOPSIS

        #include <sys/types.h>

        #include <sys/ipc.h>

 

        key_t ftok(const char *pathname, int proj_id);

pathname  (which  must  refer  to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件

proj_id (which must be nonzero) to generate a  key_t  必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。

 

返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。

 

(2)、根据这个key值,去创建一个信号量。不同进程就可以根据这个信号量的ID去找到这个信号量,然后操作它。

SYNOPSIS

      #include <sys/types.h>

        #include <sys/ipc.h>

        #include <sys/sem.h>

 

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

key: 就是我们第一步通过ftok产生的key值。

nsems:信号量的个数。//一般为1

semflg:

创建  IPC_CREAT | 权限位  例如: IPC_CREAT | 0664

 

 

(3)、要对这个信号量,进行初始化。//初始化信号量值

SYNOPSIS

        #include <sys/types.h>

        #include <sys/ipc.h>

        #include <sys/sem.h>

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

semid:是创建的信号量的ID

semnum:表示是对第几个信号量来进行设置。//一般取值为0

cmd:SETVAL 设置信号量的初始值。

 

第四个参数,取决于第三个参数,如果有第四个参数的话,它是一个联合体,这个联合体是这样定义的//第四个参数为这个联合体,主要是为了设置联合体的val值

           union semun {

                int              val;    /* Value for SETVAL */

                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */

                unsigned short  *array;  /* Array for GETALL, SETALL */

                struct seminfo  *__buf;  /* Buffer for IPC_INFO

                                            (Linux-specific) */

            };

 

  1. 、调用这个信号量去保护临界资源。//如何理解信号量?

调用P操作(-1),用完之后,调用V操作。(+1)//当信号值为负数的时候就会卡在那里等待,当使用v操作会+1变成0,就不会阻塞了;所以当信号量初始值为0时,进行p操作(-1)会卡在那里等待,当用v操作时就会被疏通;可以理解为停车场,当信号量为1时,有一个位置你可以进去,当为0时,你正好在里面,但是没有了位置多,当为-1时,表明你要在外面等待,卡在那了

 

SYNOPSIS

        #include <sys/types.h>

        #include <sys/ipc.h>

        #include <sys/sem.h>

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

semid:这个信号量的ID

struct sembuf, containing the following members:

 

            unsigned short sem_num;  /* semaphore number操作第几个信号量 */

            short          sem_op;   /* semaphore operation  P还是V操作,如果是互斥式的信号量,P操作,这个就等于-1,如果是v操作就等于1 */

            short          sem_flg;  /* operation flags  SEM_UNDO */

 

成功返回0,失败返回-1;

Sem.c

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

#include<stdlib.h>

union semun

{

int val;

struct semid_ds *buf;

unsigned short *array;

};

int sem_p(int semid)

{

struct sembuf sbuf;

sbuf.sem_num=0;//表示操作第几个信号量,与下面要统一;

sbuf.sem_op=-1;//表示进行p操作;

sbuf.sem_flg=SEM_UNDO;//默认使用这个标志位

if((semop(semid,&sbuf,1))==-1)//去设置这个信号量

{

perror("p op fail");

return -1;

}

return 0;

}

int sem_v(int semid)

{

struct sembuf sbuf;

sbuf.sem_num=0;

sbuf.sem_op=1;

sbuf.sem_flg=SEM_UNDO;

if((semop(semid,&sbuf,1))==-1)

{

perror("p ov fail");

return -1;

}

return 0;

}

int main()

{

key_t key;

int semid,ret;

pid_t pid;

union semun tmp;

key=ftok("/home/gec",2);//产生一个key值,用来创建一个信号量

printf("key=%d\n",key);

if(key<0)

{

perror("ftok fail");

exit(-1);

}

semid=semget(key,1,IPC_CREAT | 0777); //创建这个信号量 1代表信号量的个数,返回信号量的ID

if(semid<0)

{

perror("semget fail");

exit(-1);

}

tmp.val=0;//初始化信号量的初始值为0,所以下面的操作就要先用v操作来释放这个信号量

ret=semctl(semid,0,SETVAL,tmp);//初始化信号量

if(ret<0)

{

perror("semctl fail");

exit(-1);

 

}

pid=fork();

if(pid<0)

{

perror("fork fail");

exit(-1);

}

if(pid==0)

{

// sem_p(semid);

 //因为信号量的初始值为0,所以不用p操作,如果这里也用p操作大家都会锁死

sleep(3);

printf("child pid :%d\n",getpid());

sem_v(semid);//完了之后用V操作,回覆信号量初始值为1

}

else

{

sem_p(semid);//0

printf("parent pid :%d\n",getppid());

sem_v(semid);//1

printf("*****\n");

sem_p(semid);//0

sem_p(semid);//-1会在这卡住

printf("*********\n");

semctl(semid,0,IPC_RMID,tmp);

}

return 0;

 

}

 

 

老师的代码:sem.c

#include<stdio.h>

#include<stdlib.h>

#include<sys/sem.h>

 

// 联合体,用于semctl初始化

union semun

{

    int              val; /*for SETVAL*/

    struct semid_ds *buf;

    unsigned short  *array;

};

 

// 初始化信号量

int init_sem(int sem_id, int value)

{

    union semun tmp;

    tmp.val = value;

//// 控制信号量的相关信息 int semctl(int semid, int sem_num, int cmd, ...);

//SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。

//IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

    if(semctl(sem_id, 0, SETVAL, tmp) == -1)

    {

        perror("Init Semaphore Error");

        return -1;

    }

    return 0;

}

 

// P操作:

//    若信号量值为1,获取资源并将信号量值-1

//    若信号量值为0,进程挂起等待

int sem_p(int sem_id)

{

    struct sembuf sbuf;

    sbuf.sem_num = 0; /*序号 标识信号量集中的第几个信号量  // 信号量组中对应的序号,0~sem_nums-1    */

    sbuf.sem_op = -1; /*P操作  对信号量的操作  信号量值在一次操作中的改变量 */

    sbuf.sem_flg = 0;//SEM_UNDO;//sem_flg 指定IPC_NOWAIT 则semop函数出错返回EAGAIN  如果为SEM_UNDO,那么将维护进程对信号量的调整值,以便进程结束时恢复信号量的状态

// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1

// int semop(int semid, struct sembuf semoparray[], size_t numops)

    if(semop(sem_id, &sbuf, 1) == -1)

    {

        perror("P operation Error");

        return -1;

    }

    return 0;

}

 

// V操作:

//    释放资源并将信号量值+1

//    如果有进程正在挂起等待,则唤醒它们

int sem_v(int sem_id)

{

    struct sembuf sbuf;

    sbuf.sem_num = 0; /*序号*/

    sbuf.sem_op = 1;  /*V操作*/

    sbuf.sem_flg = 0;//SEM_UNDO;

 

    if(semop(sem_id, &sbuf, 1) == -1)

    {

        perror("V operation Error");

        return -1;

    }

    return 0;

}

 

// 删除信号量集

int del_sem(int sem_id)

{

    union semun tmp;

    if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)

    {

        perror("Delete Semaphore Error");

        return -1;

    }

    return 0;

}

 

 

int main()

{

    int sem_id;  // 信号量集ID

    key_t key;  

    pid_t pid;

 

    // 获取key值

    if((key = ftok(".", 'z')) < 0)

    {

        perror("ftok error");

        exit(1);

    }

 

    // 创建信号量集,其中只有一个信号量

//当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1; 如果是引用一个现有的集合,则将num_sems指定为 0 int semget(key_t key, int num_sems, int sem_flags);

    if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)

    {

        perror("semget error");

        exit(1);

    }

 

    // 初始化:初值设为0资源被占用

    init_sem(sem_id, 0);

 

    if((pid = fork()) == -1)

        perror("Fork Error");

    else if(pid == 0) /*子进程*/

    {

        sleep(2);

        printf("Process child: pid=%d\n", getpid());

        sem_v(sem_id);  /*释放资源*/

    }

    else  /*父进程*/

    {

        sem_p(sem_id);   /*等待资源*/

        printf("Process father: pid=%d\n", getpid());

        sem_v(sem_id);   /*释放资源*/

        del_sem(sem_id); /*删除信号量集*/

    }

    return 0;

}

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/weixin_41723504/article/details/81516817
今日推荐