forks代码分析

forks代码在最下面.该文章主要针对fork()函数的分析
1.首先简述一下fork()函数的特性:调用一次,返回两次,父进程中返回子进程的ID,创建的子进程中返回的为0
2.这17个fork函数中,我把它们分类进行分析.
开始分析:
一.fork2,fork3为一类:该类单纯的应用了fork函数的性质,只要画出流程图,就能非常好理解了.下面以fork3为例简单分析:

void fork3()
{
    printf("L0\n");
    fork();
    printf("L1\n");    
    fork();
    printf("L2\n");    
    fork();
    printf("Bye\n");
}

在这里插入图片描述

二.fork0,fork1,fork4,fork5为一类,该类以if语句限制不同进程的指令执行.以fork4为例子进行分析:

void fork4()
{
    printf("L0\n");
    if (fork() != 0) {
 printf("L1\n");    
 if (fork() != 0) {
     printf("L2\n");
 }
    }
    printf("Bye\n");
}

文字分析:第一个if语句创建了一个子进程,并且如果是父进程则执行if内语句.如果是子进程则输出bye结束.第二个语句中又创建一个子进程,并且如果是父进程则执行if内的语句.如果是子进程则输出bye.
流程图:
在这里插入图片描述
(三)fork6单独为一类.

void cleanup(void) {
    printf("Cleaning up\n");
}
void fork6()
{
    atexit(cleanup);
    fork();
    exit(0);
}

分析:首先解释一下atexit函数,atexit函数调用后不返回,直接退出程序,其中的参数是void (*)(void).在该代码中,参数为cleanup,调用了cleanup函数.
执行结果:
在这里插入图片描述
(四)fork7,fork8为一类,该类用了无限循环语句,以fork8为例分析

void fork8()
{
    if (fork() == 0) {
 /* Child */
 printf("Running Child, PID = %d\n",
        getpid());
 while (1)
     ; /* Infinite loop */
    } else {
 printf("Terminating Parent, PID = %d\n",
        getpid());
 exit(0);
    }
}

分析:创建子进程后,子进程输出语句后无限循环,父进程正常终止.
执行结果:
在这里插入图片描述(四)fork9,fork10,fork11为一类,该类主用了wait函数.

void fork9()
{
      int child_status;
      if (fork() == 0) {
            printf("HC: hello from child\n");
            exit(0);
       } else {
                 printf("HP: hello from parent\n");
                 wait(&child_status);
                 printf("CT: child has terminated\n");
      }
      printf("Bye\n");
}     

分析:先解释一下wait函数的作用:等待子进程结束后,父进程再继续执行.该代码中wait的参数child_status存放子进程的退出状态.fork9函数执行流程如下:创建子进程后,子进程和父进程同时进行下去,父进程执行到wait函数时,等待子进程结束再往下进行.
执行结果:
在这里插入图片描述
HC,HP的位置可以互换,但一定在CT的前面.
我们在来看一个更为复杂的例子fork11.

#define N 5
void fork11()
{
    pid_t pid[N];
    int i;
    int child_status;
    for (i = 0; i < N; i++)
           if ((pid[i] = fork()) == 0)
                    exit(100+i); /* Child */
        for (i = N-1; i >= 0; i--) {
            pid_t wpid = waitpid(pid[i], &child_status, 0);
            if (WIFEXITED(child_status))
                  printf("Child %d terminated with exit status %d\n",
                       wpid, WEXITSTATUS(child_status));
            else
                   printf("Child %d terminate abnormally\n", wpid);
             }
      }

分析:在第一个for语句中创建了五个子进程,在第二个for语句中使用了wait函数,并且如果每个子进程正常结束,则输出子进程的ID和结束状态.
执行结果:
在这里插入图片描述
(五).fork12,fork13,fork14,fork15,fork16分为一类.该类于信号有关.
先看一个简单的例子:fork12

define N 5
void fork12()
{
    pid_t pid[N];
    int i;
    int child_status;
    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0) {
      /* Child: Infinite Loop */
      while(1);
      }
      for (i = 0; i < N; i++) {
      printf("Killing process %d\n", pid[i]);
      kill(pid[i], SIGINT);
      }
      for (i = 0; i < N; i++) {
      pid_t wpid = wait(&child_status);
      if (WIFEXITED(child_status))
                printf("Child %d terminated with exit status %d\n" ,wpid, WEXITSTAT 
                US(child_status));
      else
           printf("Child %d terminated abnormally\n", wpid);
       }
}

