C++笔记--Linux编程(10)-进程控制 fork系统调用

Posted xiangjai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++笔记--Linux编程(10)-进程控制 fork系统调用相关的知识,希望对你有一定的参考价值。

Linux进程模型

传统的Linux模型里有三种创建或者修改进程的操作

        1. system用于调用shell,执行一个指定的命令;

        2. fork用于创建一个新的进程,该进程几乎是当前进程的一个完全拷贝;

        3. exec可以在进程中用另外的程序来替换当前运行的进程

什么是进程

        进程是一个正在执行的程序实例,他也是Linux基本的调度单位,一个进程由如下元素组成

        进程的当前上下文(context),它是进程当前执行状态

        进程的当前执行目录

        进程访问的文件和目录

        程序的访问权限

        内存和其他分配给进程的系统资源

进程标识号

        进程最知名的属性就是进程号(processID,PID)和它父进程号(parent processID,PPID)

        PID和PPID都是非零的整数。 一个PID唯一标识一个进程。 一个进程创建的另一个新进程称为子进程。相反地,创建子进程的进程称为父进程。

        所有进程追溯其祖先最终都会落到进程号为1的进程身上,这个进程叫init进程 Init进程是linux内核启动后第一个执行的进程。 Init引导系统,启动守护进程并且运行必要的程序。

进程PID和父进程PPID示例

创建多进程示例

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"

int main () {

    pid_t pid;
    printf("start, pid=%d, ppid=%d\\n", getpid(), getppid());

    int len = 4;

    for(int i=0; i<len; i++) {
        pid = fork();
        if(pid == 0) {
            printf("I am is child pid, pid=%d, ppid=%d\\n", getpid(), getppid());
            break;
        } else if(pid > 0) {
            printf("I am is parent pid, pid=%d, ppid=%d\\n", getpid(), getppid());
        }
    }

    sleep(4);
    printf("exit, pid=%d, ppid=%d\\n", getpid(), getppid());
    while (1) {
        sleep(4);
    }

    return 0;
}

创建进程

system系统调用

#include <stdlib.h>
int system(const char *string);

system函数传递给/bin/sh –c 来执行string所指定的命令

string中可以包含选项和参数。

如果没有找到/bin/sh,函数返回127,如果出现其他错误返回-1,成功返回0,但如果string为NULL,返回一个非0值

示例

int main(void)
{
	printf(“%d\\n”, system(“ls –l”));
	return EXIT_SUCCESS;
}

fork系统调用

#include <unistd.h>
pid_t fork(void);

pid_t fork(void); fork执行成功,向父进程返回子进程的PID,并向子进程返回0,这意味着fork即使只调用一次,也会返回两次

fork创建的新进程是和父进程(除了PID和PPID)一样的副本

父进程和子进程之间有点区别,子进程没有继承父进程的超时设置(使用alarm调用)、父进程创建的文件锁,或者未决信号

int main(void)
{
	pid_t child = fork();
	if(child == -1)
	{
		printf("call fork error\\n");
		return -1;
	}
	if(child == 0)
	{
		printf("is child\\n");
	}
	else
	{
		printf("child pid is %d\\n",  child);
	}
	return 0;
}

你不能预计父进程是在它的子进程之前还是之后运行,它的执行是无序的,是异步的

fork的异步行为意味着你不应该在子进程中执行依赖与父进程的代码,反之亦然。

fork调用可能失败,原因是系统上已经运行了太多进程,已经超过了允许它执行的最大进程数。 fork执行失败,会向父进程返回-1,而且不创建子进程。

fork过程包括把父进程全部内存映像复制给子进程,这个过程很缓慢

UNIX的设计者创建了vfork调用,vfork调用也创建新进程,但它不产生父进程的副本。

Linux已经使用了写时复制技术,因此Linux的fork和UNIX的vfork一样快。

execve函数

int execve(const char *path, const char *arg, char * const envp[]);

fork创建了一个新的进程,产生一个新的PID。

execve用被执行的程序完全替换了调用进程的映像。

execve启动一个新程序,替换原有进程,所以被执行进程的PID不会改变。

execve函数接受三个参数。

        path-要执行的文件完整路径;

        argv-传递给程序完整参数列表,包括argv[0],它一般是执行程序的名字;

        envp-是指向执行execed程序的环境指针,可以设为NULL。

示例

int main(void)
{
	char *args[] = { "/bin/ls", "-l", NULL };
	execve("/bin/ls", args, NULL);
	return 0;
}

wait和waitpid函数

wait和waitpid函数可以收集子进程的退出状态

#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

stauts保存子进程的退出状态

pid为等待进程的PID,它能接受下表列出的4种值中的一个

示例

wait函数示例

int main () {

    pid_t pid = fork();
    if(pid == 0) {
        printf("I am is child pid, pid=%d, ppid=%d\\n", getpid(), getppid());
        sleep(2);
        return 0;
    } else if(pid > 0) {
        printf("I am is parent pid, pid=%d, ppid=%d\\n", getpid(), getppid());
        int status;
        pid_t wpid = wait(&status);
        printf("wait ok wpid=%d, pid=%d\\n", wpid, pid);
        if(WIFEXITED(status)) {
            printf("child exit with %d\\n", WEXITSTATUS(status));
        } else if(WIFSIGNALED(status)) {
            printf("child kill with %d\\n", WTERMSIG(status));
        }

    }

    return 0;
}

以上是关于C++笔记--Linux编程(10)-进程控制 fork系统调用的主要内容,如果未能解决你的问题,请参考以下文章

C++笔记--Linux编程(14)-线程同步

C++笔记--Linux编程(11)-进程通信

Android C++系列:Linux守护进程

原创 C++笔记--Linux网络编程(15)-socket

《C++多线程编程》学习笔记

C++笔记--Linux编程-gcc gbb make