linux阻塞的信号先进后出,linux进程间通信

一、概述

目的:

1.数据间的通信。一个进程需要将他的数据传递给其他进程。

2.资源的共享。多个进程间进行数据的共享。

3.事件的通知。一个进程需要想另一个货一组进程发送消息,通知他们发送了某种事件。

4.进行控制。有些进程希望完全控制另一个进程的执行(如debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时的指导它的状态改变。

发展:

1.UNIX进程间通信

2.基于Sysrem V进程间通信

3.POSIX(Portable Operating System Interfance

可移植操作系统接口)进程间通信

进程通信的分类:

1.管道(pipe)和有名管道(FIFO)

2.信号(signal)

3.消息队列

4.共享内存

5.信号量

6.套接字(socket)

二、管道通信

特点:管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的末尾写入数据,另一个进程(读进程)在管道的的头部读出数据。数据被一个进程从管道中读出后,将被从管道中删除,其他进程将不能再对该数据进行读取。

分类:

无名管道、有名管道。无名管道使用与父进程与子进程间的通信;有名管道可以使用与任意两个进程间的通信。

无名管道由pipe()函数创建:

无名管道:

int pipe(int filedis[2]);

当一个管道建立时,它会创建两个文件描述符:filedis[0]用于读管道,filedis[1]用于写管道。

管道用于不同进程间的通信,通常先创建一个管道,再通过fork()函数创建一个子进程,该子进程会继承父进程所创建的管道。(必须在系统调用fork()前调用pipe(),否则子进程不会继承父文件描述符)

a4c26d1e5885305701be709a3d33442f.png

实例一(父进程与子进程间的通信):pipe_rw.c

#include

#include

#include

#include

#include

#include

int main()

{

int pipe_fd[2];

pid_t pid;

char buf_r[100];

char* p_wbuf;

int r_num;

memset(buf_r,0,sizeof(buf_r));

//创建管道

if(pipe(pipe_fd)<0)

{

printf("pipe create

error!\n");

return

-1; }

//创建子进程

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

{

printf("\n");

close(pipe_fd[1]);

sleep(2);

if((r_num=read(pipe_fd[0],buf_r,100))>0)

{

printf("%d number read from the pipe is

%s\n",r_num,buf_r); } close(pipe_fd[0]);

exit(0);

}

else

if(pid>0)

{

close(pipe_fd[0]);

if(write(pipe_fd[1],"Helio ",6)!=-1)

printf("write1 Hello success!\n ");

if(write(pipe_fd[1],"pipe",5)!=-1)

printf("\nwrite2 pipe success!\n ");

close(pipe_fd[1]);

sleep(3);

waitpid(pid,NULL,0);

exit(0);

}

}

运行结果:

a4c26d1e5885305701be709a3d33442f.png

有名管道:

函数原型:int mkfifo(const char

*pathname,mode_t mode)

pathname:FIFO文件名

mode:属性(创建、读写等)

一旦创建了一个FIFO,就可以使用open打开它,一般的文件访问函数(close、read、write等)都是用于FIFO。

头文件:#inculde、#include

非阻塞标志:

打开FIFO时,被阻塞标志(O_NONBLOCK)将对后面的读写产生如下的影响:

1.没有使用O_NONBLOCK:访问要求无法满足时进行阻塞。如试图读取空的FIFO时,将导致进行阻塞。

2.使用O_NONBLOCK:访问要求无法满足时不进行阻塞,立刻出错返回,errno是ENXIO.

实例二(使用有名管道对FIFO进行读写):fifo_write.c fifo_read.c

fifo_read.c

#include

#include

#include

#include

#include

#include

#include

#include

#define FIFO "/tmp/myfifo"

main(int argc,char **argv)

{

char buf_r[100];

int fd;

int nread;

//创建管道

if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))

printf("can't create fifoserver!\n");

printf("preparing fof reading bytes...\n");

memset(buf_r,0,sizeof(buf_r));

//打开管道

fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);

if(fd==-1)

{

perror("open");

exit(1);

}

while(1)

{

memset(buf_r,0,sizeof(buf_r));

if((nread=read(fd,buf_r,100))==-1)

{

if(errno==EAGAIN)

printf("no

data yet\n");

}

printf("read %s from FIFO\n",buf_r);

sleep(1);

}

pause();//暂停,等待信号

unlink(FIFO);

}

fifo_write.c

#include

#include

#include

#include

#include

#include

#include

#include

#define FIFO_SERVER "/tmp/myfifo"

main(int argc,char** argv)

{

int fd;

char w_buf[100];

int nwrite;

//打开管道

fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);

if(argc==1)

{

printf("please send something!\n");

exit(-1); }

strcpy(w_buf,argv[1]);

//向管道写入数据

if((nwrite=write(fd,w_buf,100))==-1)

{

if(errno==EAGAIN)

printf("the FIFO has not been

read yet.please try later!\n");

}

else

printf("write %s to the

FIFO\n",w_buf);

}

运行结果:

a4c26d1e5885305701be709a3d33442f.png

三、信号通信

信号(signal)机制是unix系统中作古老的通信机制,很多条件可以产生信号:

1.当用户按下某个按键时,产生信号;

2.硬件的异常产生心海:除数为0、无效的存储访问等。这些情况通常有硬件检测到,将其通知内核,然后内核产生适当的信号通知进程。

3.进程用kill函数将信号发送给另一个进程。

4.用户可用kill命令将信号发送给其他进程。

信号的类型:

1)SIGHUP 2)SIGINT 3)SIGQUIT 4)SIGILL 5)SIGTRAP 6)SIGIOT 7)SIGBUS 8)SIGFPE

