zoukankan      html  css  js  c++  java
  • Linux环境进程间通信(一)

    1、 管道概述及相关API应用

    1.1 管道相关的关键概念

    管道是Linux支持的最初Unix IPC形式之中的一个。具有下面特点:

    • 管道是半双工的,数据仅仅能向一个方向流动。须要两方通信时。须要建立起两个管道;
    • 仅仅能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
    • 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件。但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统。而且仅仅存在与内存中。
    • 数据的读出和写入:一个进程向管道中写的内容被管道还有一端的进程读出。

      写入的内容每次都加入在管道缓冲区的末尾,而且每次都是从缓冲区的头部读出数据。

    1.2管道的创建:

    #include <unistd.h>
    int pipe(int fd[2])

    该函数创建的管道的两端处于一个进程中间。在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程。然后通过管道实现父子进程间的通信(因此也不难推出,仅仅要两个进程中存在亲缘关系。这里的亲缘关系指的是具有共同的祖先。都能够採用管道方式来进行通信)。

    1.3管道的读写规则:

    管道两端可分别用描写叙述字fd[0]以及fd[1]来描写叙述,须要注意的是,管道的两端是固定了任务的。即一端仅仅能用于读,由描写叙述字fd[0]表示,称其为管道读端;还有一端则仅仅能用于写,由描写叙述字fd[1]来表示,称其为管道写端。假设试图从管道写端读取数据。或者向管道读端写入数据都将导致发生错误。一般文件的I/O函数都能够用于管道。如close、read、write等等。

    从管道中读取数据:

    • 假设管道的写端不存在。则觉得已经读到了数据的末尾,读函数返回的读出字节数为0;
    • 当管道的写端存在时,假设请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,假设请求的字节数目不大于PIPE_BUF。则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量)。或者返回请求的字节数(此时。管道中数据量不小于请求的数据量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本号可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red hat 7.2中为4096)。

    关于管道的读规则验证:

     /**************
     * readtest.c *
     **************/
    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    main()
    {
    	int pipe_fd[2];
    	pid_t pid;
    	char r_buf[100];
    	char w_buf[4];
    	char* p_wbuf;
    	int r_num;
    	int cmd;
    	
    	memset(r_buf,0,sizeof(r_buf));
    	memset(w_buf,0,sizeof(r_buf));
    	p_wbuf=w_buf;
    	if(pipe(pipe_fd)<0)
    	{
    		printf("pipe create error
    ");
    		return -1;
    	}
    	
    	if((pid=fork())==0)
    	{
    		printf("
    ");
    		close(pipe_fd[1]);
    		sleep(3);//确保父进程关闭写端
    	    r_num=read(pipe_fd[0],r_buf,100);
    printf(	"read num is %d   the data read from the pipe is %d
    ",r_num,atoi(r_buf));
    		
    		close(pipe_fd[0]);
    		exit();
    	}
    	else if(pid>0)
    	{
    	close(pipe_fd[0]);//read
    	strcpy(w_buf,"111");
    	if(write(pipe_fd[1],w_buf,4)!=-1)
    		printf("parent write over
    ");
    	close(pipe_fd[1]);//write
    		printf("parent close fd[1] over
    ");
    	sleep(10);
    	}	
    }
     /**************************************************
     * 程序输出结果:
     * parent write over
     * parent close fd[1] over
     * read num is 4   the data read from the pipe is 111
     * 附加结论:
     * 管道写端关闭后,写入的数据将一直存在,直到读出为止.
     ****************************************************/

    向管道中写入数据:

    • 向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空暇区域,写进程就会试图向管道写入数据。假设读进程不读走管道缓冲区中的数据。那么写操作将一直堵塞。 
      注:仅仅有在管道的读端存在时,向管道中写入数据才有意义。

      否则。向管道中写入数据的进程将收到内核传来的SIFPIPE信号。应用程序能够处理该信号,也能够忽略(默认动作则是应用程序终止)。

    对管道的写规则的验证1:写端对读端存在的依赖性

    #include <unistd.h>
    #include <sys/types.h>
    main()
    {
    	int pipe_fd[2];
    	pid_t pid;
    	char r_buf[4];
    	char* w_buf;
    	int writenum;
    	int cmd;
    	
    	memset(r_buf,0,sizeof(r_buf));
    	if(pipe(pipe_fd)<0)
    	{
    		printf("pipe create error
    ");
    		return -1;
    	}
    	
    	if((pid=fork())==0)
    	{
    		close(pipe_fd[0]);
    		close(pipe_fd[1]);
    		sleep(10);	
    		exit();
    	}
    	else if(pid>0)
    	{
    	sleep(1);  //等待子进程完毕关闭读端的操作
    	close(pipe_fd[0]);//write
    	w_buf="111";
    	if((writenum=write(pipe_fd[1],w_buf,4))==-1)
    		printf("write to pipe error
    ");
    	else	
    		printf("the bytes write to pipe is %d 
    ", writenum);
    	
    	close(pipe_fd[1]);
    	}	
    }

    则输出结果为: Broken pipe,原因就是该管道以及它的全部fork()产物的读端都已经被关闭。假设在父进程中保留读端,即在写完pipe后,再关闭父进程的读端,也会正常写入pipe,读者可自己验证一下该结论。

    因此。在向管道写入数据时,至少应该存在某一个进程,当中管道读端没有被关闭。否则就会出现上述错误(管道断裂,进程收到了SIGPIPE信号,默认动作是进程终止)

    对管道的写规则的验证2:linux不保证写管道的原子性验证

    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    main(int argc,char**argv)
    {
    	int pipe_fd[2];
    	pid_t pid;
    	char r_buf[4096];
    	char w_buf[4096*2];
    	int writenum;
    	int rnum;
    	memset(r_buf,0,sizeof(r_buf));	
    	if(pipe(pipe_fd)<0)
    	{
    		printf("pipe create error
    ");
    		return -1;
    	}
    	
    	if((pid=fork())==0)
    	{
    		close(pipe_fd[1]);
    		while(1)
    		{
    		sleep(1);	
    		rnum=read(pipe_fd[0],r_buf,1000);
    		printf("child: readnum is %d
    ",rnum);
    		}
    		close(pipe_fd[0]);
    		
    		exit();
    	}
    	else if(pid>0)
    	{
    	close(pipe_fd[0]);//write
    	memset(r_buf,0,sizeof(r_buf));	
    	if((writenum=write(pipe_fd[1],w_buf,1024))==-1)
    		printf("write to pipe error
    ");
    	else	
    		printf("the bytes write to pipe is %d 
    ", writenum);
    	writenum=write(pipe_fd[1],w_buf,4096);
    	close(pipe_fd[1]);
    	}	
    }
    输出结果:
    the bytes write to pipe 1000
    the bytes write to pipe 1000  //注意。此行输出说明了写入的非原子性
    the bytes write to pipe 1000
    the bytes write to pipe 1000
    the bytes write to pipe 1000
    the bytes write to pipe 120  //注意,此行输出说明了写入的非原子性
    the bytes write to pipe 0
    the bytes write to pipe 0
    ......

    结论:

    写入数目小于4096时写入是非原子的。 
    假设把父进程中的两次写入字节数都改为5000,则非常easy得出以下结论: 
    写入管道的数据量大于4096字节时,缓冲区的空暇空间将被写入数据(补齐)。直到写全然部数据为止。假设没有进程读数据,则一直堵塞。

    1.4管道应用实例:

    实例一:用于shell

    管道可用于输入输出重定向,它将一个命令的输出直接定向到还有一个命令的输入。比方,当在某个shell程序(Bourne shell或C shell等)键入who│wc -l后。对应shell程序将创建who以及wc两个进程和这两个进程间的管道。考虑以下的命令行:

    $kill -l 执行结果见 附一

    $kill -l | grep SIGRTMIN 执行结果例如以下:

    30) SIGPWR	31) SIGSYS	32) SIGRTMIN	33) SIGRTMIN+1
    34) SIGRTMIN+2	35) SIGRTMIN+3	36) SIGRTMIN+4	37) SIGRTMIN+5
    38) SIGRTMIN+6	39) SIGRTMIN+7	40) SIGRTMIN+8	41) SIGRTMIN+9
    42) SIGRTMIN+10	43) SIGRTMIN+11	44) SIGRTMIN+12	45) SIGRTMIN+13
    46) SIGRTMIN+14	47) SIGRTMIN+15	48) SIGRTMAX-15	49) SIGRTMAX-14

    实例二:用于具有亲缘关系的进程间通信

    以下样例给出了管道的详细应用,父进程通过管道发送一些命令给子进程。子进程解析命令,并依据命令作对应处理。

    #include <unistd.h>
    #include <sys/types.h>
    main()
    {
    	int pipe_fd[2];
    	pid_t pid;
    	char r_buf[4];
    	char** w_buf[256];
    	int childexit=0;
    	int i;
    	int cmd;
    	
    	memset(r_buf,0,sizeof(r_buf));
    	if(pipe(pipe_fd)<0)
    	{
    		printf("pipe create error
    ");
    		return -1;
    	}
    	if((pid=fork())==0)
    	//子进程:解析从管道中获取的命令。并作对应的处理
    	{
    		printf("
    ");
    		close(pipe_fd[1]);
    		sleep(2);
    		
    		while(!childexit)
    		{	
    			read(pipe_fd[0],r_buf,4);
    			cmd=atoi(r_buf);
    			if(cmd==0)
    			{
    printf("child: receive command from parent over
     now child process exit
    ");
    				childexit=1;
    			}
    			
    		       else if(handle_cmd(cmd)!=0)
    				return;
    			sleep(1);
    		}
    		close(pipe_fd[0]);
    		exit();
    	}
    	else if(pid>0)
    	//parent: send commands to child
    	{
    	close(pipe_fd[0]);
    	w_buf[0]="003";
    	w_buf[1]="005";
    	w_buf[2]="777";
    	w_buf[3]="000";
    	for(i=0;i<4;i++)
    		write(pipe_fd[1],w_buf[i],4);
    	close(pipe_fd[1]);
    	}	
    }
    //以下是子进程的命令处理函数(特定于应用):
    int handle_cmd(int cmd)
    {
    if((cmd<0)||(cmd>256))
    //suppose child only support 256 commands
    	{
    	printf("child: invalid command 
    ");
    	return -1;
    	}
    printf("child: the cmd from parent is %d
    ", cmd);
    return 0;
    }

    1.5管道的局限性

    管道的主要局限性正体如今它的特点上:

    • 仅仅支持单向数据流;
    • 仅仅能用于具有亲缘关系的进程之间;
    • 没有名字;
    • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时。为缓冲区分配一个页面大小)。
    • 管道所传送的是无格式字节流。这就要求管道的读出方和写入方必须事先约定好数据的格式。比方多少字节算作一个消息(或命令、或记录)等等;

    2、 有名管道概述及相关API应用

    2.1 有名管道相关的关键概念

    管道应用的一个重大限制是它没有名字。因此,仅仅能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样。即使与FIFO的创建进程不存在亲缘关系的进程。仅仅要可以訪问该路径,就行彼此通过FIFO相互通信(可以訪问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。

    值得注意的是。FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从開始处返回数据。对它们的写则把数据加入到末尾。它们不支持诸如lseek()等文件定位操作。

    2.2有名管道的创建

    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char * pathname, mode_t mode)

    该函数的第一个參数是一个普通的路径名,也就是创建后FIFO的名字。

    第二个參数与打开普通文件的open()函数中的mode 參数同样。 假设mkfifo的第一个參数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,假设确实返回该错误,那么仅仅要调用打开FIFO的函数就能够了。一般文件的I/O函数都能够用于FIFO,如close、read、write等等。

    2.3有名管道的打开规则

    有名管道比管道多了一个打开操作:open。

    FIFO的打开规则:

    假设当前打开操作是为读而打开FIFO时。若已经有对应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能堵塞直到有对应进程为写而打开该FIFO(当前打开操作设置了堵塞标志)。或者,成功返回(当前打开操作没有设置堵塞标志)。

    假设当前打开操作是为写而打开FIFO时,假设已经有对应进程为读而打开该FIFO。则当前打开操作将成功返回;否则,可能堵塞直到有对应进程为读而打开该FIFO(当前打开操作设置了堵塞标志);或者,返回ENXIO错误(当前打开操作没有设置堵塞标志)。

    对打开规则的验证參见 附2

    2.4有名管道的读写规则

    从FIFO中读取数据:

    约定:假设一个进程为了从FIFO中读取数据而堵塞打开FIFO,那么称该进程内的读操作为设置了堵塞标志的读操作。

    • 假设有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了堵塞标志的读操作来说,将一直堵塞。对于没有设置堵塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。
    • 对于设置了堵塞标志的读操作说,造成堵塞的原因有两种:当前FIFO内有数据。但有其他进程在读这些数据;另外就是FIFO内没有数据。解堵塞的原因则是FIFO中有新的数据写入。不论信写入数据量的大小,也不论读操作请求多少数据量。

    • 读打开的堵塞标志仅仅对本进程第一个读操作施加作用。假设本进程内有多个读操作序列。则在第一个读操作被唤醒并完毕读操作后。其他将要运行的读操作将不再堵塞。即使在运行读操作时。FIFO中没有数据也一样(此时,读操作返回0)。
    • 假设没有进程写打开FIFO,则设置了堵塞标志的读操作会堵塞。

    注:假设FIFO中有数据,则设置了堵塞标志的读操作不会由于FIFO中的字节数小于请求读的字节数而堵塞,此时。读操作会返回FIFO中现有的数据量。

    向FIFO中写入数据:

    约定:假设一个进程为了向FIFO中写入数据而堵塞打开FIFO。那么称该进程内的写操作为设置了堵塞标志的写操作。

    对于设置了堵塞标志的写操作:

    • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。假设此时管道空暇缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中可以容纳要写入的字节数时。才開始进行一次性写操作。

    • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

      FIFO缓冲区一有空暇区域。写进程就会试图向管道写入数据。写操作在写全然部请求写的数据后返回。

    对于没有设置堵塞标志的写操作:

    • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

      在写满全部FIFO空暇缓冲区后,写操作返回。

    • 当要写入的数据量不大于PIPE_BUF时。linux将保证写入的原子性。假设当前FIFO空暇缓冲区可以容纳请求写入的字节数,写完后成功返回;假设当前FIFO空暇缓冲区不可以容纳请求写入的字节数。则返回EAGAIN错误。提醒以后再写;

    对FIFO读写规则的验证:

    以下提供了两个对FIFO的读敲代码,适当调节程序中的非常少地方或者程序的命令行參数就能够对各种FIFO读写规则进行验证。

    程序1:写FIFO的程序
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    #define FIFO_SERVER "/tmp/fifoserver"
    main(int argc,char** argv)
    //參数为即将写入的字节数
    {
    	int fd;
    	char w_buf[4096*2];
    	int real_wnum;
    	memset(w_buf,0,4096*2);
    	if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
    		printf("cannot create fifoserver
    ");
    	if(fd==-1)
    		if(errno==ENXIO)
    			printf("open error; no reading process
    ");
    		
         	fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
    	//设置非堵塞标志
    	//fd=open(FIFO_SERVER,O_WRONLY,0);
    	//设置堵塞标志
    	real_wnum=write(fd,w_buf,2048);
    	if(real_wnum==-1)
    	{
    		if(errno==EAGAIN)
    			printf("write to fifo error; try later
    ");
    	}
    	else 
    		printf("real write num is %d
    ",real_wnum);
    	real_wnum=write(fd,w_buf,5000);
    	//5000用于測试写入字节大于4096时的非原子性
    	//real_wnum=write(fd,w_buf,4096);
    	//4096用于測试写入字节不大于4096时的原子性
    	
    	if(real_wnum==-1)
    		if(errno==EAGAIN)
    			printf("try later
    ");
    }
    程序2:与程序1一起測试写FIFO的规则,第一个命令行參数是请求从FIFO读出的字节数
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    #define FIFO_SERVER "/tmp/fifoserver"
    main(int argc,char** argv)
    {
    	char r_buf[4096*2];
    	int  fd;
    	int  r_size;
    	int  ret_size;
    	r_size=atoi(argv[1]);
    	printf("requred real read bytes %d
    ",r_size);
    	memset(r_buf,0,sizeof(r_buf));
    	fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);
    	//fd=open(FIFO_SERVER,O_RDONLY,0);
    	//在此处能够把读程序编译成两个不同版本号:堵塞版本号及非堵塞版本号
    	if(fd==-1)
    	{
    		printf("open %s for read error
    ");
    		exit();	
    	}
    	while(1)
    	{
    		
    		memset(r_buf,0,sizeof(r_buf));
    		ret_size=read(fd,r_buf,r_size);
    		if(ret_size==-1)
    			if(errno==EAGAIN)
    				printf("no data avlaible
    ");
    		printf("real read bytes %d
    ",ret_size);
    		sleep(1);
    	}	
    	pause();
    	unlink(FIFO_SERVER);
    }

    程序应用说明:

    把读程序编译成两个不同版本号:

    • 堵塞读版本号:br
    • 以及非堵塞读版本号nbr

    把敲代码编译成两个四个版本号:

    • 非堵塞且请求写的字节数大于PIPE_BUF版本号:nbwg
    • 非堵塞且请求写的字节数不大于PIPE_BUF版本号:版本号nbw
    • 堵塞且请求写的字节数大于PIPE_BUF版本号:bwg
    • 堵塞且请求写的字节数不大于PIPE_BUF版本号:版本号bw

    以下将使用br、nbr、w取代对应程序中的堵塞读、非堵塞读

    验证堵塞写操作:

    1. 当请求写入的数据量大于PIPE_BUF时的非原子性:
      • nbr 1000
      • bwg
    2. 当请求写入的数据量不大于PIPE_BUF时的原子性:
      • nbr 1000
      • bw

    验证非堵塞写操作:

    1. 当请求写入的数据量大于PIPE_BUF时的非原子性:
      • nbr 1000
      • nbwg
    2. 请求写入的数据量不大于PIPE_BUF时的原子性:
      • nbr 1000
      • nbw

    无论写打开的堵塞标志是否设置。在请求写入的字节数大于4096时。都不保证写入的原子性。但二者有本质差别:

    对于堵塞写来说,写操作在写满FIFO的空暇区域后,会一直等待。直到写全然部数据为止,请求写入的数据终于都会写入FIFO;

    而非堵塞写则在写满FIFO的空暇区域后。就返回(实际写入的字节数),所以有些数据终于不可以写入。

    对于读操作的验证则比較简单,不再讨论。

    2.5有名管道应用实例

    在验证了对应的读写规则后。应用实例似乎就没有必要了。

    小结:

    管道经常使用于两个方面:(1)在shell中时常会用到管道(作为输入输入的重定向),在这样的应用方式下,管道的创建对于用户来说是透明的;(2)用于具有亲缘关系的进程间通信。用户自己创建管道,并完毕读写操作。

    FIFO能够说是管道的推广,克服了管道无名字的限制,使得无亲缘关系的进程相同能够採用先进先出的通信机制进行通信。

    管道和FIFO的数据是字节流,应用程序之间必须事先确定特定的传输"协议",採用传播具有特定意义的消息。

    要灵活应用管道及FIFO。理解它们的读写规则是关键。

    附1:kill -l 的执行结果,显示了当前系统支持的全部信号:

    1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL
    5) SIGTRAP	 6) SIGABRT	 7) SIGBUS	 8) SIGFPE
    9) SIGKILL	10) SIGUSR1	11) SIGSEGV	12) SIGUSR2
    13) SIGPIPE	14) SIGALRM	15) SIGTERM	17) SIGCHLD
    18) SIGCONT	19) SIGSTOP	20) SIGTSTP	21) SIGTTIN
    22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
    26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO
    30) SIGPWR	31) SIGSYS	32) SIGRTMIN	33) SIGRTMIN+1
    34) SIGRTMIN+2	35) SIGRTMIN+3	36) SIGRTMIN+4	37) SIGRTMIN+5
    38) SIGRTMIN+6	39) SIGRTMIN+7	40) SIGRTMIN+8	41) SIGRTMIN+9
    42) SIGRTMIN+10	43) SIGRTMIN+11	44) SIGRTMIN+12	45) SIGRTMIN+13
    46) SIGRTMIN+14	47) SIGRTMIN+15	48) SIGRTMAX-15	49) SIGRTMAX-14
    50) SIGRTMAX-13	51) SIGRTMAX-12	52) SIGRTMAX-11	53) SIGRTMAX-10
    54) SIGRTMAX-9	55) SIGRTMAX-8	56) SIGRTMAX-7	57) SIGRTMAX-6
    58) SIGRTMAX-5	59) SIGRTMAX-4	60) SIGRTMAX-3	61) SIGRTMAX-2
    62) SIGRTMAX-1	63) SIGRTMAX

    除了在此处用来说明管道应用外,接下来的专题还要对这些信号分类讨论。

    附2:对FIFO打开规则的验证(主要验证写打开对读打开的依赖性)

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    #define FIFO_SERVER "/tmp/fifoserver"
    int handle_client(char*);
    main(int argc,char** argv)
    {
    	int r_rd;
    	int w_fd;
    	pid_t pid;
    	if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
    		printf("cannot create fifoserver
    ");
    	handle_client(FIFO_SERVER);
    	
    }
    int handle_client(char* arg)
    {
    int ret;
    ret=w_open(arg);
    switch(ret)
    {
    	case 0:
    	{	
    	printf("open %s error
    ",arg);
    	printf("no process has the fifo open for reading
    ");
    	return -1;
    	}
    	case -1:
    	{
    		printf("something wrong with open the fifo except for ENXIO");
    		return -1;
    	}
    	case 1:
    	{
    		printf("open server ok
    ");
    		return 1;
    	}
    	default:
    	{
    		printf("w_no_r return ----
    ");
    		return 0;
    	}
    }		
    unlink(FIFO_SERVER);
    }
    int w_open(char*arg)
    //0  open error for no reading
    //-1 open error for other reasons
    //1  open ok
    {
    	if(open(arg,O_WRONLY|O_NONBLOCK,0)==-1)
    	{	if(errno==ENXIO)
    		{
    			return 0;
    		}
    		else
    		return -1;
    	}
    	return 1;
    	
    }
  • 相关阅读:
    生活中头疼脑热及医生诊断用药相关,持续更新
    python3 面试题 英文单词全部都是以首字母大写,逐个反转每个单词
    python 代码如何打包成.exe文件(Pyinstaller)
    charles使用
    经典bug
    python3面试-查找字符串数组中的最长公共前缀
    python3面试题 按规律写出下一个数1,11,21,1211,111221
    python3 测试的时候如何批量随机生成伪数据?(faker模块的)
    python3面试题-一个包含n个整数的数组a,判断a中是否存在三个元素,a,b,c,使得a+b+c=0
    python3面试-将N(N<10000)个人排成一排,从第1个人开始报数;如果报数是M的倍数就出列
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7338732.html
Copyright © 2011-2022 走看看