分析:在第一个for循环中,创建了5个子进程,并且让for循环陷入死循环.第二个for循环则用kill函数给子进程发送SIGINT的信号,即为终止子进程(异常终止).最后一个for循环中,我们可以通过执行结果看到所有子进程都异常终止.
执行结果:
在这里插入图片描述

再来看一个更为复杂,我自己也没有弄清的例子:fork14

define N 5
int ccount = 0;
void child_handler(int sig)
{
    int child_status;
    pid_t pid = wait(&child_status);
    ccount--;
    printf("Received SIGCHLD signal %d for process %d\n", sig, pid); /* Unsafe */
    fflush(stdout); /* Unsafe */
 }
 void fork14()
{
    pid_t pid[N];
    int i;
    ccount = N;
    signal(SIGCHLD, child_handler);
     for (i = 0; i < N; i++) {
          if ((pid[i] = fork()) == 0) {
             sleep(1);
             exit(0);  /* Child: Exit */
          }
      }
      while (ccount > 0);
}

分析:for循环中创建了五个子进程,子进程都休眠一秒后正常终止,一旦子进程终止,子进程就会发送信号给父进程,然后父进程接受信号转而执行child_handler函数.通过我的猜测,有五个子进程的终止,则有父进程会接收五个信号,从而调用child_hander函数五次.但实际情况为:
在这里插入图片描述
父进程只接收到一个信号!这使我感到迷惑,不过我初步猜测时因为信号发生过于频繁,后面的信号被忽略了,这只是个人观点,究竟时怎么样,我还没弄明白.

forks.c文件:
/*

  • forks.c - Examples of Unix process control
    */
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <signal.h>

/*

  • fork0 - The simplest fork example
  • Call once, return twice
  • Creates child that is identical to parent
  • Returns 0 to child process
  • Returns child PID to parent process
    */
    void fork0()
    {
    if (fork() == 0) {
    printf(“Hello from child\n”);
    }
    else {
    printf(“Hello from parent\n”);
    }
    }

/*

  • fork1 - Simple fork example

  • Parent and child both run same code

  • Child starts with identical private state
    */
    void fork1()
    {
    int x = 1;
    pid_t pid = fork();

    if (pid == 0) {
    printf(“Child has x = %d\n”, ++x);
    }
    else {
    printf(“Parent has x = %d\n”, --x);
    }
    printf(“Bye from process %d with x = %d\n”, getpid(), x);
    }

/*

  • fork2 - Two consecutive forks
  • Both parent and child can continue forking
  • Ordering undetermined
    */
    void fork2()
    {
    printf(“L0\n”);
    fork();
    printf(“L1\n”);
    fork();
    printf(“Bye\n”);
    }

/*

  • fork3 - Three consective forks
  • Parent and child can continue forking
    */
    void fork3()
    {
    printf(“L0\n”);
    fork();
    printf(“L1\n”);
    fork();
    printf(“L2\n”);
    fork();
    printf(“Bye\n”);
    }

/*

  • fork4 - Nested forks in parents
    */
    void fork4()
    {
    printf(“L0\n”);
    if (fork() != 0) {
    printf(“L1\n”);
    if (fork() != 0) {
    printf(“L2\n”);
    }
    }
    printf(“Bye\n”);
    }

/*

  • fork5 - Nested forks in children
    */
    void fork5()
    {
    printf(“L0\n”);
    if (fork() == 0) {
    printf(“L1\n”);
    if (fork() == 0) {
    printf(“L2\n”);
    }
    }
    printf(“Bye\n”);
    }

void cleanup(void) {
printf(“Cleaning up\n”);
}

/*

  • fork6 - Exit system call terminates process
  • call once, return never
    */
    void fork6()
    {
    atexit(cleanup);
    fork();
    exit(0);
    }

/*

  • fork7 - Demonstration of zombies.
  • Run in background and then perform ps
    /
    void fork7()
    {
    if (fork() == 0) {
    /
    Child /
    printf(“Terminating Child, PID = %d\n”, getpid());
    exit(0);
    } else {
    printf(“Running Parent, PID = %d\n”, getpid());
    while (1)
    ; /
    Infinite loop */
    }
    }

