Sous Linux, l'exécution de ls entraînera des appels système read et exec ; l'exécution de n'importe quelle commande shell appellera fork et exec, mais si vous utilisez strace pour vérifier les appels système provoqués par ls, il n'y a pas de fork. dans le répertoire , donc read est appelé.
L'environnement d'exploitation de ce tutoriel : système linux7.3, ordinateur Dell G3.
La réponse est la série read et exec
Le mécanisme d'exécution des commandes shell est fork+exec, fork est un clone et execve est une transformation. La commande ls répertorie les fichiers du répertoire, donc read sera également appelé.
L'accès shell au noyau Linux est obtenu via les commandes fork et exec. La commande fork peut être créée dans le même thread.
Utilisez strace pour vérifier les appels système provoqués par ls. Il est vrai qu'il n'y a pas de fork, mais parce que l'exécution de n'importe quelle commande shell appellera fork
La transformation de execve consiste à créer un nouveau processus et à le remplacer. avec le nouveau processus Supprimez le processus d'origine.
Tout d’abord, parlons de ce que sont les appels système ?
Les utilisateurs peuvent accéder et contrôler les fichiers et les périphériques à l'aide d'un petit nombre de fonctions directement fournies par UNIX/linux. Ces fonctions sont des appels système
[1]. 系统调用
[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]
Unix将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建了一个进程之后,通常将子进程替换成新的进程映象。所以任何shell命令都会创建进程,都会用到exec系统调用。
例如:在shell命令行执行ps命令,实际上是shell进程调用fork复制一个新的子进程,在利用exec系统调用将新产生的子进程完全替换成ps进程。
用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID。exec名下是由多个关联函数组成的一个完整系列[4]
调用fork创建新进程后,父进程与子进程几乎一模一样[1,p398]。
fork是一个UNIX术语,当fork一个进程(一个运行中的程序)时,基本上是复制了它,并且fork后的两个进程都从当前执行点继续运行,并且每个进程都有自己的内存副本。
原进程是父进程,新进程是子进程。可以通过fork()
strace ls
, nous pouvons visualiser les appels système utilisés par la commande ls [2]. Une partie du résultat est la suivante :
#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 */
read()
[3]. Donc ls
doit utiliser l'appel système read
. De plus, toute commande shell qui crée un processus utilisera l'appel système exec. Remontons en arrière et trions les doutes que l'on peut avoir sur ces concepts : Par exemple : lors de l'exécution de la commande ps sur la ligne de commande shell, le processus shell appelle en fait fork pour copier un nouveau processus enfant, puis utilise l'appel système exec pour remplacer complètement le processus enfant nouvellement généré par le processus ps.
🎜Utilisez la fonction exec pour remplacer le processus actuel par un nouveau processus, et le nouveau processus a le même PID que le processus d'origine. Sous le nom exec se trouve une série complète composée de multiples fonctions associées [4]🎜🎜Après avoir appelé fork pour créer un nouveau processus, le processus parent et le processus enfant sont presque identiques [1, p398]. 🎜🎜fork est un terme UNIX, lorsqu'un processus (un programme en cours d'exécution) est fork, il est essentiellement copié, et les deux processus après le fork continuent de s'exécuter à partir du point d'exécution actuel, et chaque processus a sa propre copie de mémoire. 🎜🎜Le processus original est le processus parent et le nouveau processus est le processus enfant. Il peut être distingué par la valeur de retour defork()
. 🎜🎜🎜L'appel fork dans le processus parent renvoie le pid (identifiant du processus) du nouveau processus enfant, tandis que l'appel fork dans le processus enfant renvoie 0🎜🎜🎜Par exemple : 🎜#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 */
#include<unistd.h> ssize_t read(int filedes, void *buf, size_t nbytes);
对内核而言,所有打开的文件都通过文件描述符引用[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
.
包括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]。
[7]中提到大多数文件I/O用到的5个函数为:open/read/write/lseek/close
调用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视频教程》
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!