[操作系统自由练习] 进程的创建, fork()函数


author: 泥烟


ps命令查看系统进程

ps 的参数很多, 在此仅列出几个常用的参数

  • -A 列出所有的进程

  • -w 显示加宽可以显示较多的资讯

  • -ef 显示所有命令,连带命令行

  • -au 显示较详细的资讯

  • -aux 显示所有包含其他使用者的行程

  • au(x) 输出格式 :USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

    • USER: 行程拥有者

    • %CPU: 占用的 CPU 使用率

    • %MEM: 占用的记忆体使用率

    • VSZ: 占用的虚拟记忆体大小

    • RSS: 占用的记忆体大小

    • TTY: 终端的次要装置号码 (minor device number of tty)

    • STAT: 该行程的状态

      1. D: 无法中断的休眠状态 (通常 IO 的进程)
      2. R: 正在执行中
      3. S: 静止状态
      4. T: 暂停执行
      5. Z: 不存在但暂时无法消除
      6. W: 没有足够的记忆体分页可分配
      7. <: 高优先序的行程
      8. N: 低优先序的行程
      9. L: 有记忆体分页分配并锁在记忆体内 (实时系统或捱A I/O)
  • kill -9 pid 杀死pid号的进程

    kill 发送一个特定的信号 (signal) 给 pid 的进程根据该信号而做特定的动作,若没有指定,预设是送出终止 (TERM) 的信号

  • env 查看环境变量

在这里插入图片描述

可以发现,

  1. 所谓的父进程(37002)是由(36841, 即程序开始执行时所创建的进程)创建的

  2. 子进程(37003)占用的虚拟内存的大小是和父进程(37002)一样的

  3. 该程序中, 尽管父进程睡两秒,子进程睡一秒, 但运行时出现了连着输出了三次子进程的情况, 我觉得应该是异步性导致的

    在这里插入图片描述

fork1.c

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

main()
{
    int pid=fork();
    if(pid == -1){
        printf("fork failed.\n");
        return ;
    }
    else if(pid > 0) printf("%d %d\n", pid, getpid());
    else printf("%d %d\n", pid, getpid());
    printf("child\n");
    printf("father\n");
    return;
}

在这里插入图片描述

总结

成功创建子进程,

父进程(47428)中pid这个变量(注意,不是父进程本身的pid)即fork后返回的值, 等于子进程的pid

子进程(47429)中pid这个变量为0

父子进程均输出 child father

fork2.c

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

main(int argc, char **argv)
{
    int pid = fork();
    
    if(pid == 0){
        printf("Child\n");
        execl("/bin/ls", "ls", 0);
        perror("exce error!\n");
        printf("ccc");
    }
    else if(pid > 0) printf("Parent\n");
    else printf("FORK FAIL!\n");

    printf("%d END.\n", getpid());
}

在这里插入图片描述

总结

成功创建子进程后,

  • 子进程 👉 >>>“child” 👉 执行ls 👉 结束

  • 父进程(pid=47230) 👉 >>>“Parent” 👉 >>>“END” 👉 结束

fork2plus.c (可传参)

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

main(int argc, char **argv)
{
    int pid = fork();
    int rtn;
    if(pid == 0){
        printf("Child\n");
        printf("argc=%d\n", argc);
        for(int i = 1; i < argc; i ++){
            printf("执行%s...\n", argv[i]);
            system(argv[i]);
            perror(argv[i]);
        }
        exit(errno);
    }
    else if(pid > 0){
        wait(&rtn);
        printf("Chird is over, here is parent(%d)\n", getpid());
    }
    else printf("FORK FAIL!\n");

    printf("%d END.\n", getpid());
}

在这里插入图片描述

总结

1.分析

流程与上面几个例子差不多,

利用argc argv, 实现向程序内传参

值得一提的是, 我在这里加了个wait, 等待子进程的结束

2.argc argv

百度百科

argc,argv 用命令行编译程序时有用。

主函数main中变量(int argc,char *argv[ ])的含义

有些编译器允许将 main() 的返回类型声明为void,这已不再是合法的C++

main(int argc, char *argv[ ], char **env)才是UNIX和Linux中的标准写法。

argc: 整数,用来统计你运行程序时送给main函数的命令行参数的个数

* argv[ ]: 指针数组,用来存放指向你的字符串参数的指针,每一个元素指向一个参数

argv[0] 指向程序运行的全路径名

argv[1] 指向在DOS命令行中执行程序名后的第一个字符串

argv[2] 指向执行程序名后的第二个字符串

argv[argc]为NULL。

**env:字符串数组。env[ ]的每一个元素都包含ENVVAR=value形式的字符

串。其中ENVVAR为环境变量,value 为ENVVAR的对应值。

argc, argv,env是在main( )函数之前被赋值的,编译器生成的可执行文件,main( )不是真正的入口点,而是一个标准的函数,这个函数名与具体的操作系统有关。

proc1.c

//进程操作
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
int main(int argc,char **argv)
{
    pid_t pid,old_ppid,new_ppid;
    pid_t child,parent;
    parent = getpid();  //获得本进程PID
    if((child=fork()) < 0){
        fprintf(stderr, "%s:fork of child failed:%s\n", argv[0], strerror(errno));
        exit(1);
    }
    else if(child == 0){  //子进程被调度运行
        old_ppid = getppid();
        sleep(2);
        new_ppid = getppid();
    }
    else {
        sleep(2);
        printf("父亲(pid=%d)醒了并say good bye!\n", getpid());
        exit(0);          //父进程退出
    }
   //下面仅子进程运行
    printf("Original parent:%d\n", parent);
    printf("Child:%d\n", getpid());
    printf("Child's old ppid:%d\n", old_ppid);
    printf("Child's new ppid:%d\n", new_ppid);
    exit(0);
}

在这里插入图片描述

总结

这里old pid 和new pid都是指子进程47489,

父进程睡完就溜了, 不会执行下面的输出操作

posted @ 2022-05-10 21:07  泥烟  阅读(58)  评论(0编辑  收藏  举报