进程间通信总结

进程:一个程序的一次执行过程,是系统调度的单位,资源管理和程序执行的最小单位,线程是调度的最小单位。

程序:是静态的,是一个可执行文件,保存在硬盘上的一些指令的有序集合。

进程的执行过程:指令和数据是存放在硬盘上的,这是前提。首先运行程序---》系统会将硬盘上的指令和数据加载到内存中,(如果cpu每次都去访问硬盘,速度是非常慢的,而直接访问内存,这将非常的快)---cpu则访问内存执行代码;

进程包含三个段:代码段(存放程序代码)。

数据段:存放全局变量,malloc动态分配的地址空间,以及常数。

堆栈段:存放局部变量,子程序返回地址,子程序参数。

内存分为以下5个部分:(一个进程以创建,系统就会自动为该进程分配4G的内存,3G用户空间,1G内核空间(通过系统调用来访问内核空间))

栈区:由编译器自动分配和释放,存放函数的参数,局部变量等。

堆区:由程序员分配释放,若没有释放,程序结束时有OS来释放。

全局区(static):存放全局变量和静态变量,初始化了的放在一边,未初始化的放一边。

程序代码区:存放程序的二进制代码。

文字常量区:常量字符串,程序结束后由OS释放。

每个进程都有一个独有的pid

获取pid :函数有:getpid(); getppid();

命令有:ps –ef          ps -aux

Linux进程的7个状态:1.执行态,2.就绪态,3.阻塞态(浅睡眠,深睡眠),4.暂停态,5.死亡态(僵尸态(struct task_strucct未被清除),死亡态)。

进程的操作apifork, wait, waitpid, signal, pause, exec, kill ,exit

进程创建:最后由父进程回收子进程结构体。

#include "myhead.h"//创建多个子进程,他们属于同一父进程。

int main(int argc, char **argv)

{

int n;

n = atoi(argv[1]);

pid_t a;

int i;

for(i=0; i<n; i++){

a = fork();

if(a > 0) // parent//父进程中返回所创建的子进程的pid

continue;

if(a == 0) // child可以通过getpid()获取该子进程的pid

break;

}

if(a > 0){

for(i=0; i<n; i++)

wait(NULL);

}

printf("pid: %d, ppid: %d ",

getpid(), getppid());

exit(0);

}//

Exec():fork创建的子进程几乎拷贝了父进程的全部的内容。Exec用于更新子进程中的代码,数据段,栈段,使该进程只含有子进程它自身的代码。

代码示例:

exec函数

execl , execv, execle, execvp, execve.

int main()

{

fork()

if( ==0)

{

execl("可执行文件路径", 可执行文件名,"111","222","333"NULL);//需在子进程中执行。更多示例请参考代码文件夹。

}

}

Linuc守护进程:8个步骤

#include <unistd.h>

#include <sys/stat.h>

#include <syslog.h>

#include <sys/types.h>

#include <stdlib.h>

#include <errno.h>

#include <sys/file.h>

#include <stdio.h>

#include <sys/resource.h>

#include <signal.h>

int main(void)

{

pid_t pid;

int max_fd, i;

/***************************************

1. generate a child process, to ensure

   successfully calling setsid()

****************************************/

pid = fork();

if(pid > 0)

exit(0);

/*********************************************

2. ignore the signal SIGHUP, prevent the

   process from being killed by the shutdown

   of the present controlling termination

**********************************************/

signal(SIGHUP, SIG_IGN);

/******************************************************

3. call setsid(), let the first child process running

   in a new session without a controlling termination

*******************************************************/

setsid();

/*************************************************

4. generate the second child process, to ensure

   that the daemon cannot open a terminal file

   to become its controlling termination

**************************************************/

pid = fork();

if(pid > 0)

exit(0);

/*********************************************************

5. detach the daemon from its original process group, to

   prevent any signal sent to it from being delivered

**********************************************************/

setpgrp();

/*************************************************

6. close any file descriptor to release resource

**************************************************/

max_fd = sysconf(_SC_OPEN_MAX);

for(i=0; i<max_fd; i++)

close(i);

/******************************************

7. clear the file permission mask to zero

*******************************************/

umask(0);

/****************************************

8. change the process's work directory,

   to ensure it won't be uninstalled

*****************************************/

chdir("/");

// Congratulations! Now, this process is a DAEMON!

pause();

return 0;

}

进程间通信:为何要有进程通信:两个进程同时进行时,这两个进程间需要有些交互。比如MP3播放器,解码是一个进程,同时另一个用户操作也是一个进程,解码之后就需要将解码的数据交给用户操作的进程来执行。

