主頁 >  其他 > 【Linux】一篇文章搞定 行程 及相關操作

【Linux】一篇文章搞定 行程 及相關操作

2020-12-31 10:32:47 其他

文章目錄

  • Linux下行程相關操作
    • 1. 行程概念
      • 1.1 行程基本概念
      • 1.2 Linux下PCB的定義
    • 2.行程查看
    • 3. 行程創建
      • 3.1 fork函式
      • 3.2 vfork函式
      • 3.3 fork與vfork的異同
      • 3.4 寫時拷貝技術
    • 4. 行程退出
      • 4.1 exit函式
      • 4.2 _exit函式
      • 4.3 exit與_exit的異同
    • 5. 行程等待
      • 5.1 wait函式
      • 5.2 waitpid函式
    • 6. 程式替換
      • 6.1 execl函式和execv函式
      • 6.2 execlp函式和execvp函式
      • 6.3 execle函式和execve函式

Linux下行程相關操作

1. 行程概念

1.1 行程基本概念

行程定義:一個具有一定獨立功能的程式在一個資料集合上的一次動態執行程序

簡言之:行程是程式的一次執行程序

行程特性

  1. 動態性:行程是動態產生的,往往都會經歷創建、運行、消亡三個狀態
  2. 獨立性:各個行程之間的地址空間相互獨立
  3. 并發性:任何行程都可以一起向前推進
  4. 異步性:每個行程都以其不可預知的速度向前推進
  5. 結構化:行程 = 代碼段 + 資料段 + PCB(行程控制塊)

行程與程式的區別

  1. 行程是動態的執行程序;程式是靜態的代碼
  2. 行程是暫時的,運行在記憶體中的一個狀態變化的程序;程式是永久的,保存在外存
  3. 通過多次執行一個程式,進而產生多個行程,所以一個程式可以對應多個行程;
  4. 行程通過呼叫多個程式,進而一個行程可以執行多個程式,所以一個行程可以對應多個程式

行程是競爭計算機資源的基本單位

1.2 Linux下PCB的定義

  • 行程控制塊(PCB)是用來記錄行程相關資訊管理行程而設定的一種資料結構

  • 行程控制塊(PCB)是由作業系統(OS)維護

  • 系統通過PCB感知行程的存在

  • PCB隨行程的創建而創建并填寫,隨著行程的消亡而釋放

PCB的組成

  • 行程識別符號:用于唯一標識該行程的整數
  • 行程名:通常是可執行檔案名
  • 優先級:行程優先運行的權重
  • 暫存器值:用于保存當前行程運行到某一時刻各種資料資訊

在Linux作業系統中,每一個行程都有一個PCB,每一個PCB都對應一個task_struct結構體,簡言之,每創建一個行程就相當于創建一個task_struct結構體并填寫其中的資料,

Linux中的PCB定義在sched.h檔案中

若想獲得sched.h檔案,需要到Linux內核官網下載內核原始碼

以Linux Kernel Source Code 2.6.32 為例展示task_struct部分原始碼

在這里插入圖片描述

2.行程查看

  • 使用ps命令可以查看正在運行的行程瞬時的資訊,而不是動態連續的資訊

引數:

  • -a : 顯示所有用戶的行程
  • -u : 以用戶為主的行程狀態
  • -x : 列出較完整的資訊

列印顯示出來的資訊:

PID:行程識別符號

TTY:命令所運行的位置

STAT:行程狀態

TIME:運行該命令所占用的CPU處理時間

COMMAND:該行程所運行的命令

1.顯示當前所有行程及詳細資訊

[gongruiyang@localhost ~]$ ps -ax
   PID TTY      STAT   TIME COMMAND
     1 ?        Ss     0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
     2 ?        S      0:00 [kthreadd]
     3 ?        S      0:00 [ksoftirqd/0]
     5 ?        S<     0:00 [kworker/0:0H]
     7 ?        S      0:00 [migration/0]
 ...................................................

