Linux 进程控制

相关概念

  • 程序: 编译好的二进制文件, 存放在磁盘上(占用的是物理内存空间), 不占用系统资源(CPU, 内存, 打开的文件, 设备, 锁...)
  • 进程: 抽象的概念, 与操作系统原理联系紧密, 是活跃的程序, 占用系统资源, 在内存中执行.
    由原来的单道程序设计转换成多道程序设计, 是CPU分成若干的时间碎片, 各个进程抢占资源去执行, 虽然程序有优先级高低, 但是只是抢占到的几率高; 即使是多核, 也是需要时才会开启其他核.
    处理速度:寄存器->cache->内存->硬盘->网络->存储介质
    其他概念: 串行和并发, CPU和MMU
  • 进程控制块 PCB 在/usr/src/linux-headers-3.15.0-30/include/linux/sched.h文件中可以查看struct task_struct 结构体定义, 包含部分定义: 进程id, 进程状态, 进程切换时需要保存和回复的CPU寄存器, 描述虚拟的地址空间信息, 当前工作目录, 文件描述符表, 用户id和组id, 会话和进程组, 资源上限(命令:ulimit -a 查看), umask掩码...
  • 进程状态: 初始态(创建时, 非常短暂), 就绪态(有执行资格, 但是没执行权限, 需要抢占资源), 运行态(正在执行), 挂起态(没有执行资格和执行权限), 终止态
    点我查看进程状态图

进程控制

程序默认的是单进程的; 下面介绍多进程的使用
创建进程函数: pid_t fork(void);(查看 Linux下使用命令:man 2 fork)
点击查看描述图
返回值: 失败返回-1, 父进程返回子进程ID, 子进程返回0
查看当前进程 ps aux | grep "要查找的程序"; ps ajx

父子进程.png

刚fork完成时(父子进程之间) 1. 0-3G地址空间相同 2. PCB相同, 但是pid除外 3.其他内容由mmu映射到物理内存上, 读时共享, 写时复制

获取pid 和 ppid 对应的函数是getpid(); getppid();

//wait 函数回收子进程
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
    pid_t pid;
    pid = fork();
    
    if(pid == -1) {
        perror("fork error");
        exit(-1);
    } else if(pid > 0) {
        printf("parent process\n");
    } else if(pid == 0) {
        printf("child process\n");
    }

    printf("mul process\n");
    return 0;
}

回收进程

进程有孤儿进程, 僵尸进程

  1. 孤儿进程 : 子进程活着, 父进程死了, linxu中的init进程会领养孤儿
  2. 僵尸进程 : 子进程死了, 父进程正在忙, 没有去回收子进程
  3. 进程回收 : 代码附在下边

wait - 阻塞函数
pid_t wait(int* status);
调用一次回收一个子进程资源
返回值:

0: 回收的子进程的pid;
-1: 没有子进程可以回收了;
status -- 传出参数
获取退出时候的返回值

  • 正常退出( return num; exit(num); exit(num);
    )
    WIFEXITED(status) > 0

获取返回值: WEXITSTATUS(status)

  • 被信号杀死
    WIFSIGNALED(status) > 0

获取杀死子进程的信号
WTERMSIG(status)

waitpid -- pid_t waitpid(pid_t pid, int *status, int options);
(可以设置非阻塞, 提高wait的效率)
pid: -1: 所有的子进程
0: 当前进程组的子进程

0(pid): 回收指定进程的pcb
-pid: 回收不在当前进程组的子进程

opttions: 阻塞: 0
非阻塞: WNOHANG 可以设置为非阻塞
可以有针对性的回收某一个子进程资源 ;需要注意的是即使是非阻塞, 也需要循环来回收子进程

//wait函数回收子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/wait.h>


int counter = 100;

