APUE笔记六

1. fork函数

    fork函数用于创建一个新进程,但返回两个值:子进程返回0,而父进程返回的是子进程的ID。但子进程是父进程的副本,并不共享存储空间,只共享正文段。

#include <stdio.h>
#include <unistd.h>

int     glob = 6;
char    buf[] = "a write to stdout\n";
int main( void )
{
        int     var;
        pid_t   pid;

        var = 88;
        if ( write( STDOUT_FILENO, buf, sizeof( buf ) - 1 ) != sizeof( buf ) - 1 )
                printf("write error\n");
        printf("before fork\n");

        if ( ( pid = fork() ) < 0 )
                printf("fork error\n");
        else if ( pid == 0 ){
                glob++;
                var++;
        } else{
                sleep( 2 );
        }

        printf("pid=%d,glob=%d,var=%d\n", getpid(), glob, var);
        return 0;
}
程序输出:

    重定向到终端,则缓冲区会被flush,而重定向到文件,则不会。

2. fork函数的文件共享:

#include <stdio.h>
#include <unistd.h>

int main( void )
{
        FILE *file = fopen("temp", "w+");
        pid_t   pid;
        char    buf1[] = "abcdefghi\n";
        char    buf2[] = "ABCDEFGHI\n";

        if ( fputs( buf1, file ) == EOF ){
                printf("write error\n");
        }
        if ( ( pid = fork() ) < 0 ){
                printf("fork error\n");
        }
        else if ( pid == 0 ){
                if ( fputs( "child:\n", file ) == EOF ){
                        printf("child write error\n");
                }
                if ( fputs( buf2, file ) == EOF ){
                        printf("child write error\n");
                }
        }
        else{
                sleep( 2 );
                if ( fputs("parent:\n", file ) == EOF ){
                        printf("parent write error\n");
                }
                if ( fputs( buf1, file ) == EOF ){
                        printf("write error\n");
                }
        }
        return 0;
}

程序输出:

    备注:这里注意child和parent两个字符出现的位置。子进程完全是父进程的副本!

fork有下面两种用法:

1) 一个父进程希望复制自己,使父子进程同时执行不同的代码段。(网络编程的应用)

2) 一个进程要执行一个不同的程序


3. vfork函数

    与fork函数有以下两点不同:

1) vfork与fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec或exit,于是也就不会访问该地址空间。

2) vfork保证子进程先运行

#include <stdio.h>
#include <unistd.h>

int     glob = 6;
int main( void )
{
        int     var;
        pid_t   pid;
        var = 88;
        printf("before vfork\n");
        if ( ( pid = vfork() ) < 0 ){
                printf("vfork error\n");
        }else if ( pid == 0 ){
                glob++;
                var++;
                _exit( 0 );
        }

        printf("pid=%d, glob=%d, var=%d\n", getpid(), glob, var );
        return 0;
}

程序输出:



4. wait和waitpid函数

    内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到这些信息。这些信息至少包括进程ID,该进程的终止状态,以及该进程使用的CPU时间总量。

    当调用wait或waitpid的进程可能会发生如下情况:

1) 如果其所有子进程都还在运行,则阻塞

2) 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回

3) 如果它没有任何子进程,则立即出错返回

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

int main( void )
{
        pid_t   pid;
        int     status;

        if ((pid = fork()) < 0)
                printf("fork error\n");
        else if ( pid == 0 ){
                printf("wait函数会阻塞,直到子进程终止,并捕捉子进程的终止信息!这样也可以保证子进程比父进程先执行。\n");
                sleep( 2 );
        }

        if ( wait(&status) != pid )
                printf("wait error\n");

        if ( pid > 0 ){
                printf("main done\n");
        }
        return 0;
}

程序输出:



waitpid的优势:

1) waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态。

2) waitpid提供了一个wait的非阻塞版本。

3) waitpid支持作业控制

#include <sys/wait.h>
#include <unistd.h>

