粉絲提問:
彭老師,問下,在程序里面執行system("cd /某個目錄"),這樣會切換不成功,為啥呢
實例代碼:
粉絲的疑惑是明明第10行執行了cd /media操作, 為什么12行執行的pwd > test2.txt結果提示的仍然是當前目錄?
這是一個很不錯的問題,要想整明白這個問題,需要知道system的原理。
system()函數
通過man手冊來查看system庫函數:
由手冊可得:
- system()是庫函數
- 通過fork()函數創建子進程
- 在子進程中通過exec族函數執行shell命令
這樣大家就明白了,實際上system執行參數中的字符串代表的命令, 其實是創建了一個進程,然后在子進程中通過exec族函數來執行對應的命令。
當前工作路徑,cwd,可以通過pwd來獲取,
那么工作路徑是和進程相關的,
第10行代碼執行之后,雖然確實改變了此時的子進程的工作路徑,
但是隨著子進程的退出該工作路徑已沒有意義,
而執行到12行程序的時候,system()又會創建新的子進程,
該子進程仍然繼承父進程的工作路徑,
所以當前工作路徑就沒有變化。
程序中如何修改當前程序的工作路徑?
可以通過函數chdir()
CHDIR(2) linux Programmer's Manual CHDIR(2)
NAME
chdir, fchdir - change working directory
SYNOPSIS
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);
Feature Test macro Requirements for glibc (see feature_test_macros(7)):
fchdir():
_BSD_SOURCE || _XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED
|| /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
DESCRIPTION
chdir() changes the current working directory of the calling process to the directory specified in path.
fchdir() is identical to chdir(); the only difference is that the directory is given as an open file descriptor.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set Appropriately.
該函數是個系統調用(system是庫函數)。
代碼舉例:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(int argc, char **argv)
6 {
7 system("ls");
8 chdir("/");
9 system("ls");
10 return 0;
11 }
peng@ubuntu:~/test$ ./run
123.c a.sh basic chat chris encryption .NET run sgm3141 srand
app boot dev home initrd.img.old lib32 libx32 media opt root sbin srv tftpboot usr vmlinuz www
bin cdrom etc initrd.img lib lib64 lost+found mnt proc run snap sys tmp var vmlinuz.old
由結果可知,8行代碼修改了當前進程的工作路徑為根路徑, 所以第9行執行ls命令顯示的是根路徑下面的內容
驗證system()
下面我們來看一下,system()這個函數是如何調用系統調用的。
編寫測試函數
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 int main(int argc, char **argv)
5 {
6 system("ls");
7 return 0;
8 }
要想知道某個函數調用過程,最終調用到哪些系統調用函數,可以借助strace命令
在Linux系統中,strace命令是一個集診斷、調試、統計與一體的工具,可用來追蹤調試程序,能夠與其他命令搭配使用
執行結果:
由截圖可知, 當我們運行程序時,首先會加載鏈接庫,以便于我們執行當前程序,
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
最終調用到系統調用函數clone(),
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fffdff4b59c) = 2753
并獲取到執行結果
wait4(2753, 123.c a.sh basic chat chris encryption net run sgm3141 srand
但是前面說了,system不是調用fork的嗎?
man clone
寫的很清楚了,clone與fork行為一致。