Linux 系统调用 文件描述符

Linux 系统调用 文件描述符,第1张

Linux 系统调用 文件描述符

目录
  • 系统调用
    • main.c 先open再fork
    • main.c 先fork再open
    • main.c 打印fd的值
      • 第一种情况
      • 第二种情况
    • 用户空间、内核空间
    • 系统调用和库函数的区别
  • 替换进程
    • test.c
    • execl
    • execlp
    • execle
    • execv
    • execvp
    • execve

系统调用

PCB进程控制块进程描述符==struct task_struct

fork是写时拷贝技术,提高了效率

open read write close *** 作文件的系统调用 实现在内核中

fopen fread fgets fwrite fclse 库函数 /usr/lib/libc.so

file.txt

abcdef
main.c 先open再fork
#include
#include
#include
#include
#include
#include

int main()
{
    int fd=open("file.txt",O_RDONLY);//父进程打开
    assert(fd!=-1);

    pid_t pid=fork();
    assert(pid!=-1);

    if(pid==0)
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("child buff=%sn",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child buff=%sn",buff);
    }
    else
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("parent buff=%sn",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent buff=%sn",buff);
    }

    close(fd);
    exit(0);
}

结果

stu@stu-virtual-machine:~/Linux/day09$ cat file.txt
abcdef
stu@stu-virtual-machine:~/Linux/day09$ ./main
parent buff=a
child buff=b
child buff=c
parent buff=d

文件偏移量

先open后fork

父进程打开的文件fork之后会被子进程复制过去(相当于子进程自己没有做open,而是继承了父进程的打开)

main.c 先fork再open
#include
#include
#include
#include
#include
#include

int main()
{
    pid_t pid=fork();
    assert(pid!=-1);
    
    int fd=open("file.txt",O_RDONLY);//父进程打开
    assert(fd!=-1);

    if(pid==0)
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("child buff=%sn",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child buff=%sn",buff);
    }
    else
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("parent buff=%sn",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent buff=%sn",buff);
    }

    close(fd);
    exit(0);
}

结果

stu@stu-virtual-machine:~/Linux/day09$ ./main
parent buff=a
child buff=a
parent buff=b
child buff=b
main.c 打印fd的值
#include
#include
#include
#include
#include
#include

int main()
{
    int fd=open("file.txt",O_RDONLY);//父进程打开
    assert(fd!=-1);

    printf("fd=%dn",fd);

    pid_t pid=fork();
    assert(pid!=-1);

    if(pid==0)
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("child buff=%sn",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child buff=%sn",buff);
    }
    else
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("parent buff=%sn",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent buff=%sn",buff);
    }

    close(fd);
    exit(0);
}

结果:fd=3

stu@stu-virtual-machine:~/Linux/day09$ ./main
fd=3
parent buff=a
child buff=b
parent buff=c
child buff=d

PCB里边有文件表—结构体数组(fork后父进程的文件表是什么样子子进程就是什么样子):记录打开的文件

一个进程启动起来,至少打开三个文件:

0标准输入stdin1标准输出stdout2标准错误输出stderr3

文件描述符总是一个非负数,fd至少是3

open之后得到以下:

file.txt这块是指针 浅拷贝

磁盘上的文件都有唯一的id值:ls -i

第一种情况

先open再fork

由图可以看出parent child打印abcd的原因

第二种情况

先fork再open

用户空间、内核空间

系统调用和库函数的区别

书第五章

替换进程

exec 系列

execl execlp execle 库函数

execv execvp 库函数

(上边5个都调用下边这个系统调用)

execve 系统调用

Linux 产生新进程: fork+exec

执行两次 ps -f

stu@stu-virtual-machine:~/Linux/day09$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
stu         2412    2402  0 14:14 pts/0    00:00:00 bash
stu         2419    2412  0 14:14 pts/0    00:00:00 ps -f
stu@stu-virtual-machine:~/Linux/day09$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
stu         2412    2402  0 14:14 pts/0    00:00:00 bash
stu         2425    2412  0 14:14 pts/0    00:00:00 ps -f

ps -f的ppid都是bash的pid,bash把自己fork复制一份再exec替换成ps -f(系统中的新进程都是这么来的 eg: ./main)

test.c
#include
#include
#include
#include
#include
#include

int main()
{
    printf("test pid=%dn",getpid());

    execl("/usr/bin/ps","ps","-f",(char*)0);
    
    printf("exec errn");

    exit(0);
}

execl执行成功没有返回值,没成功继续执行原代码

结果

stu@stu-virtual-machine:~/Linux/day09$ ./test
test pid=3071
UID          PID    PPID  C STIME TTY          TIME CMD
stu         2412    2402  0 14:14 pts/0    00:00:00 bash
stu         3071    2412  0 14:42 pts/0    00:00:00 ps -f

man 3 exec

execl
int execl(const char* path, const char * arg,...,);

path:新替换的程序的路径名称(eg.which ps)

arg :传给新程序主函数的第一个参数,一般为程序的名字

arg 后面是剩余参数列表,参数个数可变,必须以空指针作为最后一个参数

execlp
int execlp(const char* file, const char * arg,...,);

自己去找ps的位置

execlp("ps","ps","-f",(char*)0);
execle
int execle(const char* path, const char * arg,...,char* const envp[]);

传环境变量

int main(int argc,char*argv[],char*envp[])
{
    printf("test pid=%dn",getpid());
    
    execle("/usr/bin/ps","ps","-f",(char*)0,envp);

    printf("exec errn");

    exit(0);
}
execv
int execv(const char * path, char* const argv[]);

可以写一个char* myargv[10]={“ps”,"-f"};保存

int main(int argc,char*argv[],char*envp[])
{
    printf("test pid=%dn",getpid());

    //execl("/usr/bin/ps","ps","-f",(char*)0);
    //execlp("ps","ps","-f",(char*)0);
    //execle("/usr/bin/ps","ps","-f",(char*)0,envp);
    char* myargv[10]={"ps","-f"};
    execv("/usr/bin/ps",myargv);

    printf("exec errn");

    exit(0);
}
execvp
int execvp(const char * file, char* const argv[]);

eg.

execvp("ps",myargv);
execve
int execve(const char * path, char* const argv[],char* const envp[]);

最终test.c

int main(int argc,char*argv[],char*envp[])
{
    printf("test pid=%dn",getpid());

    //execl("/usr/bin/ps","ps","-f",(char*)0);
    //execlp("ps","ps","-f",(char*)0);
    //execle("/usr/bin/ps","ps","-f",(char*)0,envp);
    char* myargv[10]={"ps","-f"};
    //execv("/usr/bin/ps",myargv);
    //execvp("ps",myargv);
    execve("/usr/bin/ps",myargv,envp);

    printf("exec errn");

    exit(0);
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5693866.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存