无名管道:

注意一下几点: 只有能用于具有亲缘关系的进程之间的通信。

半双工通信方式,具有固定的读端和写端fd[0]读,fd[1]写。

它是一种特殊的文件,使用read,write来读写。

基本操作:

创建第三方(无名管道):pipe(fd);

将第三方与进程形成映射:fd

读写数据readwrite;

撤销映射:closefd[0];

删除第三方;

int main()

{

int fd[2]

pipe(fd);//创建管道,必须在进程之前,不然父进程就无法,将其拷贝给子进程。

r=fork();//创建进程

if(r==0)//子进程从fd中读出数据

{

close(fd[1]);//使用fd[0]时,记得关闭子进程的fd[1].

read(fd[0],buf,4);

printf();

close(fd[0]);

}

else if(r>0)//父进程向fd[1]中写入数据

{

sleep(1);

close(fd[0]);

write(fd[1],"abef",);

close(fd[1]);

}

}

关于读写需注意几点:1.当管道中无数据时,读操作会阻塞

2.向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。

3.如果读端已关闭  ,写数据  ,进程退出

4.如果写端已关闭,读数据  --->0

有名管道: 可以使互不相关的两个进程间进行通信。

遵循先进先出的规则。

通过文件io来操作。

不支持如lseek();

  1. 创建有名管道mkfifo
  2. 映射有名管道open(fd)
  3. 读写:readwrite
  4. 撤销映射:close(fd);
  5. 删除有名管道文件;

int main()

{

mkfifo("./fifo",O_CREAT|0666)//创建一个有名管道,是一个管道文件p

int fd=open("./fifo",O_WRONLY) //打开管道得到一个文件描述符fd

wrtie(fd,“” ,)//向文件描述符中写入数据

close(fd); // 关闭文件描述符

}

p2.c

int main()

{

int fd=open("./fifo",O_RDONLY);//打开同一个管道文件,得到一个文件描述符

char buf[12];//

int r=read(fd,buf,11);//读取文件描述符中的数据

buf[r]='';对字符串的操作,这不很关键,没有这步,易出现乱码。

printf("%s",buf);

close(fd);

}

关于读写需要注意的几点容易出错,或者打不开管道:

① open(const char *path, O_RDONLY );

在这种情况下,open 调用将阻塞,除非有一个 进程以写方式打开同一个FIFO,否则它不会返回。

open(const char *path, O_RDONLY|O_NONBLOCK )

即使没有其它进程以写方式打开FIFO,这open调 用也将成功并马上返回

open(const char *path, O_WRONLY)

open调用将阻塞,直到有一个进程以读方式打开同一个 FIFO为止。

open(const char *path, O_WRONLY|O_NONBLOCK)

open调用总是立刻返回,便如果没有进程以读方式打开FIFO文件, open调用将返回一个错误(-1)并且FIFO也不会被打开。

比较好:

#include<stdio.h>

#include<unistd.h>

#include<sys/types.h>

#include<stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include<string.h>

int main()

{

if(mkfifo("/home/gec/fifo",O_CREAT|0666)==-1)

perror("mkfifo");

char str[10];

int fd=open("/home/gec/fifo",O_WRONLY);

printf("start ");

if(fd==-1)

{

printf("open error: ");

}

while(1)

{

pid_t  ret= fork();

if(ret<0)

{

printf("fork1 error:");

}

if(ret==0)

{

sprintf(str,"%d",getpid());//spriintf的使用;

write(fd,str,strlen(str));//strlen()的使用

printf("%s ",str);

sleep(1);

printf("write succes ");

exit(0);

}

wait(NULL);

}

}

信号:

在软件层次上对中断机制的一种模拟,是一种异步通信模式。

可以直接进行内核进程和用户进程之间的交互。

信号发送,信号阻塞(OS),信号的响应。

Raise(int sig)//向自己发送信号

Alarm(5)//5s后向自己发送SIGALRM信号,接收到这个信号后,进程遇到pause(),暂停5秒后继续向下执行。(一个进程最多有一个闹钟,如果有多个闹钟,以后一个为准)

//#include "myhead.h"

#include <unistd.h>

#include<signal.h>

#include<sys/types.h>

#include <sys/wait.h>

#include<stdio.h>

#include <stdlib.h>

pid_t a, b;//灵活使用全局变量

void catch_sig(int sig)

{

kill(a, SIGUSR1);/向子进程发送信号SIGUSR1

kill(b, SIGUSR2);

printf("%d,%d ",a,b);

}