/*

  • fork8 - Demonstration of nonterminating child.
  • Child still running even though parent terminated
  • Must kill explicitly
    /
    void fork8()
    {
    if (fork() == 0) {
    /
    Child /
    printf(“Running Child, PID = %d\n”,
    getpid());
    while (1)
    ; /
    Infinite loop */
    } else {
    printf(“Terminating Parent, PID = %d\n”,
    getpid());
    exit(0);
    }
    }

/*

  • fork9 - synchronizing with and reaping children (wait)
    */
    void fork9()
    {
    int child_status;

    if (fork() == 0) {
    printf(“HC: hello from child\n”);
    exit(0);
    } else {
    printf(“HP: hello from parent\n”);
    wait(&child_status);
    printf(“CT: child has terminated\n”);
    }
    printf(“Bye\n”);
    }

#define N 5
/*

  • fork10 - Synchronizing with multiple children (wait)

  • Reaps children in arbitrary order

  • WIFEXITED and WEXITSTATUS to get info about terminated children
    */
    void fork10()
    {
    pid_t pid[N];
    int i, child_status;

    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0) {
    exit(100+i); /* Child /
    }
    for (i = 0; i < N; i++) { /
    Parent */
    pid_t wpid = wait(&child_status);
    if (WIFEXITED(child_status))
    printf(“Child %d terminated with exit status %d\n”,
    wpid, WEXITSTATUS(child_status));
    else
    printf(“Child %d terminate abnormally\n”, wpid);
    }
    }

/*

  • fork11 - Using waitpid to reap specific children

  • Reaps children in reverse order
    */
    void fork11()
    {
    pid_t pid[N];
    int i;
    int child_status;

    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0)
    exit(100+i); /* Child */
    for (i = N-1; i >= 0; i–) {
    pid_t wpid = waitpid(pid[i], &child_status, 0);
    if (WIFEXITED(child_status))
    printf(“Child %d terminated with exit status %d\n”,
    wpid, WEXITSTATUS(child_status));
    else
    printf(“Child %d terminate abnormally\n”, wpid);
    }
    }

/*********

  • Signals
    *********/

/*

  • fork12 - Sending signals with the kill() function
    */
    void fork12()
    {
    pid_t pid[N];
    int i;
    int child_status;

    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0) {
    /* Child: Infinite Loop */
    while(1)
    ;
    }
    for (i = 0; i < N; i++) {
    printf(“Killing process %d\n”, pid[i]);
    kill(pid[i], SIGINT);
    }

    for (i = 0; i < N; i++) {
    pid_t wpid = wait(&child_status);
    if (WIFEXITED(child_status))
    printf(“Child %d terminated with exit status %d\n”,
    wpid, WEXITSTATUS(child_status));
    else
    printf(“Child %d terminated abnormally\n”, wpid);
    }
    }

/*

  • int_handler - SIGINT handler
    /
    void int_handler(int sig)
    {
    printf(“Process %d received signal %d\n”, getpid(), sig); /
    Unsafe */
    exit(0);
    }

/*

  • fork13 - Simple signal handler example
    */
    void fork13()
    {
    pid_t pid[N];
    int i;
    int child_status;

    signal(SIGINT, int_handler);
    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0) {
    /* Child: Infinite Loop */
    while(1)
    ;
    }

    for (i = 0; i < N; i++) {
    printf(“Killing process %d\n”, pid[i]);
    kill(pid[i], SIGINT);
    }

    for (i = 0; i < N; i++) {
    pid_t wpid = wait(&child_status);
    if (WIFEXITED(child_status))
    printf(“Child %d terminated with exit status %d\n”,
    wpid, WEXITSTATUS(child_status));
    else
    printf(“Child %d terminated abnormally\n”, wpid);
    }
    }

/*

  • child_handler - SIGCHLD handler that reaps one terminated child
    /
    int ccount = 0;
    void child_handler(int sig)
    {
    int child_status;
    pid_t pid = wait(&child_status);
    ccount–;
    printf(“Received SIGCHLD signal %d for process %d\n”, sig, pid); /
    Unsafe /
    fflush(stdout); /
    Unsafe */
    }

/*

  • fork14 - Signal funkiness: Pending signals are not queued
    */
    void fork14()
    {
    pid_t pid[N];
    int i;
    ccount = N;
    signal(SIGCHLD, child_handler);

    for (i = 0; i < N; i++) {
    if ((pid[i] = fork()) == 0) {
    sleep(1);
    exit(0); /* Child: Exit */
    }
    }
    while (ccount > 0)
    ;
    }