2.顯示某用戶的行程及詳細資訊

[gongruiyang@localhost ~]$ ps -u gongruiyang 
   PID TTY          TIME CMD
  1736 ?        00:00:00 gnome-keyring-d
  1741 ?        00:00:00 gnome-session-b
  1748 ?        00:00:00 dbus-launch
  1749 ?        00:00:00 dbus-daemon
  1807 ?        00:00:00 gvfsd
  1812 ?        00:00:00 gvfsd-fuse
  1904 ?        00:00:00 ssh-agent
  1923 ?        00:00:00 at-spi-bus-laun
  1928 ?        00:00:00 dbus-daemon
 ...................................................

3.顯示行程及其CPU和記憶體占用情況

[gongruiyang@localhost ~]$ ps -aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.1 128212  6836 ?        Ss   11:00   0:01 /usr/li
root          2  0.0  0.0      0     0 ?        S    11:00   0:00 [kthrea
root          3  0.0  0.0      0     0 ?        S    11:00   0:00 [ksofti
root          5  0.0  0.0      0     0 ?        S<   11:00   0:00 [kworke
root          7  0.0  0.0      0     0 ?        S    11:00   0:00 [migrat
root          8  0.0  0.0      0     0 ?        S    11:00   0:00 [rcu_bh

3. 行程創建

3.1 fork函式

pid_t fork(void)

fork呼叫一次,會在父行程中回傳一個值,會在子行程中回傳一個值

父行程中:回傳子行程PID

子行程中:回傳 0

Q:為什么fork會回傳兩次呢?

A:子行程復制父行程的堆和堆疊中的內容,此時,兩個行程都處于fork函式中,都在等待fork函式執行結束并回傳一個pid_t,所以會有兩個回傳值

pid_t實際上就是int,被定義在sys/typpes.h中


代碼演示:

getpid()函式是獲得當前行程的行程號

getppid()函式是獲得當前行程父行程的行程號

#include <stdio.h>                                          
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

int main()
{
  printf("       [fpid] [pid] [ppid]\n");
  pid_t fpid = fork();

  if(fpid < 0)	//創建行程失敗
    perror("fork");
  else if(fpid == 0)//子行程執行這一段代碼
    printf("child: %4d %4d %4d\n",fpid,getpid(),getppid());
  else	//父行程執行這一段代碼
    printf("father: %4d %4d %4d\n",fpid,getpid(),getppid());

  while(1)	//父子行程都執行
  {
    sleep(1);
  }

  return 0;
}

運行結果:

       [fpid] [pid] [ppid]
father:  5081  5080   2717
child:      0  5081   5080

解釋:pid為5080的父行程 創建了一個 pid為5081的子行程,其中 父行程的 父行程號為2717,子行程的 父行程號為5080


fork特性:

子行程將父行程中打開的所有檔案描述符都復制了一遍,父子行程中相同編號的檔案描述符在內核中指向同一個file結構體,也就是說,file結構體的參考數量增加

子行程不會繼承父行程的一些資料:

  1. 子行程不復制父行程設定的(若繼承會導致排它鎖矛盾)
  2. 子行程不復制父行程的pid,而是產生自己的pid
  3. 子行程不復制父行程中的pending alarmspending signals,而是將自己的pending alarms清除,將pending signals置為空

fork產生失敗可能的兩個原因:

  1. 當前運行中的行程數已經達到了系統規定的上限,此時錯誤碼(errno)的值為EAGAIN
  2. 當前系統記憶體容量不足以開辟一個新的行程,此時錯誤碼(errno)的值為ENOMEM

3.2 vfork函式

pid_t vfork(void)
  • 該函式回傳值特點與fork相同

  • vfork創建出來的子行程并不會直接將父行程的虛擬空間內容拷貝一份,而是與父行程共享一份虛擬空間,當子行程需要修改資料時,才會進行拷貝,這稱之為寫時拷貝技術

  • 父行程使用vfork創建子行程后,父行程會被掛起,直到子行程終止或被替換后,才能繼續推進

  • 應當使用exit或_exit來終止vfork創建的子執行緒,不能使用return來終止,若使用return來終止子執行緒會導父行程回到呼叫vfork處,進而無限創建子行程進而產生段錯誤


代碼演示:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{

  pid_t fpid = vfork();

  if(fpid < 0)
    perror("vfork");
  else if(fpid == 0)
  {
    printf("child:%d\n",getpid());
    while(1)
    {
      printf("while\n");                 
      sleep(1);
    }
  }
  else
    printf("father:%d\n",getpid());

  printf("========================\n");

  return 0;
}

輸出結果:

child:5898
while
while
while
while
while
^C

解釋:由于子行程處于回圈sleep中,導致父行程一直被掛起無法執行下面的代碼

3.3 fork與vfork的異同

異:

  1. fork創建的子行程是父行程的一個副本,子行程將父行程的堆疊中存的資料資訊拷貝一份到另外開辟的記憶體中去,并不能共享這些資料;vfork創建的子行程并不會立刻開辟新記憶體拷貝資料,而是共享父行程的堆疊中的資料,直到子行程終止或者被替換之前,都是在父行程的空間中運行
  2. vfork保證子行程先運行;fork是讓兩個行程異步運行

同:

  1. fork和vfork都是呼叫一次,但是回傳兩次

3.4 寫時拷貝技術

傳統拷貝方法:

  1. 為子行程的頁表分配頁幀
  2. 為子行程的頁分配頁幀
  3. 初始化子行程的頁表
  4. 把父行程的頁內容復制到子行程對應頁中
  • 傳統的拷貝父行程資源實作過于簡單且效率低下
  • 寫時拷貝是一種可以推遲甚至免除拷貝資料的技術

寫時拷貝(Copy - On - Write)技術:

子行程并不拷貝父行程的資料資源,而是父子行程共享父行程原有的資料資源,只有當要寫入的時候,才進行資源的復制

實際上,COW不但在Linux行程上有實際應用,而且在C++的String類在g++環境下也支持COW技術

#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;

int main()
{
	string str1 = "Hello\n";
	string str2 = str1;

	printf("Befor Write: str1[%x] str2[%x]\n", str1.c_str(), str2.c_str());
	
	str2.clear();
	str2 = "H\n";
	printf("After Write: str1[%x] str2[%x]\n", str1.c_str(), str2.c_str());

	return 0;
}

輸出結果:

[gongruiyang@localhost TestCOW]$ g++ COWtest.cpp -o test
[gongruiyang@localhost TestCOW]$ ./test 
Befor Write: str1[18cec38] str2[18cec38]
After Write: str1[18cec38] str2[18cec98]

解釋:從輸出結果來看,str1和str2一開始指向同一記憶體地址,共享Hello這個資料,當str2要進行寫入的時候,str2指向的地址發生改變,重新開辟空間存放資料,即只有寫入東西的時候才進行記憶體的再分配

4. 行程退出

4.1 exit函式

void exit(int status)
  • 功能:正常終止行程
  • 頭檔案:stdlib.h
  • status:回傳給父行程的狀態值,通常用0或EXIT_SUCCESS表示成功,通常用非0或EXIT_FAILURE表示例外程式終止

代碼測驗:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t fpid = fork();
  int status;
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    printf("ChildP: childPID[%d]\n",getpid());
    exit(EXIT_SUCCESS);		//正常退出子行程
  }
  else
  {
    int ret_pid = wait(&status); //父行程阻塞等待子行程退出
    printf("FatherP: %d is normally exited with status:%d\n",ret_pid,status);	
  }

  return 0;
}

輸出結果:

ChildP: childPID[8071]
FatherP: 8071 is normally exited with status:0

4.2 _exit函式

void _exit(int status);
  • 功能:立即終止子行程,并關閉所有屬于該行程的檔案描述符,該行程的所有子行程過繼給init行程,并向父行程發送SIGCHLD信號,將status作為子行程退出狀態回傳給父行程
  • 頭檔案:unistd.h
  • status:回傳給父行程的狀態值,通常用0或EXIT_SUCCESS表示成功,通常用非0或EXIT_FAILURE表示例外程式終止

代碼測驗:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t fpid = fork();
  int status;
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    printf("ChildP: childPID[%d]\n",getpid());
    _exit(EXIT_SUCCESS);
  }
  else
  {
    int ret_pid = wait(&status);                                              
    printf("FatherP: %d is normally exited with status:%d\n",ret_pid,status);
  }

  return 0;
}

輸出結果:

ChildP: childPID[8586]
FatherP: 8586 is normally exited with status:0

4.3 exit與_exit的異同

異:

  1. exit定義在stdlib.h中;_exit定義在unistd.h中
  2. exit會進行緩沖區重繪;_exit不會重繪緩沖區可能導致資料丟失

同:

  1. 退出子行程并向父行程回傳退出狀態資訊
  2. exit是_exit的封裝形式

代碼測驗:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  printf("Hello!\n");
  printf("World!");
  exit(0);
                     
  return 0;
}
#include <stdio.h>  
#include <unistd.h>  
int main()  
{  
  printf("Hello!\n"); 
  printf("World!");  
  _exit(0);           
                      
  return 0;           
}                     

