APUE学习笔记-第八章进程控制(重点摘录&部分解析)

进程控制

函数fork
#include <unistd.h>
pid_t fork(void);
  • 创建的子进程是父进程的 副本,父子进程之间共享正文段。

  • fork之后往往跟着exec, 一般使用写时复制技术

  • fork完毕之后子进程对于声明的 全局变量、局部变量的改变都只是影响自己的,并不会影响父进程中的变量的值

文件共享

父进程和子进程每个相同的打开文件描述符 共享一个文件表项,并且共享同一个文件偏移量。那么如果没有 同步策略的话,对于父子写同一个文件描述符指向的文件输出就可能会混合

fork失败原因
  1. 系统中有了太多的进程
  2. 进程总数超出了系统的限制
函数vfork

vfork函数的调用序列&返回值同fork相同, 但是语意不同。

  • vfork的目的主要是执行一个新的程序,并不会进行将父进程的地空间的复制,会立即调用exec, exit
  • 保证子进程先执行,没有调用exec函数的时候在父进程空间执行,而不是写时复制(不复制比部分复制还是快的)
  • 如果没有调用exec而更改了父进程的变量,就会同步更改到父进程中,因为没有复制,运行在父进程的地址空间中
函数exit

终止的进程通过wait or waitpid函数获得终止状态

如果父进程退出了但是子进程还没有退出的话,子进程就被init进程(pid = 1)所收养

僵死进程

如果编写一个长期运行的程序,fork了很多的进程,除非 父进程等待取得子进程的终止状态,不然子进程终止之后就变成僵死进程

wait & waitpid

wait等待任意一个子进程退出,waitpid等待特定的子进程退出。两个函数可以限定一些参数获得子进程的退出的状态

避免僵死进程的例子

不等待子进程终止也不希望子进程处于僵死状态的秘诀就是:fork两次

#include "apue.h"
#include <sys/wait.h>

int 
main() 
{
    pid_t pid;
    int   status;

    if ((pid = fork()) < 0) 
        err_sys("fork error");
    else if (pid == 0) {            //first child 
        if ((pid = fork()) < 0) 
            err_sys("fork error\n");
        else if (pid  > 0) 
            exit(0);


        //now the parent becomes init 
        sleep(2);
        printf("Second child, parent pid = %ld\n", (long) getppid());
        exit(0);
    }
    if (waitpid(pid, NULL, 0) != pid)       //wait for first child  
        err_sys("waitpid error");
    exit(0);
        
    

}


运行结果

root@iZbp12r8eimkkdor4011j3Z:~/APUE# ./a.out 
root@iZbp12r8eimkkdor4011j3Z:~/APUE# Second child, parent pid = 1
竞争条件

如果多个进程都试图对于共享的数据进行处理,但是最终的结果 取决于进程的运行顺序,那么就称之为出现了 竞争条件

🌰:进程希望等待父进程终止

while(getppid() != 1) {sleep(1);} 轮询等待,如果父进程退出了但是子进程没有退出,那么子进程的父进程就会变成init(pid = 1),实现了等待父进程的退出,但是这种方式很消耗cpu,后面章节介绍其他方法。

exec

exec用磁盘上的一个新程序替换了当前程序的 正文段、数据段、堆段、栈段

exec函数有7中,其中的传递参数跟名称中的字符相匹配

  • l:参数表 list
  • v: 传递的是一个vector:argv[]
  • p: 传递的是filename
  • e: envp[]
#include "apue.h"
#include <sys/wait.h>

char*  env_init[] = { "USER=unknown", "PATH=/tmp", NULL };

int main()
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {      //指定pathname & environment
        if (execle("/home/bing/bin/echo", "echo", "myarg1", (char*) 0, env_init) < 0) {
            err_sys("execle error");
        }
    }
    if (waitpid(pid, NULL, 0) < 0) {
        err_sys("wait error");
    }
    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        if (execlp("echo", "echo", "only 1 arg", (char*)0) < 0) {
            err_sys("execlp error");
        }
    }

    exit(0);
}
函数system

syatem进行了各种所需的各种出错的处理,进行了fork & exec 的封装

进程调度

nice(int incr) :函数增加进程的 友好度, 进程的友好度越高,进程的优先级就越低

🌰:通过增加进程的友好度看一下进程的调度,通过模拟10S内父子进程的count数目来展示进程的调度

#include "apue.h"
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
 
#if defined(MACOS)
#include <sys/syslimits.h>
#elif defined(SOLARIS)
#include<limits.h>
#elif defined(BSD)
#include<sys/param.h>
#endif
 
unsigned long long count;
struct timeval end;
 
int system(const char *cmdstring){
    pid_t   pid;
    int     status;
 
    if(cmdstring == NULL){
        return(1);
    }
 
    if((pid = fork()) < 0){
        status = -1;
    } else if(pid == 0){
        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);
    } else {
        while(waitpid(pid, &status, 0) < 0){
            if(errno != EINTR) {
                status = -1;
                break;
            }
        }
    }
 
    return(status);
}
 
void
pr_exit(int status)
{
    if(WIFEXITED(status))
        printf("normal termination, exit status = %d\n",
                WEXITSTATUS(status));
    else if(WIFSIGNALED(status))
        printf("abnormal termination, signal number = %d%s\n",
                WTERMSIG(status),
        #ifdef WCOREDUMP
                WCOREDUMP(status) ? " (core file generated)" : "");
        #else
                "");
        #endif
    else if(WIFSTOPPED(status))
        printf("child stopped, signal number = %d\n",
                WSTOPSIG(status));
}
 
 
void checktime(char *str){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    if(tv.tv_sec >= end.tv_sec && tv.tv_usec >= end.tv_usec){
        printf("%s count = %lld\n", str, count);
        exit(0);
    }
}
 
 
int main(int argc, char *argv[]){
 
    pid_t       pid;
    char        *s;
    int         nzero, ret;
    int         adj = 0;
 
    setbuf(stdout, NULL);
 
    #if defined(NZERO)
        nzero = NZERO;
    #elif defined(_SC_NZERO)
        nzero = _SC_NZERO;
    #else
    #error NZERO undefined
    #endif
 
    printf("NZERO = %d\n",nzero);
    if(argc == 2)
        adj = strtol(argv[1], NULL, 10);
    gettimeofday(&end, NULL);
    end.tv_sec += 10;
 
    if((pid = fork()) < 0){
        err_sys("fork failed;");
    } else if(pid == 0){
        s = "child";
        printf("current nice value in child is %d, adjusting by %d\n", nice(0)+nzero, adj);
        errno = 0;
        if((ret = nice(adj)) == -1 && errno != 0){
            err_sys("child set scheduling priority");
        }
        printf("now child nice value is %d\n", ret+nzero);
    } else {
        s = "parent";
        printf("current nice value in parent is %d\n", nice(0)+nzero);
    }
 
    for(;;){
        if(++count == 0)
            err_quit("%s counter warp", s);
        checktime(s);
    }
}

运行结果(一次不增加友好值,一次增加友好值)

root@iZbp12r8eimkkdor4011j3Z:~/APUE# ./a.out 
NZERO = 109
current nice value in parent is 109
current nice value in child is 109, adjusting by 0
now child nice value is 109
child count = 148075333
parent count = 148087469
root@iZbp12r8eimkkdor4011j3Z:~/APUE# ./a.out 20
NZERO = 109
current nice value in parent is 109
current nice value in child is 109, adjusting by 20
now child nice value is 128
parent count = 286392260
root@iZbp12r8eimkkdor4011j3Z:~/APUE# child count = 4176383
^C
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值