全栈开发知识库

系统函数调用

文件描述符

系统调用I/O函数。 文件描述符是非负整数,是文件的标识。
打开现存文件或新建文件时,系统(内核)会返回一个文件描述符。文件描述符用来指定已打开的文件。

在系统调用(文件IO)中,文件描述符对文件起到标识作用,如果要操作文件,就是对文件描述符的操作。

当一个程序运行或者一个进程开启时,系统会自动创建三个文件描述符: - 0 : 标准输入的文件描述符 - 1 : 标准输出的文件描述符 - 2 : 标准错误的文件描述符

利用open打开一个文件时,内核会返回一个文件描述符。

每个进程都有一张文件描述符的表,进程刚被创建时,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符0、1、2记录在表中。

在进程中打开其他文件是,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。

Linux中一个进程最多只能打开NR_OPEN_DEFAULT(即1024)个文件,故当文件不再使用时应及时调用close()函数关闭文件。

文件描述符的复制

dup和dup2是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也表示旧的文件。

fork

#include <unistd.h>

pid_t fork(void);

成功: 子进程中返回0,父进程中返回子进程id。 失败: 返回-1。

子进程会复制父进程fork()之前的所有内容。 但是fork()之后,父子进程完全独立,所以不管双方怎么改变(堆区、栈区、数据区等),都不会受对方的影响。

但是子进程会继承父进程的一些公有的区域,比如磁盘空间、内核空间。 文件描述符的偏移量就保存在内核空间中,所以父进程改变偏移量,则子进程获取的偏移量是改变之后的。

注意:只要执行一次fork,就会在原有的进程基础上创建一个新的子进程。而且如果fork之后不区分父子进程的代码区,则后面的代码都会执行。

#include <iostream>
#include <unistd.h>

using namespace std;

int main() {
    cout << getpid() << endl;
    pid_t pid = fork();
    cout << "hell " << endl;
    cout << pid << endl;
    while(1) {}
}

执行结果:

61268
hell 
61270
hell 
0
#include <iostream>
#include <unistd.h>

using namespace std;

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork fail");
    } else if (pid > 0) {
        // 父进程代码区
        while(1) {
            cout << "parent" << endl;
        }
    } else {
        // 子进程代码区
        while(1) {
            cout << "child" << endl;
        }
    }
}

vfork()

pid_t vfork()

fork和vfork的区别:
- vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度执行 - vfork和fork一样都创建一个子进程,但它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec或exit,于是也就不访问该地址空间。所以vfork复制后的父子进程会共享同一块空间。

本页由 Markdown 自动渲染 · 共 628 篇笔记