exit輸出結果:

Hello!
World!

_exit輸出結果:

Hello!

程式解釋:由測驗代碼可以看出_exit由于不重繪緩沖區導致了資料丟失,而exit將__exit進行封裝了,便得到更加安全,保全了資料

5. 行程等待

等待子行程退出->獲取子行程回傳值->釋放子行程資源->防止僵尸行程產生->防止資源泄漏

5.1 wait函式

pid_t wait (int* status)
  1. 包含頭檔案:sys/types.h和sys/wait.h
  2. status:出參,用于保存子行程退出時的狀態,
    • 正常退出情況下,一個int中的低16位中的高8位保存回傳值(取出回傳值:status & 0x7f )
    • 例外退出情況下,一個Int中的低7位保存例外退出信號值(取出例外信號值:(status >> 8) & 0xff)
  3. 回傳值:退出行程的pid

功能:阻塞父行程一直等待子行程的退出,當子行程退出后,父行程才脫離阻塞繼續推進

WIFEXITED(int status)

該宏是用于檢測行程是否正在退出:

宏值若為0代表非正常退出,若為非0則代表正常退出


代碼演示:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t fpid = fork();
  int status = 0;
    
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    printf("Child:[%d]\n",getpid());
    exit(3);
  }
  else
  {
    printf("Father:[%d]\n",getpid());
    pid_t ret_pid = wait(&status);
    if(WIFEXITED(status))
        printf("%d has exited with return code %d\n",ret_pid,WEXITSTATUS(status));
    else
        perror("wait");
  }

  return 0;
}

