父子进程的使用

一个父进程fork()创建了一个子进程,子进程共用父进程所以的代码,对于父进程的数据域,则实行写时拷贝,即父子进程进行修改时拷贝数据给子进程,不修改时共享数据。

父进程执行过的代码子进程不再执行,子进程下一步执行的命令将会和父进程一样。

僵尸进程:如果子进程先于父进程退出, 同时父进程又没有调用wait/waitpid来收集exit的状态,则该子进程将成为僵尸进程。当子进程结束后,要返回一个反馈给父进程。让父进程知道这个子进程结束任务了(正常退出)
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

如果父进程已经退出(没有wait),则该子进程将会成为孤儿进程过继给init进程
如果其父进程还没有退出,也没有wait(),那么该进程将向父进程发送SIGCHILD信号,进入僵尸状态等待父进程为其收尸。如果父进程一直没有执行wait(),那么该子进程将会持续处于僵尸状态,如果父进程到死也没有执行wait则在死后会将进程过继给init进程,由init进程来处理zombie进程,最终还是会死掉。
僵尸进程将会导致资源浪费,而孤儿则不会。

父子进程均具有:

1、得到进程的PID:pid_t getpid(void); //进程ID

2、得到进程的PPID:pid_t getppid(void); //父进程ID

如何防止僵尸进程
首先明白如何产生僵尸进程:
1、子进程结束后向父进程发出SIGCHLD信号,父进程默认忽略了它
2、父进程没有调用wait()或waitpid()函数来等待子进程的结束
第一种方法:  捕捉SIGCHLD信号,并在信号处理函数里面调用wait函数
转贴Richard Steven的Unix Network Programming代码

int
main(int argc, char **argv)
{
                ...
        Signal(SIGCHLD, sig_chld); //接收来自子进程的SIGCHLD信号
                for(;
                }
                ...
}

void
sig_chld(int signo)
{
        pid_t        pid;
        int        stat;

        while ( (pid = waitpid(-1, &stat, WNOHANG)) >; 0)
                printf("child %d terminated/n", pid);
        return;
}

第二种方法:两次fork():转载
在《Unix 环境高级编程》里关于这个在8.6节有非常清楚的说明。

实例
回忆一下8 . 5节中有关僵死进程的讨论。如果一个进程要f o r k一个子进程,但不要求它等待
子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍是调用f o r k
两次。程序8 - 5实现了这一点。
在第二个子进程中调用s l e e p以保证在打印父进程I D时第一个子进程已终止。在f o r k之后,
父、子进程都可继续执行——我们无法预知哪一个会先执行。如果不使第二个子进程睡眠,则
在f o r k之后,它可能比其父进程先执行,于是它打印的父进程I D将是创建它的父进程,而不是
i n i t进程(进程ID 1)。

#include        
#include        
#include        "ourhdr.h"

int main(void)
{
        pid_t        pid;

        if ( (pid = fork()) < 0)
                err_sys("fork error");
        else if (pid == 0) {                /* first child */
                if ( (pid = fork()) < 0)
                        err_sys("fork error");
                else if (pid > 0)
                        exit(0);        /* parent from second fork == first child */

                /* We're the second child; our parent becomes init as soon
                   as our real parent calls exit() in the statement above.
                   Here's where we'd continue executing, knowing that when
                   we're done, init will reap our status. */

                sleep(2);
                printf("second child, parent pid = %d/n", getppid());
                exit(0);
        }

        if (waitpid(pid, NULL, 0) != pid)        /* wait for first child */
                err_sys("waitpid error");

        /* We're the parent (the original process); we continue executing,
           knowing that we're not the parent of the second child. */

        exit(0);
}

区分父子进程的方法:

创建子进程,通过父进程和子进程返回值的不同,使得父进程和子进程执行不同的代码分支

int main(){

  //创建子进程

  pid_t val=fork();

  //判断父子进程

  if(val<0){    

   //返回值小于0,说明子进程创建失败

    printf("create error\n");

  }else if(val==0){          

   //返回值等于0,说明进入这里的是子进程

    printf("this is son\n");

  }else{                    

   //返回值大于0,说明进入这里的是父进程

    printf("this is father\n");

  }                            

}  

————————————————

  1. 查找进程占用
    nvidia-smi
    2. 根据子进程PID查找父进程PPID
    cat /proc/PID/status
    3. 杀死父进程
    kill -9 PPID

在调用函数中分叉的子进程,假如没有做特殊处理, 子进程会一直存在, 即使函数被调用完成,  子进程会和主程序一样,返回调用函数的上一级函数继续执行, 直到整个程序退出.

exec函数会取代执行它的进程,  也就是说, 一旦exec函数执行成功, 它就不会返回了, 进程结束.   但是如果exec函数执行失败, 它会返回失败的信息,  而且进程继续执行后面的代码!通常exec会放在fork() 函数的子进程部分, 来替代子进程执行啦, 执行成功后子程序就会消失,  但是执行失败的话, 必须用exit()函数来让子进程退出!

exec函数只是系统调用, 它是不支持管线处理的而system()函数是支持的.   他的内部会自动fork() 1个子进程,但是效率没有fork() 和 exec配合使用好.但是exec 支持执行脚本.  所以不需要管线处理的命令或者脚本可以利用fork() 和 exec函数来执行.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
共享内存和互斥量都是进程间通信的常用方式。下面是一个简单的示例程序,展示了两个非父子进程之间如何使用共享内存和互斥量进行通信。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/sem.h> #define SHM_SIZE 1024 int main() { int shmid, semid, pid; char *shmaddr; struct sembuf sem_b; // 创建共享内存 shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT|0666); if(shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); } // 创建互斥量 semid = semget(IPC_PRIVATE, 1, IPC_CREAT|0666); if(semid == -1) { perror("semget"); exit(EXIT_FAILURE); } // 初始化互斥量 semctl(semid, 0, SETVAL, 1); // 创建子进程 pid = fork(); if(pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if(pid == 0) // 子进程 { // 连接共享内存 shmaddr = (char *)shmat(shmid, NULL, 0); if(shmaddr == (void *)-1) { perror("shmat"); exit(EXIT_FAILURE); } // 等待互斥量 sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; semop(semid, &sem_b, 1); // 向共享内存写入数据 sprintf(shmaddr, "Hello, world!"); // 释放互斥量 sem_b.sem_op = 1; semop(semid, &sem_b, 1); // 断开共享内存连接 shmdt(shmaddr); exit(EXIT_SUCCESS); } else // 父进程 { // 连接共享内存 shmaddr = (char *)shmat(shmid, NULL, 0); if(shmaddr == (void *)-1) { perror("shmat"); exit(EXIT_FAILURE); } // 等待互斥量 sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; semop(semid, &sem_b, 1); // 从共享内存读取数据 printf("Received message: %s\n", shmaddr); // 释放互斥量 sem_b.sem_op = 1; semop(semid, &sem_b, 1); // 断开共享内存连接 shmdt(shmaddr); // 删除共享内存和互斥量 shmctl(shmid, IPC_RMID, NULL); semctl(semid, 0, IPC_RMID); exit(EXIT_SUCCESS); } return 0; } ``` 在这个示例程序中,父进程和子进程都连接到了同一个共享内存区域,使用互斥量来实现对共享内存的互斥访问。首先创建共享内存和互斥量,然后创建子进程。子进程先连接共享内存,然后等待互斥量,之后向共享内存写入数据,最后释放互斥量。父进程先连接共享内存,然后等待互斥量,之后从共享内存读取数据,最后释放互斥量。父进程和子进程断开共享内存连接后,再删除共享内存和互斥量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值