9)SIGKILL 10)SIGUSR1 11)SIGSEGV 12)SIGSR2 13)SIGPIPE 14)SIGALRM 15)SIGTERM

16)SIGCHLD 17)SIGCONT 18)SIGSTOP 19)SIGTSTP 20)SIGTTIN 21)SIGTTOU 22)SIGURG

23)SIGXCPU 24)SIGXFSZ 25)SIGVTALRM 26)SIGPROF 27)SIGWINCH 28)SIGIO 29)SIGPWR

下面几种是常见的信号类型:

SIGHUP:从终端上发送的结束信号

SIGINT:来自键盘的中断信号(ctrl+C)

SIGKILL:该信号结束接受信号的进程

SIGCHLD:标识子进程停止或结束的信号

SIGSTOP:来自键盘(ctrl+z)或调试程序的停止执行信号

信号的处理:

当某种信号出现时,将按照下列三种方式中的一种进行处理:

1.忽略此信号 大多数信号都按照这种方式进行处理,但是有两种信号却决不能忽略,他们是:SIGKILL和SIGSTOP。不能被忽略的原因是:他们向超级用户提供了一种终止或停止进程的方法。

2.执行用户希望的动作 通知内核在某种信号发生时,调用一个用户的函数,在用户函数中,执行用户希望的处理。

3.执行系统默认的动作 多大多数信号的系统默认动作时终止该进程。

信号的发送:

发送型号的函数有kill和raise。他们的区别为:

kill既可以向自身发送信号,也可以向其他进程发送信号。raise函数只能向进程自身发送信号。

头文件和函数原型:

#include

#include

int kill(pid_t pid,int signo)

int raise(int signo)

kill的pid参数有四种不同的情况:

1.pid>0 将信号发送给进程ID为pid的进程;

2.pid==0 将进程发送给同组的进程;

3.pid<0 将信号发送给其他进程组ID等于pid绝对值的进程;

4.pid==-1 将信号发送给所有的进程。

ALARM函数:

使用alarm函数可以设置一个时间(闹钟时间),当达到了所设定的时间,产生SIGALARM信号。如果不捕捉此信号,则默认动作时终止该进程。

#include

unsigned int alarm(unsigned int seconds)

seconds:经过了指定的seconds秒后会产生信号SIGALARM.

每个进程只能有一个闹钟时间。如果在调用alarm,时以前为改进从设置过闹钟时间,而且他还没有超时,以前登记的闹钟时间将被新值所替换。如果新登记的时间为0,表示取消以前的闹钟设置。

Pause函数:

pause函数使调用进程挂起直至捕捉到一个信号。

#include

int pause(void)

只有执行了一个信号处理函数后,挂起才结束。

signal的函数原型:

#include

void (*signal(int

signo,void(*func)(int)))(int)

理解这个函数从下面2个函数来理解:

typedef void(*sighandler_t)(int)

sighandler_t signal(int signum,sighandler_t

handler)

Func的值可能是:

1.SIG_IGN:忽略此信号

2.SIG_DFL:按系统默认方式处理

3.信号处理函数名:使用指定的函数处理

函数实例三:mysignal.c

#include

#include

#include

void my_func(int sign_no)

{

if(sign_no==SIGINT)

printf("I have get SIGINT!\n");

else if(sign_no==SIGQUIT)

printf("I have get

SIGQUIT!\n"); }

int main()

{

printf("Waiting for signal SIGINT or

SIGQUIT:\n");

//注册信号处理函数

signal(SIGINT,my_func);

signal(SIGQUIT,my_func);

pause();

exit(0);

}

执行结果:

先执行该程序,然后再另一个终端中使用ps

aux进行进程号的查询,找到所执行程序的进程号,再执行命令:kill -s SIGQUIT 进程号

a4c26d1e5885305701be709a3d33442f.png

四、共享内存

共享内存是被多个进程共享的一部分内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就立即看到了内存的数据。

共享内存的实现分为两个步骤:

1.创建共享内存 使用shmget函数;

2.映射共享内存 将这段创建的共享内存映射到据图的进程空间去,使用shmat函数。

函数原型:

创建: int shmget(key_t hey,int

size,int shmflg)

key标识共享内存的键值:0/IPC_PRIVATE.当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共性内存,如果key的取值为0,而参数shmflg中又设置IPC_PRIVATE这个标志,则同样会创建一快新的共享内存。

返回值:如果成功,返回共享内存的标示符;如果不成功,返回-1.

映射:int shmat(int shmid,char

*shmaddr,int flag)

参数:1.shmid:shmget函数返回的共享内存的标示符;

2.flag:决定以什么样的方式来确定映射的地址(通常为0:让系统自动分配)

返回值:成功,则返回共享内存映射到进程中的地址;失败,返回-1.

脱离:int shmdt(char *shmaddr)

当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离。

函数实例四:sharemem.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define PERM S_IRUSR|S_IWUSR

int main(int argc,char **argv)

{

int shmid;

char *p_addr,*c_addr;

pid_t pid;

if(argc!=2)

{

fprintf(stderr,"Usage:%s\n\a",argv[0]);

exit(1); } //创建共享内存

if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1)

{

fprintf(stderr,"Create Share

Memory Error:",strerror(errno)); exit(1);

}

//创建子进程

//pid=fork();

if(fork())

{

p_addr=shmat(shmid,0,0);

memset(p_addr,'\0',1024);

strncpy(p_addr,argv[1],1024);

wait(NULL);//释放资源,不关心终止状态

exit(0); }

else

{

sleep(1);

c_addr=shmat(shmid,0,0);

printf("Client

get %s\n",c_addr);

exit(0); }

}

执行结果:

a4c26d1e5885305701be709a3d33442f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值