輸出結果:

Father:[6985]
Child:[6986]
6986 has exited with return code 3

5.2 waitpid函式

pid_t waitpid(pid_t pid, int* status, int options)

引數:

  • status:出參,用于保存子行程的退出狀態
  • pid
> 0只等待行程ID為指定pid的子行程
= -1等待任何一個子行程,與wait功能一樣
= 0等待任一個【子行程組ID=父行程組ID】的子行程
< -1等待任一個【子行程組ID= |父行程組ID|】的子行程 (||意為絕對值)
  • options
WNOHANG指定子行程結束才阻塞父行程,回傳值為0
0與wait相同,阻塞父行程,一直等待子行程退出
WUNTRACED子行程若處于暫停狀態:立刻回傳
子行程若處于結束狀態:不予理會
  • 回傳值
> 0已經結束的子行程的pid
0使用options為WUNTRACED且無子行程退出
-1呼叫出錯(例如:無子行程)

功能:非阻塞父行程版本wait

wait中其實呼叫了waitpid


代碼測驗:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>                                             
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t fpid = fork();
  pid_t ret_waitpid = 0;
  int status;
    
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)	//子行程
  {
    printf("Child:%d\n",getpid());
    sleep(3);
    exit(0);	//子行程退出
  }
  else		//父行程
  {
    while(!ret_waitpid)	//回圈waitpid等待子行程的退出
    {
      ret_waitpid = waitpid(fpid,&status,WNOHANG);
      if(ret_waitpid == 0)
      {
        printf("%d has not exit\n",fpid);	//子行程未退出就列印
        sleep(1);
      }
    }//while結束:說明此事ret_waitpid接收到了子行程exit后回傳的pid
    if(ret_waitpid == fpid)
      printf("%d has exited with return code:%d\n",fpid,status);
  }

  return 0;
}

