进程间通信(IPC)

进程间通信概述

        进程间通信是指不同进程之间交换或传播信息。进程间通信的方式通常有管道通信、消息队列、信号量、共享存储、Socket、Stream等,其中Socket和Stream支持不同主机上的两个进程间的通信。

管道(无名管道):

        管道通信创建会在内核开辟一段空间,父进程可以往里面写数据,子进程从里面读取数据;子进程往里面写数据,父进程从里面读取数据。

特点:

  • 半双工(一边写的时候,另一边只能读)。
  • 只能用于具有亲缘关系的进程之间的通信(父子进程、兄弟进程之间)。
  • 可以看作是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
  • 管道中的数据读走之后就没有了

函数原型:


        #include

        int pipe(int fd[2])

返回值:

         当开辟管道成功时返回0,失败时则返回-1

参数:

        当一个管道创建时会创建两个文件描述符,fd[0]为读,fd[1]为写

注意:管道中国若没有数据,但是调用read取读取数据,则会发生阻塞,阻塞在read

示例:

        若要数据流从父进程流向子进程,则要关闭父进程的读端fd[0]与子进程的写端fd[1],反之,则可以使数据流从子进程流向父进程。

进程间通信(IPC)_第1张图片 数据流从父进程流向子进程 进程间通信(IPC)_第2张图片 数据流从子进程流向父进程

进程间通信(IPC)_第3张图片

 进程间通信(IPC)_第4张图片

命名管道(FIFO):

特点:

  • FIFO可以在无关的进程之间交换数据,与无名管道不同。
  • FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

函数原型:

#include

int mkfifo(const char *pathname, mode_t mode);

返回值:

成功返回0,出错返回-1

参数:

  • pathname:管道存储于文件系统中,pathname指明管道的路径名
  • mode与open中的mode参数一致,一旦创建了一个FIFO,就可以用一般的文件I/O函数操作它。

创建命名管道示例:

进程间通信(IPC)_第5张图片

 进程间通信(IPC)_第6张图片

命名管道的数据通信示例:

当open一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK,只读open要阻塞到某个其他进程为写而打开此FIFO。类似的,只写open要阻塞到某个其他进程为读而打开它。
  • 若指定了O_NONBLOCK,则只读open立即返回。而只写open将出错返回-1。如果没有进程已经打开该FIFO,其error置ENXIO。

write代码:

#include 
#include 
#include 
#include 
#include 
#include 

//int mkfifo(const char *pathname, mode_t mode);
//int open(const char *pathname, int flags);
//int open(const char *pathname, int flags, mode_t mode);

int main()
{
        int cnt = 0;
        char *str="message from fifo";
        int fd =open("/home/CLC/FILE",O_WRONLY);
        printf("write open success\n");

        while(1)
        {
                write(fd, str,strlen(str));
                sleep(1);
                if(cnt==5)
                {
                        break;
                }
        }
        close(fd);
        return 0;
}

read代码: 

#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
        char buf[30] ={0};
        int nread=0;

        if((mkfifo("/home/CLC/FILE",0600)==-1) && errno != EEXIST)
        {
                printf("mkfifo failed\n");
                perror("why");

        }

        int fd =open("/home/CLC/FILE",O_RDONLY);
        printf("open successed\n");

        while(1)
        {
                nread =read(fd,buf,30);
                printf("read %d byte from fifo,context is %s\n",nread,buf);
        }
        close();
        return 0;
}    

进程间通信(IPC)_第7张图片

 以上代码的运行结果是:若管道中还未write数据,read方先从管道中读取,则read会阻塞,知道使用write向管道中写入数据,read才继续执行。

消息队列:

消息队列是消息的链表,存放于内核中。一个消息队列都有一个ID进行标识。

特点:

  • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
  • 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
  • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按照消息的类型读取。

通信原理:

进程间通信(IPC)_第8张图片 A写B读 进程间通信(IPC)_第9张图片 B写A读

函数原型:

#include

  • int msgget(key_t  key, int flag);        创建或打开消息队列
  • int msgsnd(int msgid, const void *ptr, size_t size, int flag);        添加消息
  • int msgrcv(int msgid, void *ptr, size_t size, long type, int flag);        读取消息度,失败返回-1
  • int msgctl(int msgid, int cmd, struct msgid_ds *buf);        控制消息队列

返回值:

  • int msgctl(int msgid, int cmd, struct msgid_ds *buf);        成功返回0,失败返回-1
  • int msgget(key_t key, int msgflg);        成功返回队列ID,失败返回-1
  • ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);        成功返回0,失败返回-1
  • int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);         成功返回消息数据的长度,失败返回-1

参数:

在以下两种情况下,msgget将创建一个新的消息队列:

  • 如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位
  • key参数为IPC_PRIVATE

