在linux中,執行ls會造成read和exec系統呼叫;執行任何一個shell指令都會呼叫fork和exec,但透過strace去查看ls所造成的系統呼叫並沒有fork,ls指令要列出目錄下的文件,所以要呼叫read。
本教學操作環境:linux7.3系統、Dell G3電腦。
答案是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]
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。對錯誤命令、需要參數的命令、以及不需要參數的命令給予對應的輸出。
一切裝置都可以看作檔案。
对内核而言,所有打开的文件都通过文件描述符引用[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视频教程》
以上是linux執行ls會引起哪些系統調用的詳細內容。更多資訊請關注PHP中文網其他相關文章!