輸出結果:

9311 has not exit
Child:9311
9311 has not exit
9311 has not exit
9311 has exited with return code:0

6. 程式替換

創建子行程的目的是為了完成其他的事情,完成其他任務,這個時候就用到了程式替換

輔助代碼:子行程需要做的任務,可執行檔案名為child_task,絕對路徑為:/home/gongruiyang/ClassLinunx/pidTest/child_task

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void child_task(int argc, char* argv[])
{
  for(int i = 0; i < argc; i++)
    printf("argv[%d] = %s\n",i,argv[i]);
}
//main函式有倆引數:argc表示程式的運行引數個數,argv用于保存程式引數
int main(int argc,char* argv[])
{
  child_task(argc,argv );
                                                                    
  return 0;
}

6.1 execl函式和execv函式

int execl(const char *path, const char *arg, ...);
  • path:用于保存想要執行的可執行檔案絕對路徑
  • arg:執行可執行檔案所需要的引數,如果不需要引數可以填NULL
  • …:不定引數,說明后面的引數可以是1個,也可以是多個

代碼測驗:

#include <stdio.h>
#include <unistd.h>                                                      
#include <stdlib.h>

int main()
{
  pid_t  fpid = fork();

  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    execl("/home/gongruiyang/ClassLinunx/pidTest/child_task","-a","-b");
  }
  else
  {
    printf("Father Do!\n");
    sleep(1);
  }

  sleep(1);
  return 0;
}

輸出結果:

[gongruiyang@localhost pidTest]$ ./exectest 
Father Do!
argv[0] = -a
argv[1] = -b

int execv(const char *path, char *const argv[]);
  • path:用于保存想要執行的可執行檔案絕對路徑
  • argv:引數串列

代碼測驗:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
  pid_t  fpid = fork();
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {

    char* child_argv[32];
    child_argv[0] = "-a";
    child_argv[1] = "-b";
    child_argv[2] = "-c";                                               
    child_argv[3] = NULL; //必須要有

    execv("/home/gongruiyang/ClassLinunx/pidTest/child_task",child_argv)
  }
  else
  {
    printf("Father Do!\n");
    sleep(1);
  }

  sleep(1);
  return 0;
}

輸出結果:

Father Do!
argv[0] = -a
argv[1] = -b
argv[2] = -c

6.2 execlp函式和execvp函式

加了p之后,其中path可以不填絕對路徑,該函式會去PATH環境變數中尋找

int execlp(const char *path, const char *arg, ...);
  • path:用于保存想要執行的可執行檔案路徑
  • arg:執行可執行檔案所需要的引數,如果不需要引數可以填NULL

代碼測驗:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
  pid_t  fpid = fork();
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    execlp("ls","ls","-l",NULL);	//在環境變數中尋找ls并執行
  }
  else
  {
    printf("Father Do!\n");
    sleep(1);
  }

  sleep(1);
  return 0;
}

輸出結果:

Father Do!
總用量 144
-rwxrwxr-x. 1 gongruiyang gongruiyang 8561 12月 30 17:33 child_task
-rw-rw-r--. 1 gongruiyang gongruiyang  348 12月 30 17:34 child_task.c
............................................................

