unix系统发出警报 - CSDN
  • UNIX再学习 -- 错误和警告

    千次阅读 2017-03-20 17:00:11
    错误和警告是常会出现的现象,了解它对以后解决问题会很有帮助。下面我们就重点来详细介绍它们。一、错误

    错误和警告是常会出现的现象,了解它对以后解决问题会很有帮助。下面我们就重点来详细介绍它们。

    一、错误

    1、回忆错误

    我们之前讲解其他内容时有涉及到错误的部分,下面让我们来回忆一下:

    (1)参看:C语言再学习 -- C 预处理器

    #error  字符串 => 表示产生一个错误信息
    #warning 字符串 => 表示产生一个警告信息

    //#error和#warning的使用  
    #include <stdio.h>  
      
    #define VERSION 4  
    #define VERSION 2  
    #define VERSION 3  
    #if(VERSION < 3)  
        #error "版本过低"  
    #elif(VERSION > 3)  
        #warning "版本过高"  
    #endif  
      
    int main(void)  
    {  
        printf("程序正常运行\n");  
        return 0;  
    }  
    输出结果:  
    警告: #warning "版本过高"  
    //错误: #error "版本过低"  
    //程序正常运行  

    (2)参看:C语言再学习 -- 关键字return和exit ()函数

    C语言中通过使用返回来表示是否出错,根据返回值来进行具体的错误处理一般规则:
    1)如果返回值类型时int类型,并且返回的值不可能是负数时,则使用返回值-1代表出错,其他数据表示正常返回。
    2)如果返回值类型时int类型,并且返回的值可能是负数时,则需要使用指针取出返回值的数据,返回值仅仅表示是否出错,-1表示出错,0表示正常返回。
    3)如果返回值类型是指针类型,则返回值NULL代表出错。
    4)如果不考虑是否出错,返回值类型使用void即可。

    (3)参看:C语言再学习 -- 文件

    stderr -- 标准错误输出设备,例如:

    fprintf(stderr, "Can't open it!\n");

    (4)C语言再学习 -- EOF、feof函数、ferror函数

    ferror () 函数用法。

    2、errno 错误代码

    经常在调用 linux 系统 api 的时候会出现一些错误,比方说使用open () write () creat () 之类的函数有些时候会返回 -1,也就是调用失败,这个时候往往需要知道失败的原因。UNIX/Linux 为我们提供了外部全局变量 errno,当函数调用失败,会将具体的错误编号设置到 errno ,我们可以通过 errno 来获取错误的原因。下面我们来介绍 errno。

    (1)介绍errno

    error 全局变量在 error.h 头文件定义,extern int errno

     在文件 /usr/include/errno.h 
    /* Declare the `errno' variable, unless it's defined as a macro by
       bits/errno.h.  This is the case in GNU, where it is a per-thread
       variable.  This redeclaration using the macro still works, but it
       will be a function declaration without a prototype and may trigger
       a -Wstrict-prototypes warning.  */
    #ifndef errno
    extern int errno;
    #endif
    错误 Exx 的宏定义在 /usr/include/asm-generic 文件夹下面的  errno-base.h 和 errno.h,分别定义了 1-34 、35-132 的错误定义。

    查看 /usr/include/asm-generic/errno-base.h
    #ifndef _ASM_GENERIC_ERRNO_BASE_H
    #define _ASM_GENERIC_ERRNO_BASE_H
    
    #define	EPERM		 1	/* Operation not permitted */
    #define	ENOENT		 2	/* No such file or directory */
    #define	ESRCH		 3	/* No such process */
    #define	EINTR		 4	/* Interrupted system call */
    #define	EIO		 5	/* I/O error */
    #define	ENXIO		 6	/* No such device or address */
    #define	E2BIG		 7	/* Argument list too long */
    #define	ENOEXEC		 8	/* Exec format error */
    #define	EBADF		 9	/* Bad file number */
    #define	ECHILD		10	/* No child processes */
    #define	EAGAIN		11	/* Try again */
    #define	ENOMEM		12	/* Out of memory */
    #define	EACCES		13	/* Permission denied */
    #define	EFAULT		14	/* Bad address */
    #define	ENOTBLK		15	/* Block device required */
    #define	EBUSY		16	/* Device or resource busy */
    #define	EEXIST		17	/* File exists */
    #define	EXDEV		18	/* Cross-device link */
    #define	ENODEV		19	/* No such device */
    #define	ENOTDIR		20	/* Not a directory */
    #define	EISDIR		21	/* Is a directory */
    #define	EINVAL		22	/* Invalid argument */
    #define	ENFILE		23	/* File table overflow */
    #define	EMFILE		24	/* Too many open files */
    #define	ENOTTY		25	/* Not a typewriter */
    #define	ETXTBSY		26	/* Text file busy */
    #define	EFBIG		27	/* File too large */
    #define	ENOSPC		28	/* No space left on device */
    #define	ESPIPE		29	/* Illegal seek */
    #define	EROFS		30	/* Read-only file system */
    #define	EMLINK		31	/* Too many links */
    #define	EPIPE		32	/* Broken pipe */
    #define	EDOM		33	/* Math argument out of domain of func */
    #define	ERANGE		34	/* Math result not representable */
    
    #endif
    查看 /usr/include/asm-generic/errno.h
    #ifndef _ASM_GENERIC_ERRNO_H
    #define _ASM_GENERIC_ERRNO_H
    
    #include <asm-generic/errno-base.h>
    
    #define	EDEADLK		35	/* Resource deadlock would occur */
    #define	ENAMETOOLONG	36	/* File name too long */
    #define	ENOLCK		37	/* No record locks available */
    #define	ENOSYS		38	/* Function not implemented */
    #define	ENOTEMPTY	39	/* Directory not empty */
    #define	ELOOP		40	/* Too many symbolic links encountered */
    #define	EWOULDBLOCK	EAGAIN	/* Operation would block */
    #define	ENOMSG		42	/* No message of desired type */
    #define	EIDRM		43	/* Identifier removed */
    #define	ECHRNG		44	/* Channel number out of range */
    #define	EL2NSYNC	45	/* Level 2 not synchronized */
    #define	EL3HLT		46	/* Level 3 halted */
    #define	EL3RST		47	/* Level 3 reset */
    #define	ELNRNG		48	/* Link number out of range */
    #define	EUNATCH		49	/* Protocol driver not attached */
    #define	ENOCSI		50	/* No CSI structure available */
    #define	EL2HLT		51	/* Level 2 halted */
    #define	EBADE		52	/* Invalid exchange */
    #define	EBADR		53	/* Invalid request descriptor */
    #define	EXFULL		54	/* Exchange full */
    #define	ENOANO		55	/* No anode */
    #define	EBADRQC		56	/* Invalid request code */
    #define	EBADSLT		57	/* Invalid slot */
    
    #define	EDEADLOCK	EDEADLK
    
    #define	EBFONT		59	/* Bad font file format */
    #define	ENOSTR		60	/* Device not a stream */
    #define	ENODATA		61	/* No data available */
    #define	ETIME		62	/* Timer expired */
    #define	ENOSR		63	/* Out of streams resources */
    #define	ENONET		64	/* Machine is not on the network */
    #define	ENOPKG		65	/* Package not installed */
    #define	EREMOTE		66	/* Object is remote */
    #define	ENOLINK		67	/* Link has been severed */
    #define	EADV		68	/* Advertise error */
    #define	ESRMNT		69	/* Srmount error */
    #define	ECOMM		70	/* Communication error on send */
    #define	EPROTO		71	/* Protocol error */
    #define	EMULTIHOP	72	/* Multihop attempted */
    #define	EDOTDOT		73	/* RFS specific error */
    #define	EBADMSG		74	/* Not a data message */
    #define	EOVERFLOW	75	/* Value too large for defined data type */
    #define	ENOTUNIQ	76	/* Name not unique on network */
    #define	EBADFD		77	/* File descriptor in bad state */
    #define	EREMCHG		78	/* Remote address changed */
    #define	ELIBACC		79	/* Can not access a needed shared library */
    #define	ELIBBAD		80	/* Accessing a corrupted shared library */
    #define	ELIBSCN		81	/* .lib section in a.out corrupted */
    #define	ELIBMAX		82	/* Attempting to link in too many shared libraries */
    #define	ELIBEXEC	83	/* Cannot exec a shared library directly */
    #define	EILSEQ		84	/* Illegal byte sequence */
    #define	ERESTART	85	/* Interrupted system call should be restarted */
    #define	ESTRPIPE	86	/* Streams pipe error */
    #define	EUSERS		87	/* Too many users */
    #define	ENOTSOCK	88	/* Socket operation on non-socket */
    #define	EDESTADDRREQ	89	/* Destination address required */
    #define	EMSGSIZE	90	/* Message too long */
    #define	EPROTOTYPE	91	/* Protocol wrong type for socket */
    #define	ENOPROTOOPT	92	/* Protocol not available */
    #define	EPROTONOSUPPORT	93	/* Protocol not supported */
    #define	ESOCKTNOSUPPORT	94	/* Socket type not supported */
    #define	EOPNOTSUPP	95	/* Operation not supported on transport endpoint */
    #define	EPFNOSUPPORT	96	/* Protocol family not supported */
    #define	EAFNOSUPPORT	97	/* Address family not supported by protocol */
    #define	EADDRINUSE	98	/* Address already in use */
    #define	EADDRNOTAVAIL	99	/* Cannot assign requested address */
    #define	ENETDOWN	100	/* Network is down */
    #define	ENETUNREACH	101	/* Network is unreachable */
    #define	ENETRESET	102	/* Network dropped connection because of reset */
    #define	ECONNABORTED	103	/* Software caused connection abort */
    #define	ECONNRESET	104	/* Connection reset by peer */
    #define	ENOBUFS		105	/* No buffer space available */
    #define	EISCONN		106	/* Transport endpoint is already connected */
    #define	ENOTCONN	107	/* Transport endpoint is not connected */
    #define	ESHUTDOWN	108	/* Cannot send after transport endpoint shutdown */
    #define	ETOOMANYREFS	109	/* Too many references: cannot splice */
    #define	ETIMEDOUT	110	/* Connection timed out */
    #define	ECONNREFUSED	111	/* Connection refused */
    #define	EHOSTDOWN	112	/* Host is down */
    #define	EHOSTUNREACH	113	/* No route to host */
    #define	EALREADY	114	/* Operation already in progress */
    #define	EINPROGRESS	115	/* Operation now in progress */
    #define	ESTALE		116	/* Stale NFS file handle */
    #define	EUCLEAN		117	/* Structure needs cleaning */
    #define	ENOTNAM		118	/* Not a XENIX named type file */
    #define	ENAVAIL		119	/* No XENIX semaphores available */
    #define	EISNAM		120	/* Is a named type file */
    #define	EREMOTEIO	121	/* Remote I/O error */
    #define	EDQUOT		122	/* Quota exceeded */
    
    #define	ENOMEDIUM	123	/* No medium found */
    #define	EMEDIUMTYPE	124	/* Wrong medium type */
    #define	ECANCELED	125	/* Operation Canceled */
    #define	ENOKEY		126	/* Required key not available */
    #define	EKEYEXPIRED	127	/* Key has expired */
    #define	EKEYREVOKED	128	/* Key has been revoked */
    #define	EKEYREJECTED	129	/* Key was rejected by service */
    
    /* for robust mutexes */
    #define	EOWNERDEAD	130	/* Owner died */
    #define	ENOTRECOVERABLE	131	/* State not recoverable */
    
    #define ERFKILL		132	/* Operation not possible due to RF-kill */
    
    #define EHWPOISON	133	/* Memory page has hardware error */
    
    #endif
    还有一些更大的错误号是留给内核级别的,如系统调用等,用户程序一般是看不见的这些号的,Ubuntu12.04中/usr/src/linux-headers-3.2.0-23-generic-pae/include/linux/errno.h 

    查看 /usr/src/linux-headers-3.2.0-23-generic-pae/include/linux/errno.h 
    #ifndef _LINUX_ERRNO_H
    #define _LINUX_ERRNO_H
    
    #include <asm/errno.h>
    
    #ifdef __KERNEL__
    
    /*
     * These should never be seen by user programs.  To return one of ERESTART*
     * codes, signal_pending() MUST be set.  Note that ptrace can observe these
     * at syscall exit tracing, but they will never be left for the debugged user
     * process to see.
     */
    #define ERESTARTSYS	512
    #define ERESTARTNOINTR	513
    #define ERESTARTNOHAND	514	/* restart if no handler.. */
    #define ENOIOCTLCMD	515	/* No ioctl command */
    #define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
    
    /* Defined for the NFSv3 protocol */
    #define EBADHANDLE	521	/* Illegal NFS file handle */
    #define ENOTSYNC	522	/* Update synchronization mismatch */
    #define EBADCOOKIE	523	/* Cookie is stale */
    #define ENOTSUPP	524	/* Operation is not supported */
    #define ETOOSMALL	525	/* Buffer or request is too small */
    #define ESERVERFAULT	526	/* An untranslatable error occurred */
    #define EBADTYPE	527	/* Type not supported by server */
    #define EJUKEBOX	528	/* Request initiated, but will not complete before timeout */
    #define EIOCBQUEUED	529	/* iocb queued, will get completion event */
    #define EIOCBRETRY	530	/* iocb queued, will trigger a retry */
    
    #endif
    
    #endif

    (2)errno 转换成 字符串

    1)strerror 函数

    #include <string.h>
    char *strerror(int errnum);
    函数功能:
    主要用于将参数指定的错误编号翻译成对应的错误信息返回。
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    extern int errno ;
    
    int main ()
    {
       FILE *fp;
       // file.txt 不存在
       fp = fopen("file.txt", "r");
       if( fp == NULL ) 
       {
          fprintf(stderr, "错误码: %d\n", errno);
          fprintf(stderr, "对应错误信息为: %s\n", strerror(errno));
       }
       else 
       {
          fclose(fp);
       }
       
       return(0);
    }
    输出结果:
    错误码: 2
    对应错误信息为: No such file or directory

    2)perror 函数 (重点)

    #include <stdio.h>
    void perror(const char *s);
    函数功能:
    表示将最后一个错误信息打印出来,参数 s 不为空时原样输出,后面追加一个冒号和空格,再跟着错误信息,以及换行。
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    extern int errno ;
    
    int main (void)
    {
    	FILE *fp;
    	// file.txt 不存在
    	fp = fopen("file.txt", "r");   
    	if( fp == NULL )   
    	{    
    		printf("错误码 = %d\n",errno);   
    		perror ("打开失败"), exit (-1);   
    	}   
    	else    
    	{      
    		fclose(fp);   
    	}      
    	return(0);
    }
    输出结果:
    错误码 = 2
    打开失败: No such file or directory

    3)printf 函数

    使用 printf("%m\n");  
    %m 格式化标记打印错误信息 
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    extern int errno ;
    
    int main (void)
    {
    	FILE *fp;
    	// file.txt 不存在
    	fp = fopen("file.txt", "r");   
    	if( fp == NULL )   
    	{    
    		printf("错误码 = %d\n",errno);   
    		printf ("%m\n");   
    	}   
    	else    
    	{      
    		fclose(fp);   
    	}      
    	return(0);
    }
    输出结果:
    错误码 = 2
    No such file or directory

    (3)不能根据错误号判断是否出错

    虽然所有的错误号都不是零,但是因为函数执行成功的情况下错误号全局变量 errno 不会被修改,所以不能用该变量的值为零或非零,作为出错或没出错的判断依据。例如:
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    extern int errno ;
    
    int main ()
    {
       FILE *fp;
       // file.txt 不存在	
       fp = fopen("file.txt", "r");
    
       FILE *fp_test;
       // test.txt 存在	
       fp_test = fopen("test.txt", "r");
       if(errno)  //不能根据 errno 判断是否出错
       { 
    	   fprintf (stderr, "打开失败\n");
       }
       else 
       {
          fclose(fp_test);
       }
       
       return(0);
    }
    输出结果:
    打开失败
    上述例子,就可以看出本来不应该打印 "打开失败" 的。
    如果非要使用错误号判断是否出错也可以,那你在调用它之前必须手动将errno清零
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    extern int errno ;
    
    int main ()
    {
       FILE *fp;
       // file.txt 不存在	
       fp = fopen("file.txt", "r");
    
       errno = 0;  //将 errno 清零
       FILE *fp_test;
       // test.txt 存在	
       fp_test = fopen("test.txt", "r");
       if(errno)  //不能根据 errno 判断是否出错
       { 
    	   fprintf (stderr, "打开失败\n");
       }
       else 
       {
          fclose(fp_test);
       }
       
       return(0);
    }
    正确的做法是,先根据函数的返回值判断是否出错,在确定出错的前提下再根据 errno 的值判断具体出了什么错。
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    extern int errno ;
    
    int main ()
    {
       FILE *fp;
       // file.txt 不存在
       fp = fopen("file.txt", "r");
       if( fp == NULL ) 
       {
          fprintf(stderr, "错误码: %d\n", errno);
          fprintf(stderr, "对应错误信息为: %s\n", strerror(errno));
       }
       else 
       {
          fclose(fp);
       }
       
       return(0);
    }
    输出结果:
    错误码: 2
    对应错误信息为: No such file or directory

    二、编译错误和警告

    1、上面提到的 errno 是标准库函数的错误代码,现在来看看gcc编译错误和警告

    (1)让所有编译警告都显示出来,选项 -Wall

    如下,编辑一段警告的代码
    #include <stdio.h>  
      
    int main (void)  
    {  
        int i;  
        printf ("\n hello world![i]\n", i);  
        return 0;  
    }  
      
    root@ubuntu:/home/tarena/project/c_test# gcc -Wall hello.c -o hello  
    hello.c: 在函数‘main’中:  
    hello.c:6:2: 警告: 提供给格式字符串的实参太多 [-Wformat-extra-args]  
    hello.c:6:9: 警告: 此函数中的‘i’在使用前未初始化 [-Wuninitialized]  

    (2)将编译警告转换成错误的选项 -Werror

    编译警告很多时候会被我们忽视,在特殊场合我们还是需要重视编译警告的,如果能把编译警告变成直接输出错误,那我们的重视程度会提高很多并去解决。
    #include <stdio.h>  
      
    int main (void)  
    {  
        int i;  
        printf ("\n hello world![i]\n", i);  
        return 0;  
    }  
      
    root@ubuntu:/home/tarena/project/c_test# gcc -Wall -Werror hello.c  
    hello.c: 在函数‘main’中:  
    hello.c:6:2: 错误: 提供给格式字符串的实参太多 [-Werror=format-extra-args]  
    cc1: all warnings being treated as errors  

    (3)警告级别

    如果你觉得警告级别不够,可以使用更高的警告级别。
    参看:Options to Request or Suppress Warnings

    2、C语言编译错误及警告对照表 

    3、将警告,错误等信息输出到文件中

    (1)其中标准输出、标准输出、标准错误,可参看:C语言再学习 -- 文件

    C 程序自动打开3个文件。这3个文件被称为标准输入,标准输出和标准错误输出。默认的标准输入是系统的一般输入设备,通常为键盘;默认的标准输出和标准错误输出是系统的一般输出设备,通常为显示器,分别得到文件描述符 0, 1, 2.

    下面的方法从标准输入(键盘)获得一个字符:  ch = getchar ( );

    标准文件指针:

    stdio.h文件把3个文件指针与3个C 程序自动打开的标准文件进行了并联,如下表所示:

    标准文件  

    文件指针  

    一般使用的设备  

    标准输入

    stdin

    键盘

    标准输出

    stdout

    显示器

    标准错误

    stderr

    显示器

    这些指针都是FILE指针类型,所以可以被用作标准I/O函数的参数。

    (2)下面以make命令为例来说明,如何把对应的信息,输出到对应的文件中:

    【用法】
    1)想要把make输出的全部信息,输出到某个文件中,最常见的办法就是:
    make xxx > build_output.txt
    此时默认情况是没有改变2=stderr的输出方式,还是屏幕,所以,如果有错误信息,还是可以在屏幕上看到的。

    2)只需要把make输出中的错误(及警告)信息输出到文件中ing,可以用:
    make xxx 2> build_output.txt
    相应地,由于1=stdout没有变,还是屏幕,所以,那些命令执行时候输出的正常信息,还是会输出到屏幕上,你还是可以在屏幕上看到的。
    3)只需要把make输出中的正常(非错误,非警告)的信息输出到文件中,可以用:
    make xxx 1> build_output.txt
    相应地,由于2=stderr没有变,还是屏幕,所以,那些命令执行时候输出的错误信息,还是会输出到屏幕上,你还是可以在屏幕上看到的。
    4)想要把正常输出信息和错误信息输出到分别的文件中,可以用:
    make xxx 1> build_output_normal.txt 2>build_output_error.txt
    即联合使用了1和2,正常信息和错误信息,都输出到对应文件中了。
    5)所有的信息都输出到同一个文件中:
    make xxx > build_output_all.txt 2>&1
    其中的2>&1表示错误信息输出到&1中,而&1,指的是前面的那个文件:build_output_all.txt 。
    注意:上面所有的1,2等数字,后面紧跟着大于号'>' ,中间不能有空格。

    展开全文
  • Cherry(bc 作者之一)论证了 UNIX 的基础原则之一是让用户利用现有的实用程序来定制和创建复杂的工具。 世界上对 shell 脚本最好的概念性介绍来自一个老的 AT&T 培训视频 。在视
    导读 世界上对 shell 脚本最好的概念性介绍来自一个老的 AT&T 培训视频 。在视频中,Brian W. Kernighan(awk 中的“k”),Lorinda L. Cherry(bc 作者之一)论证了 UNIX 的基础原则之一是让用户利用现有的实用程序来定制和创建复杂的工具。

    UNIX系统之shell 脚本之源UNIX系统之shell 脚本之源

    世界上对 shell 脚本最好的概念性介绍来自一个老的 AT&T 培训视频 。在视频中,Brian W. Kernighan(awk 中的“k”),Lorinda L. Cherry(bc 作者之一)论证了 UNIX 的基础原则之一是让用户利用现有的实用程序来定制和创建复杂的工具。

    用 Kernighan 的话来说:“UNIX 系统程序基本上是 …… 你可以用来创造东西的构件。…… 管道的概念是 [UNIX] 系统的基础;你可以拿一堆程序 …… 并将它们端到端连接到一起,使数据从左边的一个流到右边的一个,由系统本身管着所有的连接。程序本身不知道任何关于连接的事情;对它们而言,它们只是在与终端对话。”

    他说的是给普通用户以编程的能力。UNIX系统之shell 脚本之源UNIX系统之shell 脚本之源

    POSIX 操作系统本身就像是一个 API。如果你能弄清楚如何在 POSIX 的 shell 中完成一个任务,那么你可以自动化这个任务。这就是编程,这种日常 POSIX 编程方法的主要方式就是 shell 脚本。

    像它的名字那样,shell 脚本就是一行一行你想让你的计算机执行的语句,就像你手动的一样。

    因为 shell 脚本包含常见的日常命令,所以熟悉 UNIX 或 Linux(通常称为 POSIX 系统)对 shell 是有帮助的。你使用 shell 的经验越多,就越容易编写新的脚本。这就像学习外语:你心里的词汇越多,组织复杂的句子就越容易。

    当您打开终端窗口时,就是打开了 shell 。shell 有好几种,本教程适用于 bashtcshkshzsh 和其它几个。在下面几个部分,我提供一些 bash 特定的例子,但最终的脚本不会用那些,所以你可以切换到 bash 中学习设置变量的课程,或做一些简单的语法调整。

    如果你是新手,只需使用 bash 。它是一个很好的 shell,有许多友好的功能,它是 Linux、Cygwin、WSL、Mac 默认的 shell,并且在 BSD 上也支持。

    Hello world

    您可以从终端窗口生成您自己的 hello world 脚本 。注意你的引号;单和双都会有不同的效果(LCTT 译注:想必你不会在这里使用中文引号吧)。

    $ echo "#/!/bin/sh" > hello.sh
    $ echo "echo 'hello world' " >> hello.sh
    

    正如你所看到的,编写 shell 脚本就是这样,除了第一行之外,就是把命令“回显”或粘贴到文本文件中而已。

    像应用程序一样运行脚本:

    $ chmod +x hello.sh
    $ ./hello.sh
    hello world
    

    不管多少,这就是一个 shell 脚本了。

    现在让我们处理一些有用的东西。

    去除空格

    如果有一件事情会干扰计算机和人类的交互,那就是文件名中的空格。您在互联网上看到过:http://example.com/omg%2ccutest%20cat%20photophoto%21%211.jpg 等网址。或者,当你不管不顾地运行一个简单的命令时,文件名中的空格会让你掉到坑里:

    $ cp llama pic.jpg ~/photos
    cp: cannot stat 'llama': No such file or directory
    cp: cannot stat 'pic.jpg': No such file or directory
    

    解决方案是用反斜杠来“转义”空格,或使用引号:

    $ touch foo/ bar.txt
    $ ls "foo bar.txt"
    foo bar.txt
    

    这些都是要知道的重要的技巧,但是它并不方便,为什么不写一个脚本从文件名中删除这些烦人的空格?

    创建一个文件来保存脚本,以释伴shebang(#!) 开头,让系统知道文件应该在 shell 中运行:

    $ echo '#!/bin/sh' > despace
    

    好的代码要从文档开始。定义好目的让我们知道要做什么。这里有一个很好的 README:

    despace is a shell script for removing spaces from file names.
    
    Usage:
    $ despace "foo bar.txt"
    

    现在让我们弄明白如何手动做,并且如何去构建脚本。

    假设你有个只有一个 foo bar.txt 文件的目录,比如:

    $ ls
    hello.sh
    foo bar.txt
    

    计算机无非就是输入和输出而已。在这种情况下,输入是 ls特定目录的请求。输出是您所期望的结果:该目录文件的名称。

    在 UNIX 中,可以通过“管道”将输出作为另一个命令的输入,无论在管道的另一侧是什么过滤器。 tr程序恰好设计为专门修改传输给它的字符串;对于这个例子,可以使用 --delete选项删除引号中定义的字符。

    $ ls "foo bar.txt" | tr --delete ' '
    foobar.txt
    

    现在你得到了所需的输出了。

    在 Bash shell 中,您可以将输出存储为变量 。变量可以视为将信息存储到其中的空位:

    $ NAME=foo
    

    当您需要返回信息时,可以通过在变量名称前面缀上美元符号($)来引用该位置。

    $ echo $NAME
    foo
    

    要获得您的这个去除空格后的输出并将其放在一边供以后使用,请使用一个变量。将命令的结果放入变量,使用反引号(`)来完成:

    $ NAME=`ls "foo bar.txt" | tr -d ' '`
    $ echo $NAME
    foobar.txt
    

    我们完成了一半的目标,现在可以从源文件名确定目标文件名了。

    到目前为止,脚本看起来像这样:

    #!/bin/sh
    
    NAME=`ls "foo bar.txt" | tr -d ' '`
    echo $NAME
    

    第二部分必须执行重命名操作。现在你可能已经知道这个命令:

    $ mv "foo bar.txt" foobar.txt
    

    但是,请记住在脚本中,您正在使用一个变量来保存目标名称。你已经知道如何引用变量:

    #!/bin/sh
    
    NAME=`ls "foo bar.txt" | tr -d ' '`
    echo $NAME
    mv "foo bar.txt" $NAME
    

    您可以将其标记为可执行文件并在测试目录中运行它。确保您有一个名为 foo bar.txt(或您在脚本中使用的其它名字)的测试文件。

    $ touch "foo bar.txt"
    $ chmod +x despace
    $ ./despace
    foobar.txt
    $ ls
    foobar.txt
    
    去除空格 v2.0

    脚本可以正常工作,但不完全如您的文档所述。它目前非常具体,只适用于一个名为 foo/ bar.txt 的文件,其它都不适用。

    POSIX 命令会将其命令自身称为$0,并将其后键入的任何内容依次命名为 $1$2$3等。您的 shell 脚本作为 POSIX 命令也可以这样计数,因此请尝试用 $1 来替换 foo/ bar.txt 。

    #!/bin/sh
    
    NAME=`ls $1 | tr -d ' '`
    echo $NAME
    mv $1 $NAME
    

    创建几个新的测试文件,在名称中包含空格:

    $ touch "one two.txt"
    $ touch "cat dog.txt"
    

    然后测试你的新脚本:

    $ ./despace "one two.txt"
    ls: cannot access 'one': No such file or directory
    ls: cannot access 'two.txt': No such file or directory
    

    看起来您发现了一个 bug!

    这实际上不是一个 bug,一切都按设计工作,但不是你想要的。你的脚本将 $1 变量真真切切地 “扩展” 成了:“one two.txt”,捣乱的就是你试图消除的那个麻烦的空格。

    解决办法是将变量用以引号封装文件名的方式封装变量:

    #!/bin/sh
    
    NAME=`ls "$1" | tr -d ' '`
    echo $NAME
    mv "$1" $NAME
    

    再做个测试:

    $ ./despace "one two.txt"
    onetwo.txt
    $ ./despace c*g.txt
    catdog.txt
    

    此脚本的行为与任何其它 POSIX 命令相同。您可以将其与其他命令结合使用,就像您希望的使用的任何 POSIX 程序一样。您可以将其与命令结合使用:

    $ find ~/test0 -type f -exec /path/to/despace {} /;
    

    或者你可以使用它作为循环的一部分:

    $ for FILE in ~/test1/* ; do /path/to/despace $FILE ; done
    

    等等。

    去除空格 v2.5

    这个去除脚本已经可以发挥功用了,但在技术上它可以优化,它可以做一些可用性改进。

    首先,变量实际上并不需要。 shell 可以一次计算所需的信息。

    POSIX shell 有一个操作顺序。在数学中使用同样的方式来首先处理括号中的语句,shell 在执行命令之前会先解析反引号 ` 或 Bash 中的 $() 。因此,下列语句:

    $ mv foo/ bar.txt `ls foo/ bar.txt | tr -d ' '`
    

    会变换成:

    $ mv foo/ bar.txt foobar.txt
    

    然后实际的 mv 命令执行,就得到了 foobar.txt 文件。

    知道这一点,你可以将该 shell 脚本压缩成:

    #!/bin/sh
    
    mv "$1" `ls "$1" | tr -d ' '`
    

    这看起来简单的令人失望。你可能认为它使脚本减少为一个单行并没有必要,但没有几行的 shell 脚本是有意义的。即使一个用简单的命令写的紧缩的脚本仍然可以防止你发生致命的打字错误,这在涉及移动文件时尤其重要。

    此外,你的脚本仍然可以改进。更多的测试发现了一些弱点。例如,运行没有参数的 despace 会产生一个没有意义的错误:

    $ ./despace
    ls: cannot access '': No such file or directory
    
    mv: missing destination file operand after ''
    Try 'mv --help' for more information.
    

    这些错误是让人迷惑的,因为它们是针对 ls 和 mv 发出的,但就用户所知,它运行的不是 ls 或 mv,而是despace

    如果你想一想,如果它没有得到一个文件作为命令的一部分,这个小脚本甚至不应该尝试去重命名文件,请尝试使用你知道的变量以及 test 功能来解决。

    if 和 test

    if 语句将把你的小 despace 实用程序从脚本蜕变成程序。这里面涉及到代码领域,但不要担心,它也很容易理解和使用。

    if 语句是一种开关;如果某件事情是真的,那么你会做一件事,如果它是假的,你会做不同的事情。这个 if-then 指令的二分决策正好是计算机是擅长的;你需要做的就是为计算机定义什么是真或假以及并最终执行什么。

    测试真或假的最简单的方法是 test 实用程序。你不用直接调用它,使用它的语法即可。在终端试试:

    $ if [ 1 == 1 ]; then echo "yes, true, affirmative"; fi
    yes, true, affirmative
    $ if [ 1 == 123 ]; then echo "yes, true, affirmative"; fi
    $
    

    这就是test 的工作方式。你有各种方式的简写可供选择,这里使用的是 -z 选项,它检测字符串的长度是否为零(0)。将这个想法翻译到你的 despace 脚本中就是:

    #!/bin/sh
    
    if [ -z "$1" ]; then
       echo "Provide a /"file name/", using quotes to nullify the space."
       exit 1
    fi
    
    mv "$1" `ls "$1" | tr -d ' '`
    

    为了提高可读性,if 语句被放到单独的行,但是其概念仍然是:如果 $1 变量中的数据为空(零个字符存在),则打印一个错误语句。

    尝试一下:

    $ ./despace
    Provide a "file name", using quotes to nullify the space.
    $
    

    成功!

    好吧,其实这是一个失败,但它是一个漂亮的失败,更重要的是,一个有意义的失败。

    注意语句 exit 1 。这是 POSIX 应用程序遇到错误时向系统发送警报的一种方法。这个功能对于需要在脚本中使用 despace ,并依赖于它成功执行才能顺利运行的你或其它人来说很重要。

    最后的改进是添加一些东西,以保护用户不会意外覆盖文件。理想情况下,您可以将此选项传递给脚本,所以它是可选的;但为了简单起见,这里对其进行了硬编码。 -i 选项告诉 mv 在覆盖已存在的文件之前请求许可:

    #!/bin/sh
    
    if [ -z "$1" ]; then
       echo "Provide a /"file name/", using quotes to nullify the space."
       exit 1
    fi
    
    mv -i "$1" `ls "$1" | tr -d ' '`
    

    现在你的 shell 脚本是有意义的、有用的、友好的 - 你是一个程序员了,所以不要停。学习新命令,在终端中使用它们,记下您的操作,然后编写脚本。最终,你会把自己从工作中解脱出来,当你的机器仆人运行 shell 脚本,接下来的生活将会轻松。

    作者简介:

    Seth Kenlon 是一位独立的多媒体艺术家,自由文化倡导者和 UNIX 极客。他是基于 Slackware 的多媒体制作项目(http://slackermedia.ml)的维护者之一

    本文地址:http://www.linuxprobe.com/bashshell-unix-linux.html

    免费提供最新Linux技术教程书籍,为开源技术爱好者努力做得更多更好,开源站点:http://www.linuxprobe.com/

    展开全文
  • UNIX系统中的进程

    2019-08-09 19:56:05
    (1)UNIX用唯一的被称为进程ID的整数值来标识进程。每个进程有一个父进程,所以有一个父进程ID。当这个父进程被终止时,由系统的INIT进程来收养这个进程。 (2)系统管理员创建用户账户时会分配唯一的整型用户ID和...

    3.1 进程标识

    (1)UNIX用唯一的被称为进程ID的整数值来标识进程。每个进程有一个父进程,所以有一个父进程ID。当这个父进程被终止时,由系统的INIT进程来收养这个进程。

    (2)系统管理员创建用户账户时会分配唯一的整型用户ID和整型组ID。系统通过用户ID和组ID从系统数据库中检索出允许这个用户使用的权限。权限最高的用户ID为0;是root用户。

    (3)真实用户ID和真实组ID,有效用户ID和有效组ID。进程用有效ID来确定文件的访问权限。

    3.2 进程状态

    (1)进程的状态转换图十分的重要。

    (2)进程执行I/O时是通过一个库函数去请求服务的,这个库函数有时被称为系统调用。

    (3)上下文切换:将一个进程从运行状态移出,并用另一个进程来代替它的行为。

    进程上下文:操作系统在上下文切换后重启进程所需的、有关此进程及其环境的信息。

    可执行代码,栈,寄存器和程序计数器都是上下文的一部分。

    3.3 进程的创建与fork调用

           (1)进程可以通过调用fork来创建新的进程。调用进程就成为父进程,被创建的进程成为子进程。fork函数拷贝了父进程的内存映像,这样新进程就会收到父进程地址空间的一份拷贝。在fork语句调用之后,两个进程都会执行后面的指令,分别在他们自己的内存映像中执行。

           fork函数的返回值是对父进程和子进程区别并执行不同代码的关键特征。fork函数向子进程返回0,将子进程的ID返回到父进程。fork失败时,他就会返回-1并设置errmo。

           (2)printf 将消息写入stdout,和fprintf 将消息写入stderr 有什么区别:

    系统会对Stdout缓存,特定的消息不会在printf返回之后立即出现。系统不会对stderr的消息进行缓存,而是立即写出。所以一般调试使用stderr。

    fork函数通过复制父进程在内存中的映像,创建了一个新的进程。子进程继承了诸如环境和权限这样的父进程属性。子进程还集成了某些父进程资源,例如打开的和设备。

    不是父进程的每个属性或资源都被子进程继承了。例如,子进程有一个系的进程ID,当然还有一个不同的父进程ID。子进程的CPU使用时间被重置为0。 子进程没有获得父进程持有的锁。父进程设置了一个警报,父进程的警报到的时候,不会通知子进程,即使在执行fork的时候,父进程有挂起的信号,子进程启动的时候也不带有挂起信号。

    尽管子进程继承了它的父进程的进程优先级和调度属性,它仍然是作为一个独立的实体去和其他的进程竞争处理器时间的。在一个繁忙的分时系统上的用户可以通过创建更多的进城来获得更多的CPU时间。一个繁忙系统的系统管理员可以通过限制进程的创建来防止用户通过创建进程来获得更多的资源份额。

     

    3.4wait函数

    一个进程创建子进程时,父进程和子进程都从fork后的那个点开始继续执行。父进程可以通过执行wait或waitpid一直阻塞到子进程结束。

    Wait函数的作用:会使调用者的执行挂起,直到子进程的状态成为可用的,或者调用者收到一个信号为止。

    子进程的状态为可用的:子进程终止或者被停止时都有可能是可用的。

    Waitpid函数:允许父进程等待一个特定的子进程,这个进程允许父进程非阻塞的检查子进程是否已经终止了。Waitpid函数有三个参数:pid、指向返回状态所在单元的指针和一个用来指定可选项的标识符。如果pid为-1,waitpid就等待任意一个子进程。Waitpid功能强大。看联机帮助。

    Wait 和waitpid函数出错时返回-1, 错误值是EINTR 函数被信号中断。

    Waitpid中如果最后一个参数是WNOHANG则表示无阻塞等待,即当前有子进程退出时则返回子进程的pid,没有子进程退出时则不会阻塞父进程。

     

    Wait和waitpid两个函数的返回值:

        如果等待到了子进程退出,返回子进程的pid 号;

           如果出现错误就会返回-1,并设置errno.

           Waitpid,使用了选项WNOHANG,waitpit返回0来报告可能有无人等待的子进程,但这些子进程的状态不可用。

           考查返回值的作用在于,判断等待一个子进程,多个子进程,以及出现错误等结果的区分。

           例1、等待一个子进程

                  pid_t cihldpid

                  childpid=wait(NULL);

                  if(childpid=-1)

                         printf(“waited for a child with pid%ld \n”,childpid);   

           例2、被信号中断后会重启的wait函数

                  pid_t r_wait(int *stat_loc){

                         int retval;

                         while(((retval=wait(stat_loc))==-1)&&(errno==EINTR));

                         return retval;

           }

     

           注释:这里的第一个条件是说明等待出错,第二个条件说明出错原因是被信号中断。

           例3、用waitpid函数写的等待所有已结束的子进程,但如果没有状态可用的子进程,代码段则避免了阻塞。函数被信号中断或者成功的等到了一个子进程,它就重启waitpid。

           Pid_t childpid;

           while(childpid=waitpid(-1,NULL,WNOHANG))//这个条件是非零即真

                  if((childpid=-1)&&(errno!=EINTR))//这个条件保证不是被信号中断

                         break;

           例4、在进程扇中等待所有子进程结束。

           while(r_wait(NULL)>0) //被信号中断的处理由r_wait()来做,等待一个子进程退出的处理由while 来做。

           For(;;){

                  childpid=wait(null);

                  if((childpid==-1)&&(errno!=EINTR))

                         break;

           }

          

    状态值

    Wait和waitpid的参数stat_loc是一个指向整数变量的指针。如果stat_loca不为NULL,这些函数就将子进程的返回状态存储在这个单元中。子进程通过调用exit,_exit._Exit或从main函数中return来返回它的状态。返回值为零说明EXIT_SUCCESS;任何其它的值都说明EXIT_FAILURE。

     

    3.5exec函数

    fork函数创建了调用进程的一份拷贝,但很多应用程序都需要子进程执行与其父进程不同的代码。Exec函数簇提供了用新的映像来覆盖调用进程的进程映像的功能。

    exec可以用来执行另一个编译好的程序,也可以是一个命令。

    这里有六中不同形式的exec函数,

    它们的区别在于:

    1、命令行参数和环境变量的传递方式。

           execl(execl,execlp,execle)函数会用一个显式的序列来传递命令行参数。

           execv(execv,execvp和execve)函数将命令行参数放在一个参数数组中传递。

           2、是否要给出可执行文件的完整路径名。

                  execl的参数path是一个进程映像文件的路径名,它可以是一个权限定路径名,也可以是一个相对于当前目录的路径名。Execl的第一个参数路径名,包括执行文件的名字。

                  execlp,第一个参数中包含一个斜杠,那么execlp就将file当作路径名,表现的和path一样,另一方面,如果file中没有斜杠,execlp就用环境变量PATH来搜寻可执行文件。

                  后面的参数就是所有命令行参数。

     

                  例如:

                  execl(“/bin/ls”,”ls”,”-l”);

                  execvp(argv[1],&argv[1]); argv[1]=”ls”argv[2]=”-l”;

                  argv[1]中不包含斜杠,所以在环境变量PATH所指定的路径中找,ls命令。

                  如果,包含了斜杠,则在相应的文件夹下面找。

     

    3.6后台进程与守护进程

     

    命令解释程序时一个用来提示命令、从标准输入中读取命令、创建子进程来执行命令并等待子进程执行完毕的一个命令解释程序。

    大多数命令解释程序将一个以&结束的行解释为应该由后台进程执行的命令。命令解释程序创建了一个后台进程时,他在发出提示符并接受其他的命令行之前不用等待进程的结束。而且,从键盘键入的ctrl-C也不能终止后台进程。

    例1

    cd /etc

    ls –l

    例 2

    ls –l &

     

    守护进程:是一个通常能够无限期运行的后台进程。UNIX操作系统依靠很多守护进程来执行例行的任务。

     

    3.7临界区

     

    一个资源同时只能被一个进程所使用,临界资源。

    每个进程中对这样的共享资源进行访问的那部分代码都被称为临界区。

    转载于:https://www.cnblogs.com/lgz24/archive/2010/06/23/1763614.html

    展开全文
  • 图解UNIX的I/O模型

    千次阅读 2017-03-12 08:43:58
    一、简述UNIX系统将所有的外部设备都看作一个文件来看待,所有打开的文件都通过文件描述符来引用。文件描述符是一个非负整数,它指向内核中的一个结构体。当打开一个现有文件或创建一个新文件时,内核向进程返回一个...

    一、简述

    UNIX系统将所有的外部设备都看作一个文件来看待,所有打开的文件都通过文件描述符来引用。文件描述符是一个非负整数,它指向内核中的一个结构体。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。而对于一个socket的读写也会有相应的文件描述符,称为socketfd(socket描述符)。
    在UNIX系统中,I/O输入操作(例如标准输入或者套接字的输入)通常包含以下两个不同的阶段:

    • 等待数据准备好
    • 从内核向进程复制数据

    例如对于套接字的输入,第一步是等待数据从网络中到达,当所等待的数据到达时,数据被复制到内核中的缓冲区。第二步则是把数据从内核缓冲区复制到应用进程的缓冲区。
    根据在这两个不同阶段处理的不同,可以将I/O模型划分为以下五种类型:

    • 阻塞式I/O模型
    • 非阻塞式I/O模型
    • I/O复用
    • 信号驱动式I/O
    • 异步I/O

    二、I/O模型

    为简单起见,我们以UDP套接字中的recvfrom函数作为系统调用来说明I/O模型。recvfrom函数类似于标准的read函数,它的作用是从指定的套接字中读取数据报。recvfrom会从应用进程空间运行切换到内核空间中运行,一段时间后会再切换回来。有关recvfrom函数的介绍,可以参考本文的参考资料3。

    2.1 阻塞式I/O模型

    阻塞式I/O模型可以说是最简单的I/O模型。

    阻塞式I/O模型
    图1:阻塞式I/O模型

    图1是阻塞式I/O模型的示意图,阅读此图须注意箭头的方向,沿着剪头方向顺时针阅读。
    图1中,应用进程调用recvfrom,然后切换到内核空间中运行,直到数据报到达且被复制到应用进程缓冲区中才返回。我们说进程从调用recvfrom开始到它返回的整段时间内是被阻塞的。recvfrom成功返回后,应用进程开始处理数据报。

    2.2 非阻塞式I/O模型

    进程把一个套接字设置为非阻塞是指,在等待I/O数据时,进程并不阻塞,如果数据还没准备好,则直接返回一个错误。

    非阻塞式I/O模型
    图2:非阻塞式I/O模型

    图2是非阻塞I/O模型的示图。
    在前两次调用recvfrom时由于数据报没准备好,因此内核马上返回一个系统调用错误。第3次调用recvfrom时,数据报已准备好,数据报被复制到应用进程的缓冲区,接着recvfrom成功返回。
    当一个应用进程像这样不断对一个非阻塞描述符循环调用recvfrom时,我们称之为轮询。应用进程会持续轮询内核,以确定某个操作是否就绪。轮询操作会消耗大量的CPU时间。

    2.3 I/O复用模型

    我们常用的select和poll函数使用了I/O复用模型。我们以select为例说明I/O复用模型的特点。

    I/O复用模型
    图3:I/O复用模型

    图3是I/O复用模型的示意图。
    当我们调用select函数时,将会阻塞于此函数,等待数据报套接字变为可读。当等待的多个套接字中的其中一个或者多个变得可读时,我们调用recvfrom把数据报复制到应用进程缓冲区。
    比较图3与图1,I/O复用模型好像没什么优势,而且应用进程为了获取数据报,还得增加了一个额外的select系统调用。不过I/O复用模型的优势在于可以同时等待多个(而不只是一个)套接字描述符就绪。

    2.4 信号驱动式I/O模型

    信号驱动I/O模型用得比较少,图4是该模型的示意图。

    信号驱动式I/O模型
    图4:信号驱动式I/O模型

    为了使用该I/O模型,需要开启套接字的信号驱动I/O功能,并通过sigaction系统调用安装一个信号处理函数。sigaction函数立即返回,我们的进程继续工作,即进程没有被阻塞。当数据报准备好时,内核会为该进程产生一个SIGIO信号,这样我们可以在信号处理函数中调用recvfrom读取数据报,也可以在主循环中读取数据报。无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间不被阻塞。

    2.5 异步I/O模型

    异步I/O模型的工作机制是,启动某个操作,并让内核在整个操作(包括等待数据和将数据从内核复制到用户空间)完成后通知应用进程。

    异步I/O模型
    图5:异步I/O模型

    图5是异步I/O模型的示意图。我们调用aio_read函数,告诉内核,当整个I/O操作完成后通知我们。该系统调用立即返回,而在等待I/O完成期间,应用进程不会被阻塞。当I/O完成(包括数据从内样复制到用户进程)后,内核会产生一个信号通知应用进程,应用进程对数据报进行处理。
    异步I/O模型与信号驱动式I/O的区别在于:信号驱动式I/O在数据报准备好时就通知应用进程,应用进程还需要将数据报从内核复制到用户进程缓冲区;而异步I/O模型则是整个操作完成才通知应用进程,应用进程在整个操作期间都不会被阻塞。

    2.6 各种I/O模型的比较

    这里写图片描述
    图6:5种I/O模型的比较

    从图6可以看到,前四种I/O模型的主要区别在于第一个阶段,它们的第二个阶段是一样的:在数据从内核复制到应用进程的缓冲区期间,进程会被阻塞于recvfrom系统调用。
    而异步I/O模型则是整个操作完成内核才通知应用进程。

    三、同步I/O和异步I/O

    POSIX标准将同步I/O和异步I/O定义为:

    • 同步I/O操作:导致请求进程阻塞,直到I/O操作完成。

    • 异步I/O操作:不导致请求进程阻塞。

    根据上述两个定义,本文介绍的前面四种模型,包括阻塞式I/O,非阻塞式I/O,I/O复用和信号驱动式I/O模型都是同步I/O模型,因为其中真正的I/O操作(recvfrom)将阻塞进程。只有异步I/O模型才符合POSIX标准的异步I/O定义。

    四、生活中的类比例子

    以生活中钓鱼为例子(例子参考了参考资料2),来说明各种I/O模型的不同,例子中的等待鱼上钩对应于上文中的等待数据,拉竿操则作对应于上文的将数据从内核复制到用户空间。

    有A,B,C,D,E五个人在钓鱼。
    A使用了最古老的鱼竿,所以开始钓鱼后,就一直守着,直接鱼上钩了再拉竿;
    B由于着急想知道有没鱼上钩,所以隔一会就看一次鱼竿看有没鱼上钩,直到看到鱼上钩后,再拉竿;
    C同时使用了N支鱼竿来钩鱼,然后等着,只要有其中一支鱼竿有鱼上钩,就将对应的鱼竿拉起来;
    D的鱼竿比较高级,当有鱼上钩后,会发出警报提示,所以D开始钓鱼后不用一直守着,一旦鱼竿发出警报,D再回来拉竿即可;
    E为了更省事,直接雇个佣人给他钓鱼,当佣人钓起鱼后,再通知E去取鱼即可。

    五、参考资料

    1. Unix网络编程,卷1:套接字联网API,第三版,W. Richard Stevens著
    2. http://blog.csdn.net/historyasamirror/article/details/5778378
    3. http://pubs.opengroup.org/onlinepubs/009695399/functions/recvfrom.html
    展开全文
  • Mac文件系统结构

    万次阅读 2016-03-12 12:01:00
    在OS X的系统中,不再有Windows用户熟悉的C盘、D盘,这是因为OS X底层是Unix系统,其目录机构符合Unix系统的规范。MAC机器主板使用了Intel主导的EFI标准,硬盘分区格式采用GPT。这种EFI+GPT的方式相比传统的BIOS+...
  • UNIX 进程--多任务原理

    千次阅读 2017-03-13 21:38:44
    了解 UNIX 的多任务原理 级别: 中级 Martin Streicher (martin.streicher@linux-mag.com), 主编, Linux Magazine 2007 年 5 月 16 日 在 UNIX® 系统中,每个系统和最终用户任务都包含在某个进程中。系统...
  • UNIX痛恨者手册

    千次阅读 2008-01-03 11:39:00
    一本很有趣的书, 是那些用惯UNIX的人对UNIX的各种指责,诋毁,谩骂和嘲笑. 是由爱而生的恨. 即使当成一本高级笑话书,也是很有价值的.UNIX痛恨者手册By Simson Garfinkel, Daniel Weise, Steven Strassmann第一章 UNIX...
  • 基于网络的IDS分析网络流量中是否有入侵,并发出警报,而HIDS通过检查网络上的事件来跟踪主机的可疑活动。 本文将介绍五个基于主机的开源入侵检测系统,以帮助您保护组织。 关于HIDS 在深入研究HIDS工具之前,让...
  • unix syslog配置

    2019-06-18 15:22:56
    配置linux syslog日志服务器 ... 目前,linux依旧使用syslogd作为日志监控进程,而在主流的linux发行版中依旧使用...从前一篇日志可以看到,对其进行必要的配置能减少很多麻烦,并且可更有效的从系统日志监控到系...
  • Unix痛恨者手册zz UNIX hater' guide

    千次阅读 2007-02-03 23:15:00
    转载自:Simson Garfinkel, Daniel Weise, Steven Strassmann原文由pengchengzou张贴 张贴者: pengchengzou (old hand)张贴日期 06/20/02 21:33一本很有趣的书, 是那些用惯UNIX的人对UNIX的各种指责,诋毁,谩骂和...
  • 一些非UNIX系统,比如Windows NT,也是与POSIX.1003标准兼容,这个标准无疑是基于UNIX。这个系统的成功是由于它的开放性、稳定性和事实的标准。随着POSIX1003.1b实时扩展标准的发布,UNIX有机会成为分布最广泛的实时...
  • LINUX与UNIX Shell编程指南

    千次阅读 2013-10-31 13:16:44
    本文来自于哥们对《LINUX与UNIX Shell编程指南》一书的写写画画,并合成了自己当初一些疑问点所致,仅限学习使用,同时向原作者致敬。 原书请见 http://product.china-pub.com/632 shell 文件安全与权限 使用...
  • 监控系统是运维人员的眼睛,当监控对象发生问题时,监控系统要第一时间发出警报,警报中除了出问题的点,还可以有一些数据和简单的分析,比如当时一段时间的cpu负载等,以帮助接收到报警的人员快速定位问题。...
  • 工控系统概述

    千次阅读 2020-01-13 16:54:58
    工业控制系统安全-工控系统概述 简短介绍工控系统架构、组成部分、功能以及术语等方面,掌握基本工控系统知识。工控系统指的是工业自动化控制系统,主要利用电子电气、机械、软件组合实现。主要是使用计算机技术,...
  • 在最近的一次街头交易会上,我被单人乐队迷住了。 是的,我很容易被逗乐,但仍然给我留下了深刻的印象。 真正的独奏交响曲结合了口琴,班卓琴,和a鼓-分别在嘴,膝,膝盖和脚上–真正的独奏交响乐使齐柏林飞船(Led ...
  • vi编辑器是Unix/Linux系统管理员必须学会使用的编辑器。看了不少关于vi的资料,终于得到这个总结。不敢独享,和你们共享。    首先,记住vi编辑器的两个模式:1、命令模式 2、编辑模式。   在一个UNIX...
1 2 3 4 5 ... 20
收藏数 2,351
精华内容 940
热门标签
关键字:

unix系统发出警报