《操作系统导论》chap 5 进程API

fork() 系统调用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {
        // parent goes down this path (original process)
        printf("hello, I am parent of %d (pid:%d)\n",
           rc, (int) getpid());
    }
    return 0;
}
➜  c5 gcc -o p1 p1.c -Wall
➜  c5 ./p1
hello world (pid:29943)
hello, I am parent of 29944 (pid:29943)
hello, I am child (pid:29944)

上面这个例子发生的事情:

  1. 主进程pid为29943,一开始输出了一个hello world
  2. 主进程调用fork()系统调用。新创建的进程和调用进程基本上完全一样。对OS来说,此时有几乎两个完全一样的p1进程在执行,且都从fork()函数返回。新进程为child,原进程为parent。child不会从main而是从fork()系统调用返回。
  3. parent从fork()返回的是child的pid,而child从fork()返回的是0.

wait()系统调用

有时候父进程需要等子进程执行完毕。可以调用wait()

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    sleep(1);
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
           rc, wc, (int) getpid());
    }
    return 0;
}
➜  c5 ./p2
hello world (pid:2070)
hello, I am child (pid:2071)
hello, I am parent of 2071 (wc:2071) (pid:2070)

父进程调用wait(),延迟自己执行直至子进程执行完毕。

wait (NULL)作用?

https://stackoverflow.com/questions/42426816/how-does-waitnull-exactly-work
wait(NULL) will block parent process until any of its children has finished. If child terminates before parent process reaches wait(NULL) then the child process turns to a zombie process until its parent waits on it and its released from memory.
If parent process doesn't wait for its child, and parent finishes first, then the child process becomes orphan and is assigned to init as its child. And init will wait and release the process entry in the process table.
In other words: parent process will be blocked until child process returns an exit status to the operating system which is then returned to parent process. If child finishes before parent reaches wait(NULL) then it will read the exit status, release the process entry in the process table and continue execution until it finishes as well.

exec()系统调用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p3.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
        printf("this shouldn't print out");
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
           rc, wc, (int) getpid());
    }
    return 0;
}
➜  c5 ./p3
hello world (pid:7124)
hello, I am child (pid:7125)
 29 123 965 p3.c
hello, I am parent of 7125 (wc:7125) (pid:7124)

可以让子进程和父进程执行不同的程序。

What it does: given the name of an executable (e.g., wc),
and some arguments (e.g., p3.c), it loads code (and static data) from that
executable and overwrites its current code segment (and current static
data) with it; the heap and stack and other parts of the memory space of
the program are re-initialized. Then the OS simply runs that program,
passing in any arguments as the argv of that process. Thus, it does not
create a new process; rather, it transforms the currently running program
(formerly p3) into a different running program (wc). After the exec()
in the child, it is almost as if p3.c never ran; a successful call to exec()
never returns.

如上设计API的目的

分离fork()和exec()在构建UNIX shell的时候很有用。可以给shell在fork之后exec之前运行代码的机会。

➜  c5 wc p3.c > newfile.txt

shell在获取了用户输入后,从文件系统找到这个可执行程序,调用fork()创建新进程,并调用exec()的某个变体来执行可执行程序,调用wait()来等待该命令的完成。子进程结束后,shell从wait()返回并再次输出一个字符串,等待用户输入下一条命令。
上面的语句,当完成wc子进程的创建,shell在调用exec()之前先关闭了标准输出,打开了文件newfile.txt。
可参考下面的代码。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
      // child: redirect standard output to a file
      close(STDOUT_FILENO); 
      open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
      // now exec "wc"...
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p4.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
    assert(wc >= 0);
    }
    return 0;
}

homework

1、Write a program that calls fork(). Before calling fork(), have the main process access a variable (e.g., x) and set its value to something (e.g., 100). What value is the variable in the child process? What happens to the variable when both the child and parent change the value of x?

父子进程访问一个变量

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void){
    int x = 100;
    int rc = fork();
    if(rc < 0) {
        printf("failed to fork\n");
    } else if(rc == 0){
        printf("child process get x: %d\n", x);
        exit(0);
    } else {
        printf("parent process set x to: %d\n", x);
    }
    return 0;
}
parent process set x to: 100
child process get x: 100

父子进程访问一个变量并分别改变它

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void){
    int x = 100;
    int rc = fork();
    if(rc < 0) {
        printf("failed to fork\n");
    } else if(rc == 0){
        x = 99;
        printf("child process get x: %d\n", x);
        exit(0);
    } else {
        x = 999;
        printf("parent process set x to: %d\n", x);
    }
    return 0;
}
parent process set x to: 999
child process get x: 99
2、 Write a program that opens a file (with the open() system call) and then calls fork() to create a new process. Can both the child and parent access the file descriptor returned by open()? What happens when they are writing to the file concurrently, i.e., at the same time?
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main(void){
    int fd, rc;
    fd = open("output.txt", O_CREAT|O_WRONLY|O_APPEND);
    char buf1[] = "content1\n";
    char buf2[] = "content2\n";
    rc = fork();
    if(rc < 0) {
        printf("failed to fork\n");
    } else if(rc == 0){
        write(fd, buf1, sizeof(buf1) - 1); //remove '\0'
        exit(0);
    } else {
        write(fd, buf2, sizeof(buf2) - 1); //remove '\0'
    }
    return 0;
}

output.txt中的结果

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

推荐阅读更多精彩内容