Linux執行ls,會引發哪些系統調用

從一個問題提及html

Linux執行ls,會引發哪些系統調用?linux

A. nmapshell

B. read編程

C. execve數組

D. fork網絡

答案是read、exec系列函數

首先咱們討論一下什麼是系統調用(system calls)?
用戶藉助UNIX/linux直接提供的少許函數能夠對文件和設備進行訪問和控制,這些函數就是系統調用[1]。.net

使用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。

  • 當從終端設備讀時,一般一次最多讀一行

  • 當從網絡讀時,網絡中的緩衝機構可能形成返回值小於所要求讀的字節數。

  • 當從管道或FIFO讀時,如若管道包含的字節少於所需的數量,那麼read將只返回實際可用的字節數。

  • 當從某些面向記錄的設備(例如磁盤)讀時,一次最多返回一個記錄。

  • 當某一信號形成中斷,而已經讀了部分數據量時。讀操做從文件的當前偏移量出開始,在成功返回以前,該偏移量將增長實際獨到的字節數

read的經典原型定義則是:

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

References

相關文章
相關標籤/搜索