函数msgrcv在读取消息队列时,type参数有以下几种情况

  • type==0 ,返沪队列中的第一个消息
  • type > 0 ,返回队列中消息类型为type的第一个消息
  • type <0 ,返回队列中消息类型小于或等于type绝对值的消息,如果有多个,则取类型值最小的消息。

        type值非0时用于以先进先出次序读消息,也可以把type看作优先级的权值。

示例:

示例1——消息队列收发数据

接收端:

#include 
#include 
#include 
#include 
#include 

//int msgget(key_t key, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main()
{
        struct msgbuf readbuf;
        struct msgbuf sendbuf={988,"thank for attatch"};

        int msgid=msgget(0x1234,IPC_CREAT|0600);
        if(msgid == -1)
        {
                printf("creat que failed\n");

        }
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from que %s\n",readbuf.mtext);

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
        return 0;
}

发送端:

#include 
#include 
#include 
#include 
#include 

//int msgget(key_t key, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main()
{
        struct msgbuf readbuf;
        struct msgbuf sendbuf={988,"thank for attatch"};

        int msgid=msgget(0x1234,IPC_CREAT|0600);
        if(msgid == -1)
        {
                printf("creat que failed\n");

        }
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from que %s\n",readbuf.mtext);

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
        return 0;
}

结果:

示例二——键值生成及消除消息队列移除

前面的示例中并没有获取key值,而是直接使用整型数将key值写死的,这种方式自然不太高级,但原理是一模一样的。

键值生成:
ftok函数

        系统建立IPC通信时必须指定一个ID。通常情况下,该ID可以通过ftok函数的到

所需头文件:

        #include

        #include

函数原型:

        key_t ftok( const char * fname, int id );

参数:

        fname:路径名/文件名

        id:子序号,虽然是int型,但是只使用8bits(1-255)

发送端:

#include 
#include 
#include 
#include 
#include 

//int msgget(key_t key, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main()
{
        struct msgbuf sendbuf={888,"this is message from que"};
        struct msgbuf readbuf ;

        key_t key;
        key=ftok(".",123);
        int msgid=msgget(key,IPC_CREAT|0600);

        if(msgid == -1)
        {
                printf("creat que failed\n");

        }

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);

        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),988,0);
        printf("return from rcv %s\n",readbuf.mtext);


        return 0;
}

接收端:

#include 
#include 
#include 
#include 
#include 

//int msgget(key_t key, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main()
{
        struct msgbuf readbuf;
        struct msgbuf sendbuf={988,"thank for attatch"};

        key_t key;
        key=ftok(".",123);
        int msgid=msgget(key,IPC_CREAT|0600);
        if(msgid == -1)
        {
                printf("creat que failed\n");

        }
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from que %s\n",readbuf.mtext);

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
        return 0;
}

结果:

消息队列移除:

以上方法中两个进程之间使用消息队列通信,如果内核中没有这个队列,就会直接创建这个队列,长此以往,内核中存放大量的队列,所以用完之后尽量把队列移除。

发送端:

#include 
#include 
#include 
#include 
#include 

//int msgget(key_t key, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main()
{
        struct msgbuf sendbuf={888,"this is message from que"};
        struct msgbuf readbuf ;

        key_t key;
        key=ftok(".",123);
        int msgid=msgget(key,IPC_CREAT|0600);

        if(msgid == -1)
        {
                printf("creat que failed\n");

        }

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);

        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),988,0);
        printf("return from rcv %s\n",readbuf.mtext);
        msgctl(msgid,IPC_RMID,NULL);


        return 0;
}

接收端:

#include 
#include 
#include 
#include 
#include 

//int msgget(key_t key, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main()
{
        struct msgbuf readbuf;
        struct msgbuf sendbuf={988,"thank for attatch"};

        key_t key;
        key=ftok(".",123);
        int msgid=msgget(key,IPC_CREAT|0600);
        if(msgid == -1)
        {
                printf("creat que failed\n");

        }
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from que %s\n",readbuf.mtext);

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);

        msgctl(msgid,IPC_RMID,NULL);
        return 0;
}

共享内存:

共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

进程间通信(IPC)_第10张图片

特点:

  • 共享内存是进程间共享数据的一种最快的方法。
  • 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
  • 使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。
  • 若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

函数原型:

 #include
 #include

  •  int shmget(key_t key, size_t size, int shmflg);        创建或获取一个共享内存
  • void *shmat(int shmid, const void *shmaddr, int shmflg);        连接共享内存到当前进程的地址空间
  • int shmdt(const void *shmaddr);        断开与共享内存的连接
  • int shmctl(int shmid, int cmd, struct shmid_ds *buf);        控制共享内存的相关信息

shmget函数用来创建或获取一个共享内存。当用shmget函数创建一段共享内存是,必须指定其size,而如果引用一个已经存在的共享内存,则将其size指定为0。