void f(int sig)

{

printf("child 1 catch the signal: %d ", sig);

}

void g(int sig)

{

printf("child 2 catch the signal: %d ", sig);

}

int main(void)

{

signal(SIGINT, catch_sig);//父进程中注册信号SIGINT

a = fork();

if(a > 0){

b = fork();

if(b > 0){ // parent

pause();

wait(NULL);//等待子进程退出,父进程继续执行

wait(NULL);

printf("parent exit! ");

exit(0);

}

else if(b == 0){ // child 2

printf("pid=%d ",getpid());

signal(SIGUSR2, g);//注册信号SIGUSR2

pause();

exit(0);

}

}

else if(a == 0){ // child 1

printf("pid=%d ",getpid());

signal(SIGUSR1, f);

pause();//暂停进程信号

exit(0);

}

}

Alarm:

#include <unistd.h>

#include<signal.h>

#include<sys/types.h>

#include <sys/wait.h>

#include<stdio.h>

#include <stdlib.h>

int main()

{

int ret;

ret =alarm(5);

printf("wakenff  ");

sleep(1);

printf("after sleep1, %d ",alarm(3));

pause();

printf("waken  ");

return 0;

}

Forkkill:

#include <unistd.h>

#include<signal.h>

#include<sys/types.h>

#include <sys/wait.h>

#include<stdio.h>

#include <stdlib.h>

int main()

{

pid_t pid;

int ret;

if((pid=fork())<0)

{

perror("fork");

exit(1);

}

if(pid==0)

{

printf("chiled process  ");

raise(SIGSTOP);

printf("chiled process exit  ");

exit(0);

}

else

{ sleep(1);

printf("pid =%d  ",pid);

if((waitpid(pid,NULL,WNOHANG))==0)

{

kill(pid,SIGKILL);

printf("kill %d ",pid);

}

}

}

Signaltest:

#include <unistd.h>

#include<signal.h>

#include<sys/types.h>

#include <sys/wait.h>

#include<stdio.h>

#include <stdlib.h>

void my_func(int sign)

{

if(sign==SIGINT)

printf("i have got sigint ");

else if(sign==SIGQUIT)

printf("i have got sigquit ");

}

int main()

{

printf("waitting for signal sigint or sigquit ");

signal(SIGINT,my_func);

signal(SIGQUIT,my_func);

pause();

printf("exit ");

exit(0);

}

拓展:

#include<stdio.h>

#include<signal.h>

#include<stdlib.h>

int output(sigset_t set)

{

printf("set.val[0]=%x ",set.__val[0]);

}

void handler(int sig)

{

int i;

sigset_t sysset;

printf("  nin hadler sig=%d ",sig);

sigprocmask(SIG_SETMASK,NULL,&sysset);

output(sysset);

printf("return ");

}

struct sigaction {

               void     (*sa_handler)(int);

               void     (*sa_sigaction)(int, siginfo_t *, void *);

               sigset_t   sa_mask;

               int        sa_flags;

               void     (*sa_restorer)(void);

           };

sa_flags=SA_SIGINFO,时,使用第二个函数原型。

int main()

{

struct sigaction act;

sigset_t set,sysset,newset;

sigemptyset(&set);

sigemptyset(&newset);//清空结构体阻塞信号集

sigaddset(&set,SIGUSR1);//将信号加入到结构体阻塞信号集sysset

sigaddset(&newset,SIGUSR2);

printf(" add sigusr1,the value of set");

output(set);//200

printf(" add sigusr2,the value of newset");

output(newset);//800

printf(" after set proc block set ,and then read to sysset ");

//将结构体阻塞信号集(在命令程序运行期间阻塞)set加入到进程阻塞信号集中

sigprocmask(SIG_SETMASK,&set,NULL);

//将进程阻塞信号集加入到结构体阻塞信号集sysset

sigprocmask(SIG_SETMASK,NULL,&sysset);

printf("system mask is: ");

output(sysset);//2a00>200+800

printf("install sigalrm,and the act.sammask is newset(siguser2) ");

act.sa_handler=handler;

act.sa_flags=0;//表示使用结构体中的前一个函数原型

act.sa_mask=newset;

sigaction(SIGALRM,&act,NULL);//运行之后该信号会自动加入到进程阻塞信号集(始终都阻塞)中。

pause();

printf("after exec isr ");

sigemptyset(&sysset);

sigprocmask(SIG_SETMASK,NULL,&sysset);

output(sysset);//200

}

