linux执行ls会引起什么系统调用

65次阅读
没有评论

共计 4450 个字符,预计需要花费 12 分钟才能阅读完成。

这篇文章给大家分享的是有关 linux 执行 ls 会引起什么系统调用的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。

在 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 系统调用。

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

包括 ls 在内,一个程序是如何运行的?

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 i++) 
  {pid_t id1=getpid();
 printf(%d  ,id1);
 printf(Child process:%d\n ,i);
  }
 else
  printf(res %d\n ,res);
  for(i=0;i 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: 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
 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。

当从终端设备读时,通常一次最多读一行

当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。

当从管道或 FIFO 读时,如若管道包含的字节少于所需的数量,那么 read 将只返回实际可用的字节数。

当从某些面向记录的设备(例如磁盘)读时,一次最多返回一个记录。

当某一信号造成中断,而已经读了部分数据量时。读操作从文件的当前偏移量出开始,在成功返回之前,该偏移量将增加实际独到的字节数

read 的经典原型定义则是:

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

感谢各位的阅读!关于“linux 执行 ls 会引起什么系统调用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计4450字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)