当一段共享内存被创建之后,它并不能被任何进程访问,必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。

shmdt函数是用来断开shmat建立的连接的。注意这并不是从系统中删除该内存,只是当前进程不能再访问该共享内存而已。

shmctl函数可以对共享内存执行多种操作,根据参数cmd执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。

返回值:

 int shmget(key_t key, size_t size, int shmflg);        成功返回共享内存ID,失败返回-1

void *shmat(int shmid, const void *shmaddr, int shmflg);        成功返回指向共享内存的指针,失败返回-1

int shmdt(const void *shmaddr);        成功返回0,失败返回-1

int shmctl(int shmid, int cmd, struct shmid_ds *buf);        成功返回0,失败返回-1

参数:

示例:

写:

#include 
#include 
#include 
#include 
#include 
//int shmget(key_t key, size_t size, int shmflg);
//void *shmat(int shmid, const void *shmaddr, int shmflg);

int main()
{
        int shmid;
        key_t key;
        char *shmaddr;
        key=ftok(".",1);
        shmid=shmget(key,1024*4,IPC_CREAT|0666);
        if(shmid ==-1)
        {
                printf("shm create failed\n");
                exit(-1);
        }

        shmaddr=shmat(shmid,0,0);

        printf("shmat ok\n");
        strcpy(shmaddr,"xxxxx");

        sleep(5);

        shmdt(shmaddr);

        shmctl(shmid,IPC_RMID,0);
        printf("quit shm\n");
        return 0;
}

读:

#include 
#include 
#include 
#include 
#include 
//int shmget(key_t key, size_t size, int shmflg);
//void *shmat(int shmid, const void *shmaddr, int shmflg);

int main()
{
        int shmid;
        key_t key;
        char *shmaddr;
        key=ftok(".",1);
        shmid=shmget(key,1024*4,0);
        if(shmid ==-1)
        {
                printf("shm create failed\n");
                exit(-1);
        }

        shmaddr=shmat(shmid,0,0);

        printf("shmat ok\n");
        printf("data: %s\n",shmaddr);


        shmdt(shmaddr);

        printf("quit shm\n");
        return 0;
}

结果:

进程间通信(IPC)_第11张图片

信号:

在内存共享中,两个进程都可以随意的对物理内存进行读写,但是,当一个进程正在写时,另一个进程又开始写,就会导致内存中的内容发生错乱,而信号就是为了防止这种情况,当某一个进程正在写时,其他进程只能读。

信号类似于软中断,许多重要的程序都需要处理信号。信号为Linux提供了一种处理异步事件的方法,比如终端用户输入ctrl+c来中断程序,会通过信号机制来停止一个程序。

更详细的信号内容见下面的文章:

Linux信号详解

特点:

  • 每个信号都有一个名字和编号,都以“SIG”开头。信号定义在signal.h头文件中,信号名都定义为正整数。具体的信号名称可以使用kill -l来查看信号的名字以及序号。序号 是从1开始的。
  • 信号的处理有如下三种方法
  1. 忽略信号:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是          SIGKILLSIGSTOP)。
  2.  捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
  3.  系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。

函数原型:

#include

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

参数:

  • signum:信号的编号。
  • handler:捕捉到信号后执行的函数指针。

示例1:捕捉ctrl+c,让ctrl+c不中断程序

  • 原理:

        ctrl+c在Linux中属于SIGINT,只要捕捉这个信号,再写一个函数对它进行处理,不让程序退出就可以了

  • 退不出程序怎么终止它?

        使用ps -aux | grep a.out查找进程ID,再使用kill -9 进程ID杀死该进程。

但是需要注意SIGKILL是不能用这种方式来不让进程中断的,也是不能被忽略的。

#include 
#include 

//typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);

void handler(int signum)
{
        printf("get signalNum=%d\n",signum);
        printf("nver quite\n");
}
int main()
{
        signal(SIGINT,handler);//signal(SIGINT,SIG_IGN)可以忽略ctrl+c
        while(1);
        return 0;
}

进程间通信(IPC)_第12张图片

 示例2:自己编写代码代替kill杀死示例1中的进程

#include 
#include 
#include 

//int kill(pid_t pid, int sig);

//typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);

int main(int argc ,char **argv)
{
        int signum;
        int pid;
        signum=atoi(argv[1]);    //atoi:ASCII码转换成int型

        pid =atoi(argv[2]);
        kill(pid, signum);
        printf("send signal ok\n");
        printf("num=%d,pid=%d\n",signum,pid);
        return 0;
}
~      

进程间通信(IPC)_第13张图片

 信号如何携带消息:

在前面信号章节中,只关注了收和发的两个动作,信号其实在发送、接收的时候还可以携带消息。

接收函数原型:

#include

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