信号量集:systemV(semphore)

  1. ftok:得到一个唯一的semid;
  2. 创建一个信号量集semget,返回一个唯一的semid;
  3. 初始化信号量集semctl(SETVAL)
  4. 判断信号量的值semctl(GETVAL);
  5. 操作信号量的值semop(),主要是设置一个结构体struct sembuf

{

Short sem_num;信号量编号

Short sem_op;对应的操作+1v),-1(p)

Short sem_flg;SEM_UNDO

}

6.读写数据

7.删除信号量集semctl(IPC_RMID);

Share memory(共享内存)

最为高效的通信方式,进程可以直接读写数据,而不需要拷贝。

内核专门留出了一块内存,可以将其映射到用户空间某段内存。用户可以直接操作这段内存来间接操作内核内存,实现通信。

多个进程共享这段内存,需要同步机制(信号量)和互斥所.

共享内存使用步骤:

  1. ftok:得到一个唯一的key
  2. 创建打开共享内存shmget,返回一个shmid;
  3. 映射一段共享内存shmat,返回内存地址的指针void *
  4. 读写操作
  5. 撤销共享内存映射shmdt
  6. 删除共享内存(一般有接收进程来实现)shmctl()

将一个结构体的数据写入是相同的操作:将两个进程的的共享内存的指针类型声明为该结构体类型即可,然后通过这个指针来获取数据,如果有多个结构体,指针需向后移。

SystemV.c(读取数据)

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/shm.h>

#include<sys/sem.h>

#include<sys/ipc.h>

int main()

{

key_t key = ftok("./",100);

if(key==-1)

perror("shmftok() error!/n");

int shmid = shmget(key,1024,IPC_CREAT|0666);

if(shmid==-1)

perror("shmget() error!/n");

char *p = shmat(shmid,NULL,0);

key_t semkey = ftok("./",102);

if(semkey ==-1)

perror("semftok() error! ");

int semid = semget(semkey,2,IPC_CREAT|0666);

if(semid == -1)

perror("semget() error! ");

// semctl(semid,0,SETVAL,1);

// if(semctl(semid,1,SETVAL,0)==-1)

// perror("semctl() error! ");

struct sembuf sembv;

sembv.sem_num = 0;

sembv.sem_op = 1;

sembv.sem_flg = SEM_UNDO;

struct sembuf sembp;

sembp.sem_num = 1;

sembp.sem_op = -1;

sembp.sem_flg = SEM_UNDO;

while(1)

{

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

// printf("%d ",semctl(semid,0,GETVAL));

perror("semop() error! ");

if(semctl(semid,0,GETVAL)==0)

{

printf("%s ",p);

semop(semid,&sembv,1);

}

else

{

perror("read error ");

}

}

}

Systemv1.c(写入数据)

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/shm.h>

#include<sys/sem.h>

#include<sys/ipc.h>

int main()

{

key_t key = ftok("./",100);

if(key==-1)

perror("shmftok() error!/n");

int shmid = shmget(key,1024,IPC_CREAT|0666);

if(shmid==-1)

perror("shmget() error!/n");

char *p = shmat(shmid,NULL,0);

key_t semkey = ftok("./",102);

if(semkey ==-1)

perror("semftok() error! ");

int semid = semget(semkey,2,IPC_CREAT|0666);//创建信号量集,返回semid

if(semid == -1)

perror("semget() error! ");

semctl(semid,0,SETVAL,1);

if(semctl(semid,1,SETVAL,0)==-1)

perror("semctl() error! ");

struct sembuf sembv;

sembv.sem_num = 1;

sembv.sem_op = 1;

sembv.sem_flg = SEM_UNDO;

struct sembuf sembp;

sembp.sem_num = 0;

sembp.sem_op = -1;

sembp.sem_flg = SEM_UNDO;

while(1)

{

// printf("%d ",semctl(semid,0,GETVAL));

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

//printf("%d ",semctl(semid,0,GETVAL));

perror("semop() error! ");

if(semctl(semid,0,GETVAL)==0)

{

scanf("%s",p);

semop(semid,&sembv,1);

}

else

{

perror("write error ");

}

}

}

消息队列:

  1. 通过ftok得到一个唯一的key
  2. 创建消息队列msgget(),返回一个唯一的msgid;
  3. 发送消息,接收消息;消息结构体典型的类型:struct msgbuf
    1. {
    2. long mtype;
    3. char mtext[];
    4. }
    5. 删除消息队列:msgctl(IPC_RMID)

message.c(先发后收)两个进程之间进行通信

#include<stdlib.h>

