unix 函数括号数字_unix函数 - CSDN
  • 看> 等书, 常会看到 kill(1) , conect(2) , select(2) 这样的写法. ...2 系统调用, 即由内核提供的函数。 3 例程, 即库函数。 4 设备, 即/dev目录下的特殊文件。 5 文件格式描述, 例如

    看<<unix高级程序设计>> 等书, 常会看到 kill(1) , conect(2) , select(2) 这样的写法.

    1,2 表示什么?   

    Linux下最通用的领域及其名称及说明如下:

    领域 名称 说明

    1 用户命令, 可由任何人启动的。

    2 系统调用, 即由内核提供的函数。

    3 例程, 即库函数。

    4 设备, 即/dev目录下的特殊文件。

    5 文件格式描述, 例如/etc/passwd。

    6 游戏, 不用解释啦!

    7 杂项, 例如宏命令包、惯例等。

    8 系统管理员工具, 只能由root启动。

    9 其他(Linux特定的), 用来存放内核例行程序的文档。

    n 新文档, 可能要移到更适合的领域。

    o 老文档, 可能会在一段期限内保留。

    l 本地文档, 与本特定系统有关的。


    在使用man 查询这些命令的时候,  可以用下面这样的格式:

    $man  数字  命令

    例如 :  $man 1 kill   #表示命令kill 

                $man 2 kill   #表示系统调用 kill

    展开全文
  • 这些数字表示可在"Unix manual"中的哪一部分找到对应的文档。 你可以输入"man 3 ctime"在手册的第三部分寻找到"ctime"的手册页。传统的Unix手册页分部如下:1 用户层面命令 2 系统调用 3 库函数 4 设备和设备...

     

    它们看上去像某种函数调用,但不是。 这些数字表示可在"Unix manual"中的哪一部分找到对应的文档。 你可以输入"man 3 ctime"在手册的第三部分寻找到"ctime"的手册页。

    传统的Unix手册页分部如下:

    1 用户层面命令 
    2 系统调用 
    3 库函数 
    4 设备和设备驱动 
    5 文件格式 
    6 游戏 
    7 各式零碎杂物,如宏包等 
    8 系统维护命令

    一些Unix版本使用非数字的分部名。 比如Xenix用"C"代表命令,"S"代表函数。 一些新版本的Unix需要用"man -s # title"代替"man # title"

    每个部分都有一个简介,可用"man # intro"阅读,#表示部分的编号。

    某些场合,编号是必要的,以区别同名的命令和系统调用。比如系统可能存在time(1),time命令的手册页,同时还有time(3),判断当前时间的子程序调用。这时就需要用man 1 time和man 3 time,指明你感兴趣的手册页。

    你也可在一些系统中看到其它的分部,甚至上述分部的亚部,例如Ultrix有3m, 3n, 3x and 3yp等。

     

    展开全文
  • 其中 5 种为正常终止,它们是:(1)在 main 函数中执行 return(2)调用 exit 函数,并不处理文件描述符,多进程(3)调用 _exit 或 _Exit(4)最后一个线程从其启动例程返回(5)从最后一个线程调用 pthread_exit...

    我们一开始讲进程环境时,就有提到了。进程有 8 种方式使进程终止。

    其中 5 种为正常终止,它们是:

    (1)在 main 函数中执行 return
    (2)调用 exit 函数,并不处理文件描述符,多进程
    (3)调用 _exit 或 _Exit
    (4)最后一个线程从其启动例程返回
    (5)从最后一个线程调用 pthread_exit
    异常终止有 3 种方式,它们是:
    (6)调用 abort,产生 SIGABRT 信号
    (7)进程接收到某些信号
    (8)最后一个线程对“取消”请求做出响应 

    我们之前是讲过的,参看:C语言再学习 -- 关键字return和exit ()函数   接下来重点介绍 exit 和 wait 系列函数。

    一、exit 系列函数

    1、exit 函数

    #include <stdlib.h>
    void exit(int status)

    (1)函数功能

    正常终止调用进程

    (2)参数解析

    status:进程退出码,相当于 main 函数的返回值。被终止的进程的父进程可以通过 wait 或 waitpid 函数,获取 status 参数的低 8 位,以了解导致该进程终止的具体原因。
    exit(0)表示正常退出exit(x)(x不为0)都表示异常退出,这个 x 是返回给操作系统(包括UNIX,Linux,和MS DOS)的,以供其他程序使用。
    通常情况下,程序成功执行完一个操作正常退出的时候会带有值 EXIT_SUCCESS。在这里,EXIT_SUCCESS 是宏,它被定义为 0。如果程序中存在一种错误情况,当您退出程序时,会带有状态值EXIT_FAILURE,被定义为 1
    标准C中有 EXIT_SUCCESS 和 EXIT_FAILURE 两个宏,位于 /usr/include/stdlib.h中:
    #define EXIT_FAILURE    1   /* Failing exit status.  */
    #define EXIT_SUCCESS    0   /* Successful exit status.  */

    (3)示例说明

    #include <stdio.h>  
    #include <stdlib.h>  
      
    int main ()  
    {  
       printf("程序开头....\n");  
         
       printf("退出程序....\n");  
       exit(0);  //等同 return 0;
      
       printf("程序结尾....\n");  
      
       return(0);  
    }  
    输出结果:  
    程序开头....  
    退出程序....  

    (4)进阶

    exit 和 return 的区别,我就不再重复了,之前讲的够详细了。现在讲点新的知识点。
    exit 函数在终止调用进程之前还会做三件收尾工作:

    1》》调用实现通过 atexit 或 on_exit 函数注册的退出处理函数。

    注册退出函数 atexit
    #include <stdlib.h>
    int atexit(void (*function)(void));
    成功返回 0, 失败返回非 0
    参数解析:
    function 为退出处理函数指针,所指向的函数既无返回值亦无参数,在进程终止前被调用,为进程料理临终事宜。
    函数解析:
    注意 atexit 函数本身并不调用退出处理函数,而只是将 function 参数所表示的退出处理函数地址,保存(注册)在系统内核的某个地方(进程表项)。待到 exit 函数被调用或在 main 函数里执行 return 语句时,再由系统内核根据这些退出处理函数的地址来调用它们。此过程亦称回调。
    ISO C规定,一个进程最多可登记32个终止处理函数这些函数由 exit 按登记相反的顺序自动调用如果同一函数登记多次,也会被调用多次。
    示例说明:
    #include <stdlib.h>  
    #include <stdio.h>  
    //所指向的函数既无返回值亦无参数
    void my_exit1 (void)  
    {  
        printf ("first exit handler\n");  
    }  
    void my_exit2 (void)  
    {  
        printf ("second exit handler\n");  
    }  
    int main()  
    {  
        atexit (my_exit2);  
        atexit (my_exit1);  
        atexit (my_exit1);  //回调顺序正好和注册顺序相反
        printf ("main is done\n");  
        return 0; // 相当于exit(0)  
    }  
    输出结果:  
    main is done  
    first exit handler  
    first exit handler  
    second exit handler  
    注册退出函数 on_exit
    #include <stdlib.h>
    int on_exit(void (*function)(int , void *), void *arg);
    成功返回 0,失败返回非 0
    参数解析:
    第一个参数:退出处理函数指针,所指向的函数无返回值但有两个参数。其中第一个参数来自传递给 exit 函数的 status 参数或在 main 函数里执行 return 语句的返回值,而第二个参数来自传递给 on_exit 函数的 arg 参数。该函数在进程终止前被调用,为进程料理临终事宜。
    第二个参数:泛型指针,将作为第二个参数传递给 function 所指向的退出处理函数。
    示例说明:
    #include <stdlib.h>  
    #include <stdio.h>  
    //所指向的函数无返回值但有两个参数
    void my_exit1 (int status, void *arg)  
    {  
        printf ("%s", (char*)arg);  
    	printf ("status = %d\n", status);
    }  
    void my_exit2 (int status, void *arg)  
    {  
        printf ("%s", (char*)arg);  
    }  
    int main()  
    {  
        on_exit (my_exit2, "second exit handler\n");  
        on_exit (my_exit1, "first exit handler\n");  
        on_exit (my_exit1, "first exit handler\n");  //回调顺序正好和注册顺序相反
        printf ("main is done\n");  
        return 1; // 相当于exit(1)  
    }  
    输出结果:
    main is done
    first exit handler
    status = 1
    first exit handler
    status = 1
    second exit handler

    2》》冲刷并关闭所有仍处于打开状态的标准 I/O 流。

    仍处于打开状态的标准 I/O 流,即缓冲 I/O。其特征就是对应每一个打开的文件,在内存中都有一片缓冲区。
    每次读文件时,会连续读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区中读取;同样,每次写文件时,也仅仅是写入内存中的缓冲区,等满足了一定的条件(如达到一定数量或遇到特定字符等,最典型的就是咱们的vim中使用的:w命令),再将缓冲区中的内容一次性写入文件。
    这种技术大大增加了文件读写的速度,但也给咱们的编程带来了一些麻烦。比如有些数据你认为已经被写入到文件中,实际上因为没有满足特定的条件,它们还只是被保存在缓冲区内,这时用 _exit() 函数直接将进程关闭掉,缓冲区中的数据就会丢失。因此,若想保证数据的完整性,最好使用 exit() 函数。
    缓冲区之前讲过很多次了,参看:UNIX再学习 -- 标准I/O
    举个例子:
    //示例一
    #include <stdio.h>  
    #include <stdlib.h>  
    
    int main (void)
    {
    	printf ("111111111\n");
    	printf ("222222222"); //冲刷了缓冲区
    	exit (0);
    }
    输出结果:
    111111111
    222222222
    //示例二
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>
    
    int main (void)
    {
    	printf ("111111111\n");
    	printf ("222222222"); //缓冲区数据丢失
    	_exit (0);
    }
    输出结果:
    111111111
    使用 fflush() 冲刷缓冲:
    #include <stdio.h>
    #include <unistd.h>
    
    int main (void)
    {
    	printf ("11111111111111\n");
    	printf ("22222222222222");
    	fflush (stdout);
    	_exit (0);
    }
    输出结果:
    11111111111111
    22222222222222

    3》》删除所有通过 tmpfile 函数创建的临时文件。

    暂时不讲 tempfile 。感兴趣可自行 man tempfile

    最后注意,exit 并不处理文件描述符、多进程(父进程和子进程)以及作业控制。
    exit 函数的内部调用了更底层的系统调用函数 _exit,后者也可以被用户空间的代码直接调用,比如在用 vfork 函数创建的子进程里。

    2、_exit/_Exit 函数

    #include <unistd.h>
    void _exit(int status);
    #include <stdlib.h>
    void _Exit(int status);
    不返回
    

    (1)函数功能

    正常终止调用进程

    (2)参数解析

    status:进程退出码,相当于 main 函数的返回值。

    (3)函数解析

    _exit 是UC函数,_Exit 是标C函数。在UNIX系统中,两者是同义的,并不冲洗标准 I/O 流。 
    _exit 在终止调用进程之前也会做三件收尾工作,但与 exit 函数所做的不同。事实上,exit 函数在做完它那三件收尾工作之后紧接着就会调用 _exit 函数
    1》》关闭所有仍处于打开状态的文件描述符
    2》》将调用进程的所有子进程托付给 init 进程收养
    3》》向调用进程的父进程发送 SIGCHLD (17) 信号
    从这三件收尾工作可以看出,它们所影响完全都是调用进程自己的资源,与其父进程没有任何关系。这对用 vfork 函数创建的子进程而言显得尤其重要。因为用 vfork 函数创建的子进程存在太多与其父进程共享的资源。如果在子进程里调用了 exit 函数或者在子进程的 main 函数里执行了 return 语句(这两者本质上是等价的),不仅销毁了子进程的资源,同时也销毁了父进程的资源,而父进程恰恰要在子进程终止以后从挂起中恢复运行,其后果可想而知。

    (4)示例说明

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <sys/types.h>  
      
    int main(void)  
    {  
        //使用vfork函数创建子进程  
        pid_t pid = vfork();  
        if(-1 == pid)  
        {  
            perror("vfork"),exit(-1);  
        }  
        if(0 == pid) //子进程  
        {  
            printf("子进程%d开始运行\n",getpid());  
            sleep(3);  
            printf("子进程结束\n");  
            //子进程不退出,则结果不可预知  
            _exit(0);//终止子进程  
        }  
        printf("父进程%d开始执行\n",getpid());  
        printf("父进程结束\n");  
        return 0;  
    }  
    输出结果:  
    子进程2762开始运行  
    子进程结束  
    父进程2761开始执行  
    父进程结束  

    3、pthread_exit 函数

    不是今天的重点,等到讲完 线程回过头再详细讲它。
    #include <pthread.h>
    void pthread_exit(void *retval);
    

    (1)函数功能

    主要用于终止正在运行的线程,通过参数 retval 来带出线程的退出状态信息,在同一个进程中的其他线程可以通过调用 pthread_join 函数来获取退出状态信息。

    (2)示例说明

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <pthread.h>  
    void* task(void* p)  
    {  
        static int i=0;  
    //  int i=0;  
        for(i=0;i<=100;i++)  
        {  
            if(i==10)  
            {  
                pthread_exit((void*)&i);  
            }  
            printf("子进程中:i=%d\n",i);  
        }  
    }  
    int main()  
    {  
        //1.启动线程打印1~100之间的数  
        pthread_t tid;  
        pthread_create(&tid,NULL,task,NULL);  
        //2.等待子进程结束,并且获取返回值  
        int* pi=NULL;  
        pthread_join(tid,(void**)&pi);  
        printf("子线程中变量的值是:%d\n",*pi);  
        return 0;  
    }  
    编译:gcc test.c -pthread  
    输出结果:  
    子进程中:i=0  
    子进程中:i=1  
    子进程中:i=2  
    子进程中:i=3  
    子进程中:i=4  
    子进程中:i=5  
    子进程中:i=6  
    子进程中:i=7  
    子进程中:i=8  
    子进程中:i=9  
    子线程中变量的值是:10  

    二、wait 系列函数

    下面我们来讲一下 wait 系列函数。

    1、wait 函数 

    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);
    成功返回所回收子进程的 PID,失败返回 -1

    (1)函数功能

    主要用于挂起正在运行的进程进入等待状态,直到有一个子进程终止。

    (2)参数解析

    status 是一个整型指针,如果 status 不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将该参数指定为空指针

    (3)函数解析

    1》》父进程在创建若干子进程以后调用 wait 函数

    若所有子进程都在运行,则阻塞,直至有子进程终止。
    若有一个子进程已终止,则返回该子进程的 PID 并通过 status 参数 (若非 NULL)输出其终止状态。
    若没有需要等待的子进程,则返回 -1,置 error 为 ECHILD
    示例说明:
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <errno.h>
    
    int main(void)
    {
    	pid_t pid1;
    	if((pid1=fork())<0)
    		perror("fork");
    	if(pid1 == 0)
    	{
    		printf("这是子进程,pid1=%d,",getpid());
    		printf("父进程的pid1=%d\n",getppid());
    		_exit(0);
    	}
    
    	pid_t pid2;
    	if((pid2=fork())<0)
    		perror("fork");
    	if(pid2 == 0)
    	{
    		printf("这是子进程,pid2=%d,",getpid());
    		printf("父进程的pid2=%d\n",getppid());
    		_exit(0);
    	}
    
    	printf("这是父进程,pid=%d\n",getpid());
    
    	while (1)
        {
            pid_t pid = wait (NULL);
            if (pid == -1)
            {
    			if (errno != ECHILD)
                {
                    perror ("wait");
                    exit (EXIT_FAILURE);
                }
    			printf ("子进程都死光了\n");
                break;
            }
            printf ("%d子进程终止\n", pid);
        }
    
        return 0;
    }
    输出结果:
    这是父进程,pid=2944
    这是子进程,pid1=2945,父进程的pid1=2944
    2945子进程终止
    这是子进程,pid2=2946,父进程的pid2=2944
    2946子进程终止
    子进程都死光了
    
    示例解析:
    如果不使用 wait ,父进程先于子进程终止,则子进程成为孤儿进程了。使用 wait 在于可挂起正在运行的父进程进入等待状态,直到有子进程终止。
    思考,父进程使用 sleep 不是也可以保证子进程先被调度,避免产生孤儿进程么。
    wait 和 sleep 有何区别,稍后会讲。

    2》》如果一个子进程在 wait  函数套用之前,已经终止并处于僵尸状态,wait 函数会立即返回,并取得该子进程的终止状态,同时子进程僵尸消失。由此可见 wait 函数主要完成三个任务。

    1、阻塞父进程的运行,直到子进程终止再继续,停等同步。
    2、获取子进程的 PID 和终止状态,令父进程得知谁因何而死。
    3、为子进程收尸,防止大量僵尸进程耗费系统资源。
    以上三个任务中,即使前两个与具体需求无关,仅仅第三个也足以凸显 wait 函数的重要性,尤其是对那些多进程服务器型的应用而言。
    僵尸进程的产生,上一篇有讲,参看:UNIX再学习 -- 函数 fork 和 vfork
    下面简单用示例介绍一下僵尸进程:
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
      
      
    int main (void)  
    {  
        pid_t pid;  
      
        pid = fork ();  
        if  (pid == -1)  
            perror ("fail to fork"), exit (1);  
        else if (pid == 0)  
        {  
            printf ("这是子进程 pid = %d", getpid ());  
            printf ("父进程的 ppid = %d\n",  getppid ());  
        }  
        else  
        {  
            //while (1);  
            sleep (10); 
            printf ("这是父进程 ppid = %d\n", getpid ());  
      
        }  
      
        return 0;  
    }
    实验:
    在一个终端执行 ./a.out
    # ./a.out 
    这是子进程 pid = 2868父进程的 ppid = 2867 
    (十秒后)
    这是父进程 ppid = 2867
    
    在另一个终端,查看进程信息
    # ps -C a.out -o ppid,pid,stat,cmd
     PPID   PID STAT CMD
     2366  2867 S+   ./a.out
     2867  2868 Z+   [a.out] <defunct>
    结论:
    虽然,子进程已经结束,但是父进程仍未终止,没有回收子进程的退出状态,子进程即成为僵尸进程。
    而父进程调用 wait,会终止子进程的僵尸态,示例说明:
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    
    int main (void)  
    {  
        pid_t pid, pr;  
      
        pid = fork ();  
        if  (pid == -1)  
            perror ("fail to fork"), exit (1);  
        else if (pid == 0)  
        {  
            printf ("这是子进程 pid = %d", getpid ());  
            printf ("父进程的 ppid = %d\n",  getppid ());  
        }  
        else  
        {  
    		pr = wait (NULL);
            //while (1);  
            sleep (10); //可以保证子进程先被调度  
            printf ("这是父进程 ppid = %d\n", getpid ());  
      
        }  
      
        return 0;  
    }  
    
    实验:
    在一个终端执行 ./a.out
    # ./a.out 
    这是子进程 pid = 2889父进程的 ppid = 2888
    (十秒后)
    这是父进程 ppid = 2888
    
    在另一个终端,查看进程信息
    # ps -C a.out -o ppid,pid,stat,cmd
     PPID   PID STAT CMD
    
    结论:
    可以发现,确实子进程的僵尸态没有了。不过需要注意的是,wait 使用的位置。
    上面有这样一句话,如果一个子进程在 wait  函数套用之前,已经终止并处于僵尸状态,所以说,子进程必须在 wait 函数套用之前结束。如果,wait 先于子进程,是没有效果的。
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
      
      
    int main (void)  
    {  
        pid_t pid, pr;  
        pr = wait (NULL);
      
        pid = fork ();  
        if  (pid == -1)  
            perror ("fail to fork"), exit (1);  
        else if (pid == 0)  
        {  
            printf ("这是子进程 pid = %d", getpid ());  
            printf ("父进程的 ppid = %d\n",  getppid ());  
        }  
        else  
        {  
    		
            //while (1);  
            sleep (10); 
            printf ("这是父进程 ppid = %d\n", getpid ());  
      
        }  
      
        return 0;  
    }  
    实验:
    在一个终端执行 ./a.out
    # ./a.out 
    这是子进程 pid = 2932父进程的 ppid = 2931
    (十秒后)
    这是父进程 ppid = 2931
    
    在另一个终端,查看进程信息
    # ps -C a.out -o ppid,pid,stat,cmd
     PPID   PID STAT CMD
     2366  2931 S+   ./a.out
     2931  2932 Z+   [a.out] <defunct>
    
    再有一个,上述例子中都是使用的 wait (NULL),如果参数非 NULL,则可以输出子进程终止状态。
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
      
      
    int main (void)  
    {  
        pid_t pid, pr; 
       int status = 0;	
      
        pid = fork ();  
        if  (pid == -1)  
            perror ("fail to fork"), exit (1);  
        else if (pid == 0)  
        {  
            printf ("这是子进程 pid = %d", getpid ());  
            printf ("父进程的 ppid = %d\n",  getppid ());  
    		_exit (100);
        }  
        else  
        {  
    		int status = 0;	
    		pr = wait (&status);
    
            //while (1);  
            sleep (10); //可以保证子进程先被调度  
            printf ("这是父进程 ppid = %d\n", getpid ());  
    		printf ("status = %d, pr = %d\n", status, pr);
      
        }  
      
        return 0;  
    }  
    输出结果:
    这是子进程 pid = 3078父进程的 ppid = 3077
    这是父进程 ppid = 3077
    status = 25600, pr = 3078
    
    总结:
    子进程异常退出(_exit (100)),通过 wait 函数参数 status 获得终止状态。

    3》》子进程的终止状态通过 wait 函数的 status 参数输出给该函数调用者。<sys/wait.h> 头文件提供了几个辅助分析进程终止状态的工具宏。

    查看: /usr/include/i386-linux-gnu/sys/wait.h 
    /* This will define all the `__W*' macros.  */
    # include <bits/waitstatus.h>
    
    # define WEXITSTATUS(status)	__WEXITSTATUS (__WAIT_INT (status))
    # define WTERMSIG(status)	__WTERMSIG (__WAIT_INT (status))
    # define WSTOPSIG(status)	__WSTOPSIG (__WAIT_INT (status))
    # define WIFEXITED(status)	__WIFEXITED (__WAIT_INT (status))
    # define WIFSIGNALED(status)	__WIFSIGNALED (__WAIT_INT (status))
    # define WIFSTOPPED(status)	__WIFSTOPPED (__WAIT_INT (status))
    讲解:
    参看:WAIT(2)
    WIFEXITED (status)   (常用)
    判断子进程是否正常终止,是则为真。
    WIEXITSTATUS (status)  (常用)
    获取子进程调用 exit、_exit、Exit 函数时所传入的参数或者 main 函数中 return 语句返回值的低 8 位。
    WIFSIGNALED (status)
    判断子进程是否异常终止,是则为真 
    WTERMSIG (status) 
    宏获取导致子进程异常终止的信号。
    WIFSTOPPED (status)
    判断当前暂停子进程是否返回,是则为真。
    WSTOPSIG (status)
    获取使子进程暂停的信号。
    示例说明:
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main (void)
    {
    	int status;
    	pid_t pc,pr;
    	pc =  fork();
    	if (pc < 0)
    		printf("error ocurred!\n");
    	else if(pc == 0)
    	{
    		printf("This is child process with pid of %d\n",getpid());
    		exit (3);
    	}
    	else
    	{
    		pr = wait(&status);
    		printf ("status = %d\n", status);
    		printf ("status >> 8 = %d\n", status >> 8);
    		if(WIFEXITED(status))
    		{
    			printf("The child process %d exit normally.\n",pr);
    			printf("The WEXITSTATUS return code is %d.\n",WEXITSTATUS(status));
    			printf("The WIFEXITED return code is %d.\n",WIFEXITED(status));
    		}
    		else
    			printf("The child process %d exit abnormally.\n",pr);
    	}
    	return 0;
    }
    输出结果:
    This is child process with pid of 3370
    status = 768
    status >> 8 = 3
    The child process 3370 exit normally.
    The WEXITSTATUS return code is 3.
    The WIFEXITED return code is 1.
    示例解析:
    通过示例可以看出 wait 返回子进程 ID;WEXITSTATUS (status) 返回 exit (3) 参数 3;子进程正常终止,WIFEXITED 为真。 

    思考:其中的我添加打印了 status 的值为 768,以及status >> 8 的值为 3,啥意思? status 到底是什么?
    我们上面说了,status 是一个整型指针,如果 status 不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。那终止状态是什么,怎么表示的?
    再有,获取子进程调用 exit、_exit、Exit 函数时所传入的参数或者 main 函数中 return 语句返回值的低 8 位
    这句话就说明了,status 是 exit、_exit、Exit 函数时所传入的参数或者 main 函数中 return 语句返回值的低 8 位。
    那么 低 8 位、高 8 位 又是怎么回事?  
    下面开始讲解下:
    exit、wait、WEXITSTATUS 中的 status 关系
    (1)exit 函数参数 status 为整型,称为终止状态(或退出状态,exit status)。
    参看:Exit Status
    查看 /linux-2.6.37.1/kernel/exit.c
    SYSCALL_DEFINE1(exit, int, error_code)
    {
    	do_exit((error_code&0xff)<<8);
    }
    
    查看函数NORET_TYPE void do_exit(long code)
    有 tsk->exit_code = code;
    
    所以子进程的返回值就是这么确定的,它的低8位为零,次低8位为真正退出码。
    (2)wait 函数参数 status 为整型指针。
    高8位 记录进程调用exit退出的状态(正常退出);
    低8位 记录进程接受到的信号 (非正常退出)
    如果正常退出(exit) ---高8位是退出状态号,低8位是0
    如果非正常退出(signal)----高八位是0,低8位是siganl id


    (3)再查看 /usr/include/i386-linux-gnu/bits/waitstatus.h 获得 __WEXITSTATUS(status) 定义。
    /* Everything extant so far uses these same bits.  */
    
    /* If WIFEXITED(STATUS), the low-order 8 bits of the status.  */
    #define	__WEXITSTATUS(status)	(((status) & 0xff00) >> 8)
    
    /* If WIFSIGNALED(STATUS), the terminating signal.  */
    #define	__WTERMSIG(status)	((status) & 0x7f)
    
    /* If WIFSTOPPED(STATUS), the signal that stopped the child.  */
    #define	__WSTOPSIG(status)	__WEXITSTATUS(status)
    
    /* Nonzero if STATUS indicates normal termination.  */
    #define	__WIFEXITED(status)	(__WTERMSIG(status) == 0)
    
    /* Nonzero if STATUS indicates termination by a signal.  */
    #define __WIFSIGNALED(status) \
      (((signed char) (((status) & 0x7f) + 1) >> 1) > 0)
    
    /* Nonzero if STATUS indicates the child is stopped.  */
    #define	__WIFSTOPPED(status)	(((status) & 0xff) == 0x7f)
    
    /* Nonzero if STATUS indicates the child continued after a stop.  We only
       define this if <bits/waitflags.h> provides the WCONTINUED flag bit.  */
    #ifdef WCONTINUED
    # define __WIFCONTINUED(status)	((status) == __W_CONTINUED)
    #endif
    
    /* Nonzero if STATUS indicates the child dumped core.  */
    #define	__WCOREDUMP(status)	((status) & __WCOREFLAG)
    总结一下:
    wait 参数整型指针 (int *status)高8位记录进程调用 exit 退出的状态(即exit 的参数status)(正常退出);低8位 记录进程接受到的信号 (非正常退出)。WEXITSTATUS 返回值为((status) & 0xff00) >> 8,即 exit 退出状态。
    简单点来表示就是:  exit (3) --> wait (&(3<<8 ))--> WEXITSTATUS (3<<8) = ((3<<8)&0xff) >>8 = 3

    2、waitpid 函数

    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t waitpid(pid_t pid, int *status, int options);
    成功返回所回收子进程的 PID 或 0,失败返回 -1

    (1)函数功能

    等待并回收任意或特定子进程

    (2)参数解析

    第一个参数:进程号,可取以下值。
    pid < -1     等待并回收特定进程组(由 pid标识)的任意子进程
    pid == -1  等待并回收任意子进程,相当于 wait 函数  (掌握)
    pid == 0    等待并回收与调用进程同进程组的任意子进程
    pid > 0      等待并回收特定子进程(由pid标识) (掌握)
    第二个参数:输出子进程的终止状态,可置 NULL
    第三个参数:选项,可取以下值 (默认给 0 即可)
    0    阻塞模式,若所等子进程仍在运行,则阻塞,直至其终止。  (掌握)
    WNOHANG   非阻塞模式,若所等子进程仍在运行,则返回 0  (掌握)
    WCONTINUED  若实现支持作业控制,那么由 pid 指定的任一子进程在停止后已经继续,但其装填尚未报告,则返回其状态。
    WUNTRACED  若某实现支持作业控制,而由 pid 指定的任一子进程已处于停止状态,并且其状态自停止以来还未报告过,则返回其状态。WIFSTOPPED 宏确定返回值是否对应于一个停止的子进程。

    (3)函数解析

    1》》回收特定子进程

    事实上,无论一个进程是正常终止还是异常终止,都会通过系统内核向其父进程发送 SIGCHLD (17)信号。父进程可以忽略该信号,也可以提供一个针对该信号的处理函数,在信号处理函数中以异步的方式回收子进程。这样做不仅流程简单,而且僵尸的存货时间短,回收效率高。
    其中 error 等于 ECHILD 表示没有需要等待的子进程。
    示例说明:
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <errno.h>
    
    int main (void)
    {
    	pid_t pid;
    	if ((pid = fork ()) < 0)
    		perror ("fork"), exit (1);
    	else if (pid == 0)
    	{
    		printf ("这是子进程 pid = %d\n", getpid ());
    		printf ("父进程的 pid = %d\n", getppid ());
    		exit (0);
    	}
    	else 
    		printf ("这是父进程 pid = %d\n", getpid ());
    	
    	//回收特定子进程
    	pid = waitpid (pid, NULL, 0);
    	if (pid == -1)
    	{
    		if (errno != ECHILD)
    			perror ("waitpid"), exit (1);
    	}
    	else
    		printf ("%d子进程终止\n", pid);
    
    	printf ("%d父进程终止\n", getpid ());
    	return 0;
    }
    输出结果:
    这是父进程 pid = 2892
    这是子进程 pid = 2893
    父进程的 pid = 2892
    2893子进程终止
    2892父进程终止
    

    2》》非阻塞模式回收所有子进程

    waitpid函数以非阻塞模式运行时,可以等待并回收所有子进程,等待的同时做空闲处理。
    示例说明:
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <errno.h>
    
    int main (void)
    {
    	pid_t pid1;
    	if ((pid1 = fork ()) < 0)
    		perror ("fork"), exit (1);
    	if (pid1 == 0)
    	{
    		printf ("这是子进程pid1 = %d", getpid ());
    		printf ("父进程pid1 = %d\n", getppid ());
    		exit (0);
    	}
    	pid_t pid2;
    	if ((pid2 = fork ()) < 0)
    		perror ("fork"), exit (1);
    	if (pid2 == 0)
    	{
    		printf ("这是子进程pid2 = %d", getpid ());
    		printf ("父进程pid2 = %d\n", getppid ());
    		exit (0);
    	}
    
    	//sleep (2);
    	printf ("这是父进程pid = %d\n", getpid ());
    
    	while (1)
    	{
    		pid_t pid = waitpid (-1, NULL, WNOHANG);
    		if (pid == -1)
    		{
    			if (errno != ECHILD)
    				perror ("waitpid"), exit (1);
    			printf ("子进程都死光了\n");
    			break;
    		}
    		if (pid)
    			printf ("%d子进程终止\n", pid);
    		else 
    			printf ("在这里进行空闲处理\n");、
    		//表示所等子进程仍在运行,此时父进程出现空闲时间,可在这里进行空闲处理。
    	}
    	return 0;
    }
    输出结果:
    这是父进程pid = 3029
    在这里进行空闲处理
    在这里进行空闲处理
    ....
    在这里进行空闲处理
    在这里进行空闲处理
    这是子进程pid2 = 3031父进程pid2 = 3029
    这是子进程pid1 = 3030父进程pid1 = 3029
    在这里进行空闲处理
    3031子进程终止
    3030子进程终止
    子进程都死光了
    

    3》》通过 WUNTRACED 和 WCONTINUED 支持作业控制 (了解)

    这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
    ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

    4》》使用2次 fork 避免僵尸进程

    如果一个进程 fork 一个子进程,但不要它等待子进程终止,也不希望子进程处于僵尸状态直到父进程终止,实现这一要求的诀窍是调用 fork 两次。
    #include <stdio.h>  
    #include <sys/wait.h>  
    #include <stdlib.h>  
      
    int main(void)
    {  
    	pid_t pid;  
    	if((pid = fork()) < 0)
    		perror("fork"), exit (1);
    	else if(pid == 0)
    	{  
    		if((pid = fork()) < 0)  
    			perror("fork"), exit (1);
    		else if(pid > 0)
    			exit(0);  
    		else
    		{  
    			sleep(2);  
    			printf("second child,parent pid = %d\n",getppid());  
    			exit(0);  
    		}  
    	}
      
    	if(waitpid(pid,NULL,0) != pid) //回收第一次fork的子进程,但是第二个没有回收  
    		perror("waitpid"), exit (1); 
    
    	return 0;  
    }  
    输出结果:
    second child,parent pid = 1

    3、wait 和 waitpid 函数区别

    (1)在一个子进程终止前,wait 使其调用者阻塞,而 waitpid 有一选项 WNOHANG ,可使调用者不阻塞。
    (2)waitpid 并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
    (说的是 waitpid 第一个参数选项,如等待特定的子进程)
    (3)waitpid 通过 WUNTRACED 和 WCONTINUED 支持作业控制。 
    (4)wait(&status) 等同于 waitpid(-1, &status, 0);

    4、wait 与 sleep 区别

    我们上面简单提到了一下两者都可以避免产生孤儿进程。但是有什么区别呢,下面我们简单介绍下。
    讲之前先说一下优先级,if (pid = fork () < 0)   对不对? 
    答案是错误的。 '<' 的优先级 高于 '=',所有应该是加括号  if ((pid = fork ()) < 0)  

    (1)使用 wait 函数,避免孤儿进程

    #include <stdio.h>  
    #include <unistd.h>  
    #include <stdlib.h>  
      
    int main (void)  
    {  
        pid_t pid, pr;  
        if ((pid = fork ()) < 0)  
            perror ("fork"), exit (1);  
        else if (pid == 0)  
        {  
            sleep (3);   
            printf ("这是子进程 pid = %d", getpid ());  
            printf ("父进程的 ppid = %d\n",  getppid ());  
    		exit (0);
        }  
        else   
        {  
    		pr = wait (NULL);
            printf ("这是父进程 ppid = %d\n", getpid ());  
        }  
        return 0;  
    }  
    
    输出结果:
    这是子进程 pid = 3603父进程的 ppid = 3602
    这是父进程 ppid = 3602
    

    (2)使用 sleep,避免孤儿进程

    #include <stdio.h>  
    #include <unistd.h>  
    #include <stdlib.h>  
      
    int main (void)  
    {  
        pid_t pid;  
        if ((pid = fork ()) < 0)  
            perror ("fork"), exit (1);  
        else if (pid == 0)  
        {  
            sleep (3);   
            printf ("这是子进程 pid = %d", getpid ());  
            printf ("父进程的 ppid = %d\n",  getppid ());  
    		exit (0);
        }  
        else   
        {  
    		sleep (5);
            printf ("这是父进程 ppid = %d\n", getpid ());  
        }  
        return 0;  
    }  
    
    输出结果:
    这是子进程 pid = 3611父进程的 ppid = 3610
    这是父进程 ppid = 3610
    但是,如果你注意看输出时的等待时间,就会发现。
    wait 等待 3 秒然后,同时输出结果。而 sleep 先等待 3 秒,打印子进程,然后再等待 2 秒打印父进程。
    所以说,sleep 是休眠指定时间,到时间继续往下执行。而 wait 是等待,需要被触发才能继续往下执行
    当然,wait 还有其他功能,是 sleep 没有的。上面讲的很清楚了,就不一一说明了。
    这里只是简单提一句,等讲到线程、Linux部分,可以继续深入探讨。

    三、总结

    第八章东西有点多,一章拆成了几篇文章来写,看的节奏有点慢。最近也有点分心了,周末出去玩没问题,但是占用休息和本来用来学习的时间用在修图上,这就不该了。收起玩心,抓紧时间复习吧!
    展开全文
  • 一、无参函数 没有参数的函数,直接调用实现某些功能。 函数编写在脚本中,与其他命令一起存储,但是函数必须定义在脚本的最开始部分; 也就是说,包含函数的脚本中,所有的函数都得定义在脚本的最开始部分; ...

    一、无参函数

    没有参数的函数,直接调用实现某些功能。

    函数编写在脚本中,与其他命令一起存储,但是函数必须定义在脚本的最开始部分;

    也就是说,包含函数的脚本中,所有的函数都得定义在脚本的最开始部分;

    然后在定义函数之后调用或者在其他脚本中引用这些定义的函数。

    实例1、下面是一个简单的自定义函数,求1到10的和:

    pg no_param_test
    #!/bin/ksh
    # 测试无参自定义函数
    # author:_yeeXun
    # date  :2013-3-4 8:37:29
    
    no_param_test() {
    SUM=0
    #for i in { 1..10 }
    for i in 1 2 3 4 5 6 7 8 9 10
    do
      echo $i
      SUM=`expr $SUM + $i`
      i=`expr $i + 1`
      if [ $i -eq 11 ]; then
        echo "Sum:$SUM"
      fi
    done
    }
    no_param_test
    # EOF
    执行脚本:
    sh no_param_test
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Sum:55

    二、有参函数

    带参数的自定义函数,在高级语言(如,C,JAVA,C#等)中,参数都是在函数后面的括号中;

    而shell中则在函数体内检查参数个数。在调用函数的时候,直接在后面跟着参数即可。

    无论参数有多少个,都紧跟在函数名后面。

    1、单个参数

    实例2、脚本如下:

    pg direc_check
    #!/bin/ksh
    
    is_directory() {
    _DIR_NAME=$1
    # check params
    if [ $# -lt 1 ]; then
      echo "is_directory:I need a directory name to check"
      return 1
    fi
    
    # check
    if [ ! -d $_DIR_NAME ]; then
      return 1
    else
      return 0
    fi
    }
    
    error_msg() {
    echo "**********"
    echo $@
    echo "**********"
      return 0
    }
    
    echo -n "Enter destination directory:"
    read DIR
    if is_directory $DIR
    then 
      echo "$DIR is a directory and exists in $PWD"
    else
      error_msg "$DIR does not exist ... creating it now"
      mkdir $DIR > /dev/null 2>&1
      if [ $? -ne 0 ]; then
        error_msg "Could not create directory::chech it out!"
        exit 1
      else :
      fi
    fi
    
    # author:_yeeXun
    # date  :2013-3-2 15:27:57
    # EOF

    这里函数中# check params部分就是在做参数检查,这里$#表示输入的参数个数。

    下面是执行这个脚本:

    sh direc_check
    Enter destination directory:test
    **********
    test does not exist ... creating it now
    **********
    再次执行时候,输入同样的名称,则会提示此目录已经存在。
    sh direc_check
    Enter destination directory:test
    test is a directory and exists in /usr/b4nx/user/ytcclb

    我们在添加一个目录,通过awk语言查看当前目录下通过此脚本创建的目录:

    ls -l | awk '{if($0~/^d/) print $0}'
    drwxr-xr-x   2 b4nx     group        512 Mar  2 15:32 call_fun_test
    drwxr-xr-x   2 b4nx     group        512 Mar  2 15:04 test

    2、多个参数

    不管是单个参数,还是多个参数,都通过检查判断,这里$#表示输入的参数个数;

    $1表示输入的第一个参数,$2表示第二个,$3表示第四个,以此类推。

    下面是一个截取字符串的函数:

    实例3、pg chop

    #!/bin/ksh
    # chop
    # to call:chop string how_many_chars_to_chop
    chop() {
    _STR=$1
    _CHOP=$2
    
    # awk's substr starts at 0,wo need to increment by one
    CHOP=`expr $_CHOP + 1`
    
    # check we have two params
    if [ $# -ne 2 ]; then
      echo "check_length:I need a string and how many characters to chop"
      return 1
    fi
    
    # check the length of the string first
    _LENGTH=`echo $_STR | awk '{print length($0)}'`
    
    if [ "$_LENGTH" -lt "$_CHOP" ]; then
      echo "The length of the string is short"
      return 1
    fi
    
    echo $_STR | awk '{print substr($1,'$_CHOP')}'
    }
    
    # call the function
    echo -n "Enter the string:"
    read STR
    echo -n "Enter the start position:"
    read LEN
    chop $STR $LEN
    
    # author:_yeeXun
    # date  :2013-3-2 14:17:34
    # EOF

    这个脚本中,定义了一个函数chop截取字符串,2个参数,分别用$1,$2表示;

    $1表示被截取的字符串,$2表示截取的起始位置。执行如下:

    sh chop
    Enter the string:string123
    Enter the start position:5
    ng123

    sh chop
    Enter the string:_yeexunInCSDN
    Enter the start position:10
    CSDN

    三、函数返回值

    函数的返回值表示执行函数的一个状态,

    1、return 0:正常返回。

    2、return 1:返回错误。

    3、return  :最后的命令执行状态决定返回值,可使用$?检测。


    四、函数调用
    1、同脚本

    脚本中,函数必须在脚本一开始就定义,定义之后才可调用。

    同一脚本中,在顶部定义函数,定义完后再调用,如实例1、实例2、实例3。

    2、脚本间

    可以将这些函数定义在同一个脚本中,作为公共使用的函数脚本,然后再需要调用的脚本中引用此脚本即可。

    引用方式如下:<dot><space><directory><script>

    实例4、函数调用 — 跨脚本

    函数脚本:

    pg direc_check.sh
    #!/bin/ksh
    
    is_directory() {
    _DIR_NAME=$1
    
    # check params
    if [ $# -lt 1 ]; then
      echo "is_directory:I need a directory name to check"
      return 1
    fi
    
    # check
    if [ ! -d $_DIR_NAME ]; then
      return 1
    else
      return 0
    fi
    }
    
    error_msg() {
    echo "**********"
    echo $@
    echo "**********"
      return 0
    }
    
    # 多行注释方式::<<' ... '
    :<<'
    echo -n "Enter destination directory:"
    read DIR
    if is_directory $DIR
    then
      echo "$DIR is a directory and exists in $PWD"
    else
      error_msg "$DIR does not exist ... creating it now"
      mkdir $DIR > /dev/null 2>&1
      if [ $? -ne 0 ]; then
        error_msg "Could not create directory::chech it out!"
        exit 1
      else :
      fi
    fi
    '
    # author:_yeeXun
    # date  :2013-3-2 15:27:57
    # EOF

    调用此脚本中函数的脚本:

    pg fun_test
    #!/bin/ksh
    
    # call the function outside the script is_drectory
    # <dot><space><directory with functions><script with functions>
    . $HOME/user/ytcclb/direc_check.sh
    
    echo -n "Enter destination directory:"
    read DIR
    if is_directory $DIR
    then
      echo "$DIR is a directory and exists in $PWD"
    else
      error_msg "$DIR does not exist ... creating now"
      mkdir $DIR > /dev/null 2>&1
      if [ $? -ne 0 ]; then
        error_msg "Could not create directory::check it out"
        exit 1
      else :
      fi
    fi
    # author:_yeeXun
    # date  :2013-3-2 15:49:12
    # EOF

    当输入一个已存在的目录时候:

    sh fun_test
    Enter destination directory:test
    test is a directory and exists in /usr/b4nx/user/ytcclb
    输入不存在的目录名:

    sh fun_test
    Enter destination directory:dir_test
    **********
    dir_test does not exist ... creating now
    **********

    查看当前目录下所有目录名:

    ls -l | awk '{if($0~/^d/) print $0}'
    drwxr-xr-x   2 b4nx     group        512 Mar  2 15:32 call_fun_test
    drwxr-xr-x   2 b4nx     group        512 Mar  4 14:16 dir_test
    drwxr-xr-x   2 b4nx     group        512 Mar  2 15:04 test

    --the end--

    展开全文
  • php 中大括号的规范

    2015-08-10 16:49:28
    在三种主要的大括号放置规则中,有两种是可以接受的,如下的第一种是最好的: 将大括号放置在关键词下方的同列处: ...传统的UNIX括号规则是,首括号与关键词同行,尾括号与关键字同列: if (condition) {  ...
  • 用于内存管理的malloc/free这对函数,对于使用C语言的程序员应该很熟悉。前段时间听说有的IT公司以“实现一个简单功能的malloc”作为面试题,正好最近在复习K&R,上面介绍了使用UNIX接口的实现,因此花了些时间仔细...
  • R的源起 R是S语言的一种实现。S语言是由 AT&T贝尔实验室开发的一种用来进行数据探索、统计分析、作图的解释型语言。最初S语言的实现版本主要是S-PLUS。S-PLUS是一个商业 软件,它基于S语言,并由MathSoft公司的统计...
  • 最主要的是熟悉语法格式和各种常用函数,再结合以前所学的编程语言,来快速消化并掌握它。  工欲善其事,必先利其器。所以熟悉了语法格式后,常用函数则需要有个大概了了解,方便用时来查。下面是我收集的常用...
  • 【C++】花括号作用域

    2017-11-26 08:48:14
    括号作用域 //作用域,可以看作一个变量的有效使用区域 //函数外部也是一个大的作用域,暂且记作:作用域0 void Fun() { //函数内部作用域,暂且记作:作用域1 while() { //循环内部,暂且记作:作用域2 ...
  • 1969年,Unix初始,没有fork,没有exec,没有pipe,没有 “一切皆文件” ,但是那时它已经是Unix了。它简单,可塑。 Melvin Conway在1963年的论文中叙述fork思想时就解释说并行路径要用结果来交互,也就是在汇合的...
  • 1.ORACLE先创建函数方法,再直接使用,MySQL直接使用方法UNIX_TIMESTAMP,FROM_UNIXTIME oracle_to_unix(create_date) create or replace function oracle_to_unix(in_date in date) return number is begin...
  • //4.2 数字绝对值数字ceil(): 进一法取整echo ceil(9.999); // 10 浮点数进一取整floor(): 舍去法取整 echo floor(9.999); // 9 浮点数直接舍去小数部分fmod(): 浮点数取余 $x = 5.7; $y = 1.3; // 两个浮点数
  • 2.4函数由于函数用于创建C++程序的模块,在C++的OOP定义至关重要,因此必须熟悉它。含火速的某些方面术语高级主题,将在第七章和第八章重点讨论函数。然而,现在了解函数的一些基本特性,将使得在以后的函数学习中...
  • mysql数据库中的函数

    2018-10-30 09:57:25
    Mysql中的函数主要有数学函数,字符串函数,日期函数,时间函数以及条件判断函数、系统信息函数和加密函数等。 1、流程控制函数  流程控制函数主要用于根据满足条件的不同,执行相应的流程,mysql中流程控制函数...
  • 本文详细介绍了hive窗口函数,即包括开窗函数和分析函数两部分。
  • 分清函数指针和指针函数 关于指针和数组斩不断理还乱的恩怨还真是说了不少,不过现在应该已经理清了。有了上一讲的基础,本讲的内容相对来说就比较容易理解了。 1.指向函数的指针(函数指针) 来分析这样一个声明...
  • UNIX再学习 -- shell编程

    2017-03-23 09:49:41
    UNIX环境高级编程看了三章,遇到不少重定向等shell命令。本想到Linux时再讲,看来有必要提前了。之前有看过一本《嵌入式Linux软硬件开发详解》这本书里有简单介绍了一部分shell常用命令,就结合它来简单介绍下shell...
  • main()函数参数的意义

    2015-07-27 10:47:48
    main(int argc,char *argv[ ]) argv为指针的指针 argc为整数 char **argv or: char *argv[] or: char argv[][] ...main()括号内是固定的写法。 下面给出一个例子来理解这两个参数的用法: 假设程序的名称为prog,
1 2 3 4 5 ... 20
收藏数 40,544
精华内容 16,217
关键字:

unix 函数括号数字