int main( void )
{
        pid_t   pid1;
        pid_t   pid2;

        if ( ( pid1 = fork()) < 0 ){
                printf("fork error\n");
        }
        else if ( pid1 == 0 ){
                printf("fork 1....\n");
                exit( 0 );
        }

        if ( ( pid2 = fork() ) < 0 ){
                printf("fork error\n");
        }
        else if ( pid2 == 0 ){
                printf("fork 2....\n");
                exit( 0 );
        }

        if ( waitpid( pid2, NULL, 0 ) != pid2 ){
                printf("fork 2 exit error\n");
        }
        else{
                printf("fork 2 exit ok\n");
        }

        if ( waitpid( pid1, NULL, 0 ) != pid1 ){
                printf("fork 1 exit error\n");
        }
        else{
                printf("fork 1 exit ok\n");
        }

        return 0;
}

程序输出:



5. 竞争条件

    父进程/子进程之间的相互竞争:

#include <stdio.h>
#include <sys/wait.h>

static void charatatime( char * );
int main( void )
{
        pid_t   pid;
        if ( ( pid = fork() ) < 0 ){
                printf("fork error\n");
        }
        else if ( pid == 0 )
                charatatime("output from child\n");
        else{
                charatatime("output from parent\n");
        }

        return 0;
}

static void charatatime( char *str )
{
        char    *ptr;
        int     c;
        setbuf( stdout, NULL );
        for ( ptr = str; ( c = *ptr++ ) != 0; )
                putc( c, stdout );
}

程序输出有点乱:

 

6. exec函数

    exec函数通常用于执行可执行文件,所以在fork后直接运行较好。我们先编写一个用于exec执行的程序echoall.c:

#include <stdio.h>

int main( int argc, char *argv[] )
{
        int     i;
        char    **ptr;
        extern char **environ;

        for ( i = 0; i < argc; i++ )
                printf("argv[%d]:%s\n", i, argv[ i ] );
        for ( ptr = environ; *ptr != 0; ptr++ )
                printf("%s\n", *ptr );

        return 0;
}
然后编译如下:

cc -o echoall echoall.c

    则echoall为可执行文件,然后我们就可以编写exec程序文件:

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
char    *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };

int main( void )
{
        pid_t   pid;
        if ( ( pid = fork() ) < 0 )
                printf("fork error\n");
        else if ( pid == 0 ){
                if ( execle( "/home/leichaojian/echoall","echoall","myarg1","MY ARG2", ( char * )0, env_init ) < 0 )
                        printf("execle error\n");
        }
        if ( waitpid( pid, NULL, 0 ) < 0 )
                printf("wait error\n");
        if ( ( pid = fork() ) < 0 )
                printf("fork error\n");
        else if ( pid == 0 ){
                if ( execlp( "/home/leichaojian/echoall","echoall","only 1 arg",( char * )0 ) < 0 ){
                        printf("execlp error\n");
                }
        }
        return 0;
}
    其中/home/leichaojian可以通过指令pwd来得到。程序运行如下:



7. 解释器文件

    用一个程序来解释脚本语言(PS:找段时间学习shell编程)

echoall.c:

#include <stdio.h>

int main( int argc, char *argv[] )
{
        int     i;
        char    **ptr;
        extern char **environ;

        for ( i = 0; i < argc; i++ )
                printf("argv[%d]:%s\n", i, argv[ i ] );
//      for ( ptr = environ; *ptr != 0; ptr++ )
//              printf("%s\n", *ptr );

        return 0;
}

然后我们执行:

cc -o echoall echoall.c

shell.c:

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

int main( void )
{
        pid_t   pid;
        if ( ( pid = fork() ) < 0 )
                printf("fork error\n");
        else if ( pid == 0 ){
                if ( execl("/home/leichaojian/testinterp", "testinterp",
                        "myarg1","MY ARG2",(char *)0 ) < 0 )
                        printf("execl error\n");
        }
        if ( waitpid( pid, NULL, 0 ) < 0 )
                printf("waitpid error\n");
        return 0;
}
然后程序输出如下:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值