• 技术文章 >运维 >linux运维

    linux执行ls会引起哪些系统调用

    长期闲置长期闲置2022-03-18 11:05:00原创367

    在linux中,执行ls会引起read和exec系统调用;执行任何一个shell命令都会调用fork和exec,但是通过strace去查看ls引起的系统调用并没有fork,ls命令要列出目录下的文件,所以要调用read。

    本教程操作环境:linux7.3系统、Dell G3电脑。

    linux执行ls会引起哪些系统调用

    答案是read、exec系列

    shell命令执行机制就是 fork+exec, fork是分身,execve是变身。ls命令要列出目录下的文件,所以read也会调用。

    shell访问Linux内核就是通过fork和exec命令实现的,fork命令创建可以一个相同的线程出来。

    通过strace去查看ls引起的系统调用,确实是没有fork,但是因为执行任何一个shell命令都会调用fork

    execve的变身就是创建一个新的进程,并用新的进程去替换掉原来的进程。

    首先我们讨论一下什么是系统调用(system calls)?
    用户借助UNIX/linux直接提供的少量函数可以对文件和设备进行访问和控制,这些函数就是系统调用[1]。

    使用strace ls命令我们可以查看ls命令使用到的系统调用[2],其中一部分输出内容如下:

    open(".", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 3
    getdents64(3, /* 68 entries */, 32768)  = 2240
    getdents64(3, /* 0 entries */, 32768)   = 0
    close(3)                                = 0

    open系统调用打开当前目录文件,返回获得的文件描述符。可以看到该文件使用O_RDONLY标志打开。

    只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节[3]。

    所以ls要用到read系统调用。除此之外,任何shell命令都会创建进程,都会用到exec系统调用。

    回过头来梳理一下我们对于这些概念可能产生的疑惑:

    1. 包括ls在内,一个程序是如何运行的?
    2. open系统调用打开当前目录文件,返回获得的文件描述符。那什么是文件描述符?

    1 进程是如何运行的

    每个运行中的程序被称为进程[1]

    Unix将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建了一个进程之后,通常将子进程替换成新的进程映象。所以任何shell命令都会创建进程,都会用到exec系统调用。
    例如:在shell命令行执行ps命令,实际上是shell进程调用fork复制一个新的子进程,在利用exec系统调用将新产生的子进程完全替换成ps进程。

    用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID。exec名下是由多个关联函数组成的一个完整系列[4]

    调用fork创建新进程后,父进程与子进程几乎一模一样[1,p398]。

    fork是一个UNIX术语,当fork一个进程(一个运行中的程序)时,基本上是复制了它,并且fork后的两个进程都从当前执行点继续运行,并且每个进程都有自己的内存副本。

    原进程是父进程,新进程是子进程。可以通过fork()返回值区分。

    父进程中fork调用返回的是新的子进程的pid(process id),而子进程中fork调用返回的是0

    举个例子:

    #include<unistd.h>
    #include<stdio.h>
    #define LEN 10
    int main()
    {
        pid_t id=getpid();
        printf("Main pid: %d \n",id);
    	int i;
    	pid_t res=fork();
    	if(res==0)
    	{
    	  for(i =0;i<LEN;i++) 
    	  {
    		pid_t id1=getpid();
    		printf("%d ",id1);
    		printf("Child process:%d\n",i);
    	  }
    	}
    	else
    	{
    	  printf("res %d\n",res);
    	  for(i=0;i<LEN;i++) 
    	  {
    		pid_t  id2=getpid();
    		printf("%d ",id2);
    		printf("parent process:%d\n",i);
    	  }
    	}
    
    	printf("THE END\n");
    	 return 0;
    }
    
    /*output
    Main pid: 10965 
    res 10966
    10965 parent process:0
    10965 parent process:1
    10965 parent process:2
    10965 parent process:3
    10965 parent process:4
    10965 parent process:5
    10965 parent process:6
    10965 parent process:7
    10965 parent process:8
    10965 parent process:9
    10966 Child process:0
    10966 Child process:1
    THE END
    10966 Child process:2
    10966 Child process:3
    10966 Child process:4
    10966 Child process:5
    10966 Child process:6
    10966 Child process:7
    10966 Child process:8
    10966 Child process:9
    THE END
    */

    如果想要程序启动另一程序的执行但自己仍想继续运行的话,怎么办呢?那就是结合fork与exec的使用[6][1, p397]

    举个例子(修改自[6]):

    #include<string.h>
     #include <errno.h>
     #include <stdio.h>
     #include <stdlib.h>
    #include<unistd.h>
     
     char command[256];
     void main()
     {
        int rtn; /*子进程的返回数值*/
        while(1) {
           /* 从终端读取要执行的命令 */
           printf( ">" );
           fgets( command, 256, stdin );
           command[strlen(command)-1] = 0;
           if ( fork() == 0 ) {/* 子进程执行此命令 */
              execlp( command, NULL );
              /* 如果exec函数返回,表明没有正常执行命令,打印错误信息*/
              perror( command );
              exit( errno );
           }
           else {/* 父进程, 等待子进程结束,并打印子进程的返回值 */
              pid_t sonid=wait ( &rtn );
              printf(" child pid: %d\n",sonid);
              printf( " child process return %d\n", rtn );
           }
       }
    }
    
    /*output:错误命令、需要参数命令、正确命令
    >aa
    aa: No such file or directory
     child pid: 11230
     child process return 512
    >echo
    A NULL argv[0] was passed through an exec system call.
     child pid: 11231
     child process return 134
    >ps
     child pid: 11247
     child process return 139
    */

    先fork,然后子进程借助exec调用程序command。对错误命令、需要参数的命令、以及不需要参数的命令给出对应的输出。

    2 文件描述符(file descripter,fd)

    一切设备都可以看作文件。

    对内核而言,所有打开的文件都通过文件描述符引用[7]。文件描述符是非负整数,范围是[0,OPEN_MAX -1]。现在OPEN_MAX 一般为64

    但是[7]又说对于FreeBSD 8.0,Linux 3.2.0 ,Mac OS X 10.6.8等, fd变化范围几乎无限,只受到存储器数量、int字长以及系统管理员所配置的软限制和硬限制的约束。。。why?

    当open或者create一个新文件时,内核向进程返回一个文件描述符。

    当读、写一个文件时,使用open或create返回的文件描述符标识该文件,将其作为参数传送给read / write

    按照惯例,fd为0 / 1 / 2分别关联STDIN_FILENO / STDOUT_FILENO / STDERR_FILENO。这些常量也定义在unistd.h.

    3 系统调用包含在哪些头文件中呢?

    包括exec、fork、read、write在内,许多系统调用包含在unistd.h头文件中

    POSIX,Portable Operating System Interface。是UNIX系统的一个设计标准,很多类UNIX系统也在支持兼容这个标准,如Linux。
    unistd.h是POSIX标准定义的unix类系统定义符号常量的头文件,包含了许多UNIX系统服务的函数原型[5]。在该头文件,用于访问设备驱动程序的底层函数(系统调用)有这五个:open/close/read/write/ioctl[1]。

    4 文件I/O

    [7]中提到大多数文件I/O用到的5个函数为:open/read/write/lseek/close

    4.1 函数read

    调用read函数从打开文件中读数据。

    #include<unistd.h>
    ssize_t read(int filedes, void *buf, size_t nbytes);

    返回值:

    成功,读出的字节数;

    失败,-1;

    遇到文件尾,0

    有多种情况可使实际读到的字节数少于要求读的字节数:

    例如,若在到达文件尾端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将回0。

    read的经典原型定义则是:

    int read(int fd, char*buf, unsigned nbytes);

    相关推荐:《Linux视频教程

    以上就是linux执行ls会引起哪些系统调用的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:linux
    上一篇:linux中sshd是什么 下一篇:linux编辑文件的命令是什么
    Web大前端开发直播班

    相关文章推荐

    • linux为什么要挂载• linux中grep是什么• linux中什么是ha• linux中proc是什么• linux的用户组是什么
    1/1

    PHP中文网