Linux 系统调用 文件描述符

系统调用

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

批注 2021-11-30 212341

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

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

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

file.txt

abcdef

main.c 先open再fork

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>

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=%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child buff=%s\n",buff);
    }
    else
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("parent buff=%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent buff=%s\n",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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>

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=%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child buff=%s\n",buff);
    }
    else
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("parent buff=%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent buff=%s\n",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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>

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

    printf("fd=%d\n",fd);

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

    if(pid==0)
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("child buff=%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child buff=%s\n",buff);
    }
    else
    {
        char buff[32]={0};
        read(fd,buff,1);
        printf("parent buff=%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent buff=%s\n",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标准输入stdin
1标准输出stdout
2标准错误输出stderr
3

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

open之后得到以下:

批注 2021-11-30 220816

file.txt这块是指针 浅拷贝

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

第一种情况

先open再fork

批注 2021-11-30 221144

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

第二种情况

先fork再open

批注 2021-11-30 221715

用户空间、内核空间

批注 2021-12-01 140326

系统调用和库函数的区别

书第五章

替换进程

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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>

int main()
{
    printf("test pid=%d\n",getpid());

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

    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,...,/*(char*) NULL*/);

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

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

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

execlp

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

自己去找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=%d\n",getpid());
    
    execle("/usr/bin/ps","ps","-f",(char*)0,envp);

    printf("exec err\n");

    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=%d\n",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 err\n");

    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=%d\n",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 err\n");

    exit(0);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值