struct sigaction {
        void   (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
        void   (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
        sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
        int   sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
 };
//回调函数句柄sa_handler、sa_sigaction只能任选其一

参数:

signum:信号的编号

 *act:类似signal的handler参数

*oldact:备份原有信号的操作

发送函数原型:

#include
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
   int   sival_int;
   void *sival_ptr;
 };

参数:

pid:发给哪个进程

sig:发的信号

value:消息

以下是一些参数详解:

进程间通信(IPC)_第14张图片

进程间通信(IPC)_第15张图片进程间通信(IPC)_第16张图片进程间通信(IPC)_第17张图片

示例:信号携带消息收发

接收信号并接收信号携带的消息:

#include 
#include 
#include 

//int kill(pid_t pid, int sig);

//typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);

//int sigaction(int signum, const struct sigaction *act,
//              struct sigaction *oldact);

void handler(int signum, siginfo_t *info, void *context)
{
        printf("get signum %d\n",signum);

        if(context != NULL)
        {
                printf("get data =%d \n",info->si_value.sival_int);
                printf("get data =%d \n",info->si_int);
                printf("senc pid is %d \n",info->si_pid);
        }
}

int main(int argc ,char **argv)
{
        int signum;
        struct sigaction act;

        printf("rev pid id %d \n",getpid());
        act.sa_sigaction=handler;
        act.sa_flags=SA_SIGINFO;

        sigaction(SIGUSR1, &act ,NULL);
        while(1);
        return 0;
}
     

发送信号并携带消息:

#include 
#include 
#include 

//int kill(pid_t pid, int sig);

//typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);

//int sigaction(int signum, const struct sigaction *act,
//              struct sigaction *oldact);
int main(int argc ,char **argv)
{
        int signum=atoi(argv[1]);
        int pid=atoi(argv[2]);

        union sigval value;
        value.sival_int=100;
        sigqueue(pid,signum,value);

        printf("send done\n");
        printf("send pid id %d \n",getpid());
        while(1);
        return 0;
}

运行结果:

信号量

信号量虽然和信号只差了一个字,但是它们是两种不同的概念。信号量(semaphore)与前面的IPC结构不同,它是一个计数器。信号量用于实现进程间的互斥同步,而不是用于存储进程间的通信数据。

临界资源:

各进程采取互斥的方式,实现共享的资源称为临界资源。多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次智能提供一个进程使用。一次仅允许一个 进程使用得资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。

特点:

  1. 信号量用于进程间同步,若要再进程间传递数据需要结合共享内存
  2. 信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作
  3. 每次对信号量的PV操作不仅限于信号量值得加1或减1,而且可以加减任意正整数
  4. 支持信号量组

函数原型:

最简单的信号量只能取0和1的变量,这也是信号量最常见的一种形式,叫做二值信号量。而可以取多个正整数的信号量被称为通用信号量。

Linux下的信号量函数都是再统一的信号量数组上进行操作的,而不是在一个单一的二值信号量上操作。

#include 
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);  
// 控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);

参数:

num_sems:信号量集中信号量的个数

sem_flags:获取信号量的时候一些权限,类似文件创建时的操作

示例:

控制子进程新运行,子进程运行完毕后,再运行父进程。

实现思路,先不放信号,等到子进程运行后放信号,在此期间父进程一直阻塞在取信号量的过程中,直到子进程放好信号量后,父进程才运行。

#include 
#include 
#include 
#include 
//int semget(key_t key, int nsems, int semflg);
//int semctl(int semid, int semnum, int cmd, ...);
//int semop(int semid, struct sembuf *sops, unsigned nsops);

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

void pGetKey(int id)
{
        struct sembuf set;
        set.sem_num=0;
        set.sem_op=-1;
        set.sem_flg=SEM_UNDO;
        semop(id ,&set  ,1);
        printf("get the key\n");

}
void vPutBackKey(int id)
{
        struct sembuf set;
        set.sem_num=0;
        set.sem_op=1;
        set.sem_flg=SEM_UNDO;
        semop(id ,&set  ,1);
        printf("put back the key\n");

}
int main(int argc,char **argv)
{
        key_t key;
        int semid;
        key = ftok(".",2);
        semid =semget(key,1,IPC_CREAT|0666);//creat and get  semaphore

        union semun initsem;
        initsem.val=0;
        semctl(semid,0,SETVAL,initsem);
        //init sem,0 means operate the 0th semaphore
        //SETVAL uses to set the value of the semaphore

        int pid =fork();
        if(pid>0)
        {
                pGetKey(semid);
                printf("this is father\n");
               vPutBackKey(semid);
        }

        if(pid ==0)
        {
                printf("this is child\n");
                vPutBackKey(semid);
        }
        else
        {
                printf("fork faile\n");
        }
        return 0;
}
                                             

你可能感兴趣的:(Linux系统编程——进程,linux,c语言)