int main(int argc, const char* argv[])
{
    int number = 5;

    int i;
    pid_t pid;

    for(i=0; i<number; ++i)
    {
        pid = fork();
        if(pid == 0)
            break;
    }

    if(i<number)
    {
        // 子进程
        counter += 100;
        printf("child pid = %d, ppid = %d\n", getpid(), getppid());
        printf("counter = %d\n\n", counter);
        //sleep(100);
        exit(9);
    }
    else if(i == number)
    {
        counter += 300;
        printf("parent pid = %d, ppid = %d\n", getpid(), getppid());
        printf("counter = %d\n\n", counter);
        sleep(1);

        // 回收子进程
        int status;
        pid_t wpid;
        while( (wpid=wait(&status)) != -1)
        {
            // 正常退出
            if(WIFEXITED(status))
            {
                printf("porcess exit by number: %d\n", WEXITSTATUS(status));
            }
            // 被信号杀死
            else if(WIFSIGNALED(status))
            {
                printf("process kill by signal: %d\n", WTERMSIG(status));
            }
            printf("child died pid = %d\n", wpid);
        }
    }
    

    return 10;
}

//验证父子进程是否文件共享
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, const char* argv[]) {
    int fd = open("temp", O_CREAT | O_RDWR, 0664);
    if(fd == -1) {
        perror("open error");
        exit(1);
    }

    pid_t pid = fork();
    if(pid == -1)  {
        perror("fork error");
        exit(1);
    }

    if(pid > 0) {
        char* p = "123123123";
        write(fd, p, strlen(p)+1);
    } else if(pid == 0) {
        // 睡1s保证父进程已经完成了文件的写操作
        sleep(1);
        char buf[1024];
        lseek(fd, 0, SEEK_SET);
        int len = read(fd, buf, sizeof(buf));
        printf("%s\n", buf);
    }
    close(fd);
    return 0;
}
//子进程执行不同任务, 父进程负责回收子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    int i = 0;
    int number = 3;
    pid_t pid;

    for(i = 0; i<number; ++i)
    {
        pid = fork();
        if(pid == 0)
        {
            break;
        }
    }

    // 父进程
    if(i == number)
    {
        sleep(2);
        // 回收子进程
        pid_t wpid;
        int status;
        while( (wpid = waitpid(0, &status, WNOHANG)) != -1 )
        {
            if(wpid == 0)
            {
                continue;
            }
            printf("child pid = %d\n", wpid);
            if(WIFEXITED(status))
            {
                printf("return number: %d\n", WEXITSTATUS(status));
            }
            else if(WIFSIGNALED(status))
            {
                printf("exit by signal: %d\n", WTERMSIG(status));
            }
        }
    }
    else if(i == 0)
    {
        execl("/home/kevin/test/app", "app", NULL);
    }
    else if(i == 1)
    {
        execl("./error", "error", NULL);
    }
    else if(i == 2)
    {
        execlp("ps", "ps", "aux", NULL);
    }

    printf("over......\n");
    return 0;
}

//waitpid 来回收子进程, 注意非阻塞时也需要循环去回收
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    int num = 3;
    int i = 0;
    pid_t pid;

    for(i=0; i<num; ++i)
    {
        pid = fork();
        if(pid == 0)
        {
            break;
        }
    }

    if(i == 0)
    {
        execlp("ps", "ps", "aux", NULL);
        perror("execlp ps");
        exit(1);
    }
    else if(i == 1)
    {
        execl("/home/kevin/test/app", "app", NULL);
        perror("execl app");
        exit(1);
    }
    else if(i == 2)
    {
        execl("./error", "error", NULL);
        perror("execl error");
        exit(1);
    }
    else if(i == num)
    {
        // 回收
        int status;
        pid_t wpid;
        while( (wpid = waitpid(-1, &status, WNOHANG)) != -1 )
        {
            if(wpid == 0)
                continue;
            printf(" ----- child died pid = %d\n", wpid);
            if(WIFEXITED(status))
            {
                printf("return value %d\n", WEXITSTATUS(status));
            }
            else if(WIFSIGNALED(status))
            {
                printf("died by signal: %d\n", WTERMSIG(status));
            }
        }
    }
    return 0;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,085评论 0 23
  • 进程创建 普通函数调用完成后,最多返回(return)一次,但fork/vfork会返回二次,一次返回给父进程,一...
    陈伟志阅读 585评论 0 3
  • fork 函数生成子进程 输出 创建成功。 父进程返回子进程的 pid=6437,子进程返回的pid=0。 父进程...
    谢小帅阅读 297评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 作用域 在JS中(非ES6),只有函数作用域,没有块作用域。例如,for循环,while等{}内部的变量其实是和外...
    DeeJay_Y阅读 297评论 0 0