#include<stdio.h>

#include<sys/msg.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<string.h>

#include<sys/sem.h>

struct msgbuf

{

long mtype;

char mtext[12];

};

int main()

{

key_t key = ftok("./",20);

if(key==-1)

perror("ftok() error ");

int msgid = msgget(key,IPC_CREAT|0666);

if(msgid==-1)

perror("msgget() error! ");

 

struct msgbuf send1,rcv1;

send1.mtype = 200;

strcpy(send1.mtext,"hello!");

 

 

key_t semkey = ftok("./",21);

int semid = semget(semkey,4,IPC_CREAT|0666);

if(semid ==-1)

perror("semget() error! ");

 

semctl(semid,0,SETVAL,1);

semctl(semid,1,SETVAL,0);

semctl(semid,2,SETVAL,0);

semctl(semid,3,SETVAL,0);

struct sembuf semb0,semb1,semb2,semb3;

semb0.sem_num = 0;

semb0.sem_op = -1;

semb0.sem_flg = SEM_UNDO;

 

semb1.sem_num = 1;

// semp1.sem_op = 1;

semb1.sem_flg = SEM_UNDO;

 

semb2.sem_num = 2;

// semp.sem_op = -1;

semb2.sem_flg = SEM_UNDO;

 

semb3.sem_num = 3;

// semp.sem_op = -1;

semb3.sem_flg = SEM_UNDO;

 

 

while(1)

{

semb0.sem_op = -1;

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

perror("semop() error! ");

if(semctl(semid,0,GETVAL)==0)

{

char messag[30];

printf("please input:");

scanf("%s",messag);

strcpy(send1.mtext,messag);

msgsnd(msgid,&send1,strlen(send1.mtext)+1,0);

semb1.sem_op = 1;

semop(semid,&semb1,1);

}

semb3.sem_op = -1;

semop(semid,&semb3,1);

if(semctl(semid,3,GETVAL)==0)

{

msgrcv(msgid,&rcv1,sizeof(rcv1),100,0);

printf("accept:");

printf("%s ",rcv1.mtext);

semb0.sem_op = 1;

semop(semid,&semb0,1);

}

// else

// {

// perror("msg1 error! ");

// }

}

 

}

message2.c(先收后发)两个进程之间进行通信

#include<stdlib.h>

#include<stdio.h>

#include<sys/msg.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<string.h>

#include<sys/sem.h>

struct msgbuf

{

long mtype;

char mtext[12];

};

int main()

{

key_t key = ftok("./",20);

if(key==-1)

perror("ftok() error ");

int msgid = msgget(key,IPC_CREAT|0666);

if(msgid==-1)

perror("msgget() error! ");

 

struct msgbuf send1,rcv1;

send1.mtype = 100;

// strcpy(send1.mtext,"hello!");

 

 

key_t semkey = ftok("./",21);

int semid = semget(semkey,4,IPC_CREAT|0666);

if(semid ==-1)

perror("semget() error! ");

 

// semctl(semid,0,SETVAL,1);

// semctl(semid,1,SETVAL,0);

 

struct sembuf semb0,semb1,semb2,semb3;

semb0.sem_num = 0;

// semb0.sem_op = -1;

semb0.sem_flg = SEM_UNDO;

 

semb1.sem_num = 1;

// semb1.sem_op = 1;

semb1.sem_flg = SEM_UNDO;

 

semb2.sem_num = 2;

// semb1.sem_op = 1;

semb2.sem_flg = SEM_UNDO;

 

semb3.sem_num = 3;

// semb1.sem_op = 1;

semb3.sem_flg = SEM_UNDO;

 

while(1)

{

semb1.sem_op=-1;

// semop(semid,&semb1,1);

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

perror("semop() error! ");

if(semctl(semid,1,GETVAL)==0)

{

msgrcv(msgid,&rcv1,sizeof(rcv1),200,0);

printf("accept:");

printf("%s ",rcv1.mtext);

semb2.sem_op = 1;

semop(semid,&semb2,1);

}

semb2.sem_op = -1;

semop(semid,&semb2,1);

if(semctl(semid,2,GETVAL)==0)

{

char messag[30];

printf("please input:");

scanf("%s",messag);

strcpy(send1.mtext,messag);

msgsnd(msgid,&send1,strlen(send1.mtext)+1,0);

semb3.sem_op = 1;

semop(semid,&semb3,1);

}

// else

// {

// perror("msg2 error! ");

// }

}

 

}

原文地址:https://www.cnblogs.com/defen/p/5251685.html