/*

  • child_handler2 - SIGCHLD handler that reaps all terminated children
    /
    void child_handler2(int sig)
    {
    int child_status;
    pid_t pid;
    while ((pid = wait(&child_status)) > 0) {
    ccount–;
    printf(“Received signal %d from process %d\n”, sig, pid); /
    Unsafe /
    fflush(stdout); /
    Unsafe */
    }
    }

/*

  • fork15 - Using a handler that reaps multiple children
    */
    void fork15()
    {
    pid_t pid[N];
    int i;
    ccount = N;

    signal(SIGCHLD, child_handler2);

    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0) {
    sleep(1);
    exit(0); /* Child: Exit */

    }
    while (ccount > 0) {
    pause();
    }
    }

/*

  • fork16 - Demonstration of using /bin/kill program
    */
    void fork16()
    {
    if (fork() == 0) {
    printf(“Child1: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    if (fork() == 0)
    printf(“Child2: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    while(1);
    }
    }

/*

  • Demonstration of using ctrl-c and ctrl-z
    */
    void fork17()
    {
    if (fork() == 0) {
    printf(“Child: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    }
    else {
    printf(“Parent: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    }
    while(1);
    }

int main(int argc, char *argv[])
{
int option = 0;
if (argc > 1)
option = atoi(argv[1]);
switch(option) {
case 0: fork0();
break;
case 1: fork1();
break;
case 2: fork2();
break;
case 3: fork3();
break;
case 4: fork4();
break;
case 5: fork5();
break;
case 6: fork6();
break;
case 7: fork7();
break;
case 8: fork8();
break;
case 9: fork9();
break;
case 10: fork10();
break;
case 11: fork11();
break;
case 12: fork12();
break;
case 13: fork13();
break;
case 14: fork14();
break;
case 15: fork15();
break;
case 16: fork16();
break;
case 17: fork17();
break;
default:
printf(“Unknown option %d\n”, option);
break;
}
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,您说得对。下面是将会死锁的哲学家用餐问题算法改进为“无法获得任何一根筷子的锁就立即释放已有的锁并从头开始尝试获取,直到尝试成功”的伪代码: ``` #define N 5 // 哲学家数目 #define LEFT (i + N - 1) % N // 左邻居编号 #define RIGHT (i + 1) % N // 右邻居编号 #define THINKING 0 // 哲学家思考状态 #define HUNGRY 1 // 哲学家饥饿状态 #define EATING 2 // 哲学家就餐状态 int state[N]; // 哲学家状态数组 pthread_mutex_t mutex; // 全局互斥锁 pthread_mutex_t s[N]; // 筷子锁数组 // 哲学家尝试获取筷子 void take_forks(int i) { pthread_mutex_lock(&mutex); state[i] = HUNGRY; test(i); pthread_mutex_unlock(&mutex); pthread_mutex_lock(&s[i]); } // 哲学家释放筷子 void put_forks(int i) { pthread_mutex_lock(&mutex); state[i] = THINKING; test(LEFT); test(RIGHT); pthread_mutex_unlock(&mutex); } // 测试哲学家是否可以就餐 void test(int i) { if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) { state[i] = EATING; pthread_mutex_unlock(&s[i]); } else { pthread_mutex_unlock(&s[i]); // 无法获得任何一根筷子的锁就立即释放已有的锁并从头开始尝试获取,直到尝试成功 usleep(100); take_forks(i); } } // 哲学家线程 void *philosopher(void *arg) { int i = *(int *)arg; while (1) { think(); // 哲学家思考 take_forks(i); // 哲学家尝试获取筷子 eat(); // 哲学家就餐 put_forks(i); // 哲学家释放筷子 } } int main() { pthread_t tid[N]; int i, id[N]; pthread_mutex_init(&mutex, NULL); for (i = 0; i < N; i++) { pthread_mutex_init(&s[i], NULL); state[i] = THINKING; id[i] = i; pthread_create(&tid[i], NULL, philosopher, &id[i]); } for (i = 0; i < N; i++) { pthread_join(tid[i], NULL); } return 0; } ``` 在测试哲学家是否可以就餐时,如果无法获得任何一根筷子的锁,就立即释放已有的锁并从头开始尝试获取,直到尝试成功。这样可以防止死锁的发生,但可能会降低程序的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值