int execvp(const char *path, char *const argv[]);
  • path:用于保存想要執行的可執行檔案路徑
  • argv:引數串列

測驗代碼:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
  pid_t  fpid = fork();
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    char* child_argv[10];
    child_argv[0] = "ls";
    child_argv[1] = "-l";
    child_argv[2] = NULL;    
    execvp("ls",child_argv);
  }
  else
  {
    printf("Father Do!\n");
    sleep(1);
  }

  sleep(1);
  return 0;
}

結果輸出:

Father Do!
總用量 144
-rwxrwxr-x. 1 gongruiyang gongruiyang 8561 12月 30 17:33 child_task
-rw-rw-r--. 1 gongruiyang gongruiyang  348 12月 30 17:34 child_task.c

6.3 execle函式和execve函式

輔助程式:列印環境變數

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char* argv[],char* env[])
{
  printf("env-var:");                        
  for(int i = 0 ; env[i] != NULL; i++)
    printf("env[%d]:%s\n",i,env[i]);
  return 0;
}
int execle(const char *path, const char *arg,..., char * const envp[]);
  • path:用于保存想要執行的可執行檔案絕對路徑
  • arg:執行可執行檔案所需要的引數,如果不需要引數可以填NULL
  • …:不定引數,說明后面的引數可以是1個,也可以是多個
  • envp:自定義環境變數,可以填NULL
int execvpe(const char *path, char *const argv[], char *const envp[]);
  • path:用于保存想要執行的可執行檔案路徑
  • argv:引數串列
  • envp:自定義環境變數,可以填NULL

加了e之后,相比于之前,增加了一個環境變數引數,可以使用自定義環境變數

測驗代碼:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
  pid_t  fpid = fork();
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    char* child_env[10];
    child_env[0] = "MYVAL=1000";
    child_env[1] = "TMP=12";
    child_env[2] = NULL;
    execvpe("/home/gongruiyang/ClassLinunx/pidTest/envtest",NULL,child_env);
    //execvle("/home/gongruiyang/ClassLinunx/pidTest/envtest",NULL,child_env);
  }                                                                               
  else
  {
    printf("Father Do!\n");
    sleep(1);
  }

  sleep(1);
  return 0;
}

輸出結果:

Father Do!
env-var:env[0]:MYVAL=1000
env[1]:TMP=12

用于保存想要執行的可執行檔案絕對路徑

  • arg:執行可執行檔案所需要的引數,如果不需要引數可以填NULL
  • …:不定引數,說明后面的引數可以是1個,也可以是多個
  • envp:自定義環境變數,可以填NULL
int execvpe(const char *path, char *const argv[], char *const envp[]);
  • path:用于保存想要執行的可執行檔案路徑
  • argv:引數串列
  • envp:自定義環境變數,可以填NULL

加了e之后,相比于之前,增加了一個環境變數引數,可以使用自定義環境變數

測驗代碼:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
  pid_t  fpid = fork();
  if(fpid < 0)
    perror("fork");
  else if(fpid == 0)
  {
    char* child_env[10];
    child_env[0] = "MYVAL=1000";
    child_env[1] = "TMP=12";
    child_env[2] = NULL;
    execvpe("/home/gongruiyang/ClassLinunx/pidTest/envtest",NULL,child_env);
    //execvle("/home/gongruiyang/ClassLinunx/pidTest/envtest",NULL,child_env);
  }                                                                               
  else
  {
    printf("Father Do!\n");
    sleep(1);
  }

  sleep(1);
  return 0;
}

輸出結果:

Father Do!
env-var:env[0]:MYVAL=1000
env[1]:TMP=12

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/242821.html

標籤:其他

上一篇:shell腳本一鍵裝機(pxe配合kickstart無人值守)

下一篇:Linux用戶管理-相關命令及組態檔-超詳細-概念詳解-初學全

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more