Linux进程操作

进程概念

程序运行的时候就生成了进程,程序结束时进程就销毁.所有只有在程序正在运行时才会有新的一个进程生成

查询进程

显示所有进程:
ps:默认情况下,ps命令将显示当前用户的进程。
ps -e:显示所有进程。
ps -A:显示所有进程,与ps -e相同。
ps -a:显示终端上的所有进程,包括其他用户的进程。
​
按进程状态筛选:
ps aux:显示所有进程的详细状态。
ps -ef | grep <pattern>:使用grep命令过滤特定模式的进程。
​
按CPU或内存使用筛选:
ps aux --sort=-%cpu:按CPU使用率降序显示进程。
ps aux --sort=-%mem:按内存使用率降序显示进程。
​
显示特定用户的进程:
ps -u <username>:显示指定用户的进程。
​
显示命令行参数:
ps -o args=:显示完整的命令行参数。
​
显示线程信息:
ps -T:显示当前线程的层次结构。
​
其他选项:
ps -o pid,tty,cmd:自定义输出格式,只显示进程ID、终端类型和执行的命令行。
ps -p <pid>:显示指定进程ID的详细信息。
ps -f:全格式显示进程信息。
ps -l:长格式显示进程信息。
​
查看更详细的状态信息:
ps -o stat,tid,comm,lwp,pri,ni,stime,etime,cpu,rss,pcpu,pmem,args --sort=-tid --sort=-pcpu 这条命令提供了关于进程的详细状态信息,包括线程ID、命令名称、本地和远程信号时间、CPU和内存使用情况等。
​
组合使用:
可以通过组合不同的选项和参数来定制ps命令的输出,以满足特定的需求。例如,ps aux --sort=-%cpu | head 命令将按CPU使用率降序显示进程,并只显示前10个结果。
​
查看特定用户的进程:
如果你想查看特定用户的所有进程,可以使用以下命令: ps -u <用户名>,例如 ps -u root。如果你还想查看该用户所运行的所有子进程,可以使用 ps -e -o user,pid,cmd | grep <用户名> 命令。例如,要查找所有以"root"用户运行的进程,你可以输入 ps -e -o user,pid,cmd | grep root。这将列出所有由root用户启动的进程及其命令行。
​
查看特定进程:
如果你想查看特定进程的详细信息,可以使用 ps -p <pid> 命令,其中 <pid> 是你要查找的进程的ID号。例如,要查找PID为1234的进程,你可以输入 ps -p 1234。这会显示出该进程的详细信息,包括它的状态、CPU使用率、内存使用率等。
​
查看线程信息:
如果你想查看某个进程的线程信息,可以使用 ps -T -p <pid> 命令,其中 <pid> 是你要查找的进程的ID号。例如,要查找PID为1234的进程的所有线程,你可以输入 ps -T -p 1234。这会显示出该进程的所有线程及其相关信息。
​
查看系统负载:
如果你想查看系统的负载情况,可以使用 uptime 命令。这将显示出系统已经运行了多长时间、有多少用户在线、以及系统负载的平均值等信息。如果你还想查看更详细的系统负载信息,可以使用 top 或 htop 命令。这些命令可以实时地显示出系统的CPU使用率、内存使用率、运行的进程等信息,并且还可以进行交互操作。
ps  // 查询进程
ps -aux     // 查询所有进程
ps -aux|grep bash   // 筛选查询 查询带有bash的进程
top     //  动态显示 查询进程运行情况,及其能直观的看出硬件的消耗情况(进程运行占用硬件的比例)
pstree  // 命令来显示整个进程树
ps -eo pid,comm,cmd     // -e表示列出全部进程,-o pid,comm,cmd表示我们需要PID,COMMAND,CMD信息
ps -o pid,ppid,cmd  

PID 进程标识符

getpid()    // 获取自身程序的运行PID
getppid()   // 获取父进程的PID
#include <sys/types.h>  // 要使用就必须包含这两个头文件
#include <unistd.h>     // 要使用就必须包含这两个头文件
​
pid_t getpid(void);     // 返回值是一个非负整数
pid_t getppid(void);
 

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
​
​
int main()
{
    pid_t pid1,ppid1;   // 定义一个变量用于接收getpid函数返回进程号
    
    pid1 = getpid();    // 返回值时一个非负整数
    ppid1 = getppid();  // 获取父进程
    
    printf("The process number of this program is:%d\n",pid1);
    printf("The parent process number of this program process is:%d\n",ppid1);
    
    while(1);
    
    return 0;
}

父进程*子进程

在Linux中,除了进程0以外的所有进程都是由其他进程使用系统调用fork创建的,这里调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程,因而除了进程0以外的进程都只有一个父进程,但一个进程可以有多个子进程1。子进程继承了对应的父进程的大部分属性,如文件描述符2。父进程先执行fork()系统调用,这个调用的结果是系统中多出了一个跟父进程内容完全一样的进程,这个新进程被称为子进程3。
Unix/linux系统中的进程创建是这样的,理解起来有点绕。
​
父进程先执行fork()系统调用,这个调用的结果是系统中多出了一个跟父进程内容完全一样的进程,这个新进程被称为子进程,当然该进程的PCB中父进程指针是指向第一个进程的。
​
前后两个进程各自有自己的地址空间,形式上有点像把一个文件拷贝了一个副本。虽然资源也相互独立,但拷贝时父进程执行过程已生成的数据,子进程也拷了一份。说简单点像一个执行到半路的程序突然在系统中多出了一个孪生兄弟,什么都跟自己一样,但要管自己叫老爸。
​
当然这样的简单复制本身是没什么用处的。要让它发挥作用,还需要再执行 exec(B )系统调用,这个调用可以让当前进程转而执行另一个可执行代码(一个新的程序)。简单的说进程本来在执行A程序,一旦执行到这个调用,就转而开始执行B程序。
​
至此,父子两进程就变的不一样了,但不管它们各自执行的什么代码,其父子关系不会改变,在父进程中可以使用子进程的进程ID(在执行fork()时的返回值中得到)来中止子进程的执行。当然子进程也可以因为自己的执行程序结束而终止执行
​
父进程和子进程先后执行的问题,是这样的,在fork之后,是父进程先执行,然后一个时间片到达之后就是子进程再执行了。
​
每一个子进程都有一个父进程,当进程终止或者结束的时候,都会给父进程发送一个SIGCHLD信号,系统默认是父进程忽略这个信号,如果父进程希望被告知其子进程的这种状态改变,则应该捕获这个信号,捕捉函数一般是wait函数来取得子进程ID和子进程状态。
​
对于键盘上的Ctrl+按键的操作,一般是产生一个信号,然后进程捕捉这个信号。另外当然我们可以直接使用这些信号,通过kill命令,把信号发给相应的其他进程。
​
wait函数是父进程等待子进程结束,也就是说当子进程结束的时候会发送给父进程一个信号SIGCHID,这时候父进程通过wait函数接收到这个信号,这时候父进程就知道子进程结束了。这个正好用在shell解析器的编写里面,shell解析器作为父进程,而命令行命令作为子进程,当子进程结束的时候就会告诉父进程,这时候父进程就可以提示输入下一个命令了。
​
一个进程只能为他自己或者他的子进程设置进程组ID,在他的子进程调用了exec函数之后,就不能再改变该子进程的进程组ID了。

进程的创建

fork()函数

/*
#include <sys/types.h>  // 包含头文件
#include <unistd.h>     // 包含头文件
​
pid_t fork(void);   // 原型
​
***fork函数调用成功,返回两次
​
返回值为0,代表当前进程是子进程
​
返回值非负数,代表当前进程为父进程       // 返回的就是子进程的PID
​
调用失败,只返回-1
*/​

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
​
​
int main()
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    
    pid1 = fork();      // 生成一个新的进程(复制?)        // 生成子进程
    
    if(pid1 > 0){   // 返回值大于0时父进程
        printf("fork return:%d\t",pid1);
        printf("This is the parent process.Pid:%d\n",getpid());
    }else if(pid1 == 0){    // 等于0时是子进程
        printf("fork return:%d\t",pid1);
        printf("This is a child process.Pid:%d\n",getpid());
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
​
    return 0;
}

父进程与子进程的数据关系

fork()函数拷贝

各进程修改数值进程之间互不影响

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
​
​
int main()
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    int age = 27;
    
    
    printf("age:%d\n",age);
    
    // fork()函数完全拷贝之后的代码生成一个新的子进程
    pid1 = fork();      // 生成一个新的进程(复制?)        // 生成子进程
    
    if(pid1 > 0){   // 返回值大于0时父进程
        printf("fork return:%d\t",pid1);
        printf("This is the parent process.Pid:%d\n",getpid());
        age++;      // 父进程自增    不会影响已生成的子进程
    }else if(pid1 == 0){    // 等于0时是子进程
        printf("fork return:%d\t",pid1);
        printf("This is a child process.Pid:%d\n",getpid());
        age = age - 16;     // 子进程的数据修改也不会影响其他进程
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
        
    }
    
    printf("age:%d\n",age);
    return 0;
}

目的

一对多的情况下父进程无法离开,子进程就是服务于请求者的...
(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的--父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
(2)一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec
​
****子进程就是服务于请求者的...
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
​
​
int main()
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    int age;
    
    
    // fork()函数完全拷贝之后的代码生成一个新的子进程
    
​
    while(1){
        printf("Please enter your age:\n");
        scanf("%d",&age);
        if(age > 18){   // 当年龄大于18时,生成一个新的字进程
            pid1 = fork();      // 生成一个新的进程(复制?)        // 生成子进程
            if(pid1 == 0){  // 等于0时是子进程
                while(1){
                    printf("fork return:%d\t",pid1);
                    printf("This is a child process.Pid:%d\n",getpid());
                    sleep(3);   // 延时3秒
                }
            }
        }else{
            printf("You are underage.\n");
        }
    }
    return 0;
}

vfork()函数

#include <sys/types.h>
#include <unistd.h>
​
pid_t vfork(void);
​
// **与fork()使用方法一样

区别

一.vfork直接时用父进程的进程空间(fork是复制),不拷贝
​
二.vfork先保证子进程运行,当子进程调用exit退出后,父进程再执行
​
**子进程直接可以影响父进程的数据?  // 必须先退出子进程,数据才能正常的运行

fork是父线程,子线程抢占CPU运行

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
​
int main()  //  fork是父线程,子线程抢占CPU运行
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    int count = 0;
​
    pid1 = fork();      // 生成一个新的进程直接用父进程的内存空间
    
    if(pid1 > 0){   // 返回值大于0时父进程
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is the parent process.Pid:%d\n",getpid());
            
            sleep(1);
        }
    }else if(pid1 == 0){    // 等于0时是子进程
        while(1){
            
            printf("fork return:%d\t",pid1);
            printf("This is a child process.Pid:%d\n",getpid());
            sleep(1);
        }
        
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
    
    return 0;
}

vfork是先运行子线程,正常exit退出子线程后,再运行父线程

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
​
int main()
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    int count = 0;
​
    pid1 = vfork();     // 生成一个新的进程直接用父进程的内存空间
    
    if(pid1 > 0){   // 返回值大于0时父进程
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is the parent process.Pid:%d\n",getpid());
            printf("count:%d\n",count);
            sleep(3);
        }
    }else if(pid1 == 0){    // 等于0时是子进程
        while(1){
            
            printf("fork return:%d\t",pid1);
            printf("This is a child process.Pid:%d\n",getpid());
            if(count == 2){
                exit(0);    // 必须正常退出子进程,才不会破坏数据(变量)
            }
            count++;
            sleep(1);
        }
        
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
    
    return 0;
}

进程退出

不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打
开描述符,释放它所使用的存储器等。对上述任意一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit、_exit和_Exit),实现这一点的方法是,将其退出状态(exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination sratus)。
​
​
在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态

正常退出

1.main函数调用return

2.进程调用exit(),标准C库
#include <stdlib.h>
​
void exit(int status);
​
status:传入一个状态码即可/一般传入0

3.进程调用 _exit()或者 _Exit(),属于系统调用
#include <unistd.h>
​
void _exit(int status);
​
status:传入一个状态码即可/一般传入0
#include <stdlib.h>
​
void _Exit(int status);
​
status:传入一个状态码即可/一般传入0

重点_线程,需要补充
4.其他补充

(1).进程最后一个线程返回 // 线程是重点

(2).最后一个线程调用pthread_exit

异常退出

1.调用abort

2.当进程收到某些信号时,如CTRL+C

3.最后一个线程对取消(cancellation)请求做出响应

等待子进程退出

为什么要等待子进程退出

子进程退出,父进程不收集子进程退出状态信息**僵死进程(僵尸)

vfork()

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
​
int main()
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    int count = 0;
​
    pid1 = vfork();     // 生成一个新的进程直接用父进程的内存空间
    
    if(pid1 > 0){   // 返回值大于0时父进程
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is the parent process.Pid:%d\n",getpid());
            printf("count:%d\n",count);
            sleep(3);
        }
    }else if(pid1 == 0){    // 等于0时是子进程
        while(1){
            
            printf("fork return:%d\t",pid1);
            printf("This is a child process.Pid:%d\n",getpid());
            if(count == 2){
                exit(0);    // 必须正常退出子进程,才不会破坏数据(变量)
            }
            count++;
            sleep(1);
        }
        
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
    
    return 0;
}

fork()

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
​
int main()
{
    pid_t pid1;     // 定义一个变量用于接收getpid函数返回进程号
    int count = 0;
​
    pid1 = fork();      // 生成一个新的进程直接用父进程的内存空间
    
    if(pid1 > 0){   // 返回值大于0时父进程
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is the parent process.Pid:%d\n",getpid());
            printf("count:%d\n",count);
            sleep(3);
        }
    }else if(pid1 == 0){    // 等于0时是子进程
        while(1){
            
            printf("fork return:%d\t",pid1);
            printf("This is a child process.Pid:%d\n",getpid());
            if(count == 5){
                exit(0);    // 必须正常退出子进程,才不会破坏数据(变量)
            }
            count++;
            sleep(1);
        }
        
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
    
    return 0;
}

父进程收集子进程退出状态

父进程收集子进程退出状态信息,父进程等待子进程退出后再运行

wait/waitpid函数原型

#include <sys/types.h>
#include <sys/wait.h>
​
pid_t wait(int *wstatus);   // 常用
​
pid_t waitpid(pid_t pid, int *wstatus, int options);    // 常用
​
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
​
wstatus:整型指针    返回整型指针
宏                   说明
WIFEXITED(status)   如果子进程正常结束,它就返回真;否则返回假。
WEXITSTATUS(status) 如果WIFEXITED(status)为真,则可以用该宏取得子进程exit()返回的结束代码。
​
WIFSIGNALED(status) 如果子进程因为一个未捕获的信号而终止,它就返回真;否则返回假。
WTERMSIG(status)    如果WIFSIGNALED(status)为真,则可以用该宏获得导致子进程终止的信号代码。
WCOREDUMP(status)   获取子进程异常终止所产生的内核文件
​
WIFSTOPPED(status)  如果当前子进程被暂停了,则返回真;否则返回假。
WSTOPSIG(status)    如果WIFSTOPPED(status)为真,则可以使用该宏获得导致子进程暂停的信号代码。
​
WIFCONTINUED(status) 如果状态是由已经被continued子进程返回,返回true。
​
参数pid为欲等待的子进程识别码,其具体含义如下:
​
pid<-1  等待进程组号为pid绝对值的任何子进程。
pid=-1  等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数。
pid=0   等待进程组号与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程。
pid>0   等待进程号为pid的子进程。
​
options     // 一般设置WNOHANG
​
参数options提供了一些另外的选项来控制waitpid()函数的行为。如果不想使用这些选项,则可以把这个参数设为0。
​
主要使用的有以下两个选项:
    
参数  说明
WNOHANG 如果pid指定的子进程没有结束,则waitpid()函数立即返回0,而不是阻塞在这个函数上等待;如果结束了,则返回该子进程的进程号。
WUNTRACED   如果子进程进入暂停状态,则马上返回。
#include <stdio.h>
#include <sys/types.h>  // wait头文件
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>   // wait头文件
int main()
{
    pid_t pid1;     
    int count = 0;
​
    pid1 = fork();      
    
    if(pid1 > 0){   // 返回值大于0时父进程
        wait(NULL);     // 父进程收集子进程退出状态信息,父进程等待子进程退出后再运行
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is the parent process.Pid:%d\n",getpid());
            printf("count:%d\n",count);
            sleep(3);
        }
    }else if(pid1 == 0){    // 等于0时是子进程
        while(1){
            
            printf("fork return:%d\t",pid1);
            printf("This is a child process.Pid:%d\n",getpid());
            if(count == 5){
                exit(0);    // 必须正常退出子进程,才不会破坏数据(变量)
            }
            count++;
            sleep(1);
        }
        
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
    
    return 0;
}

解析退出状态码

WEXITSTATUS(status)这个是解析子进程退出状态码
#include <stdio.h>
#include <sys/types.h>  // wait头文件
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>   // wait头文件
int main()
{
    pid_t pid1;     
    int count = 0;
    int status = -7;
​
    pid1 = fork();      
    
    if(pid1 > 0){   // 返回值大于0时父进程
        wait(&status);      // 父进程收集子进程退出状态信息,父进程等待子进程退出后再运行
        printf("PID:%d Exit status code of the child process:%d\n",pid1,WEXITSTATUS(status));   // WEXITSTATUS(status)这个是解析子进程退出状态码
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is the parent process.Pid:%d\n",getpid());
            printf("count:%d\n",count);
            sleep(3);
        }
    }else if(pid1 == 0){    // 等于0时是子进程
        while(1){
            printf("fork return:%d\t",pid1);
            printf("This is a child process.Pid:%d\n",getpid());
            if(count == 5){
                exit(13);   // 必须正常退出子进程,才不会破坏数据(变量)
            }
            count++;
            sleep(1);
        }
        
    }else{  // 调用失败,返回-1
        printf("fork return:%d\t",pid1);
    }
    
    return 0;
}
waitpid函数不阻塞方式实例
#include <stdio.h>
#include <sys/types.h>	// wait头文件
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>	// wait头文件
int main()
{
	pid_t pid1;		
	int count = 0;
	int status = 999;

	pid1 = fork();		
	
	if(pid1 > 0){	// 返回值大于0时父进程
//		wait(&status);		// 父进程收集子进程退出状态信息,父进程等待子进程退出后再运行
		waitpid(pid1,&status,WNOHANG);	// 不阻塞方式运行 子进程也会变成僵死进程
		printf("PID:%d Exit status code of the child process:%d\n",pid1,WEXITSTATUS(status));	// WEXITSTATUS(status)这个是解析子进程退出状态码 使用这个就没有什么意义
		while(1){
			printf("fork return:%d\t",pid1);
			printf("This is the parent process.Pid:%d\n",getpid());
			printf("count:%d\n",count);
			sleep(1);
		}
	}else if(pid1 == 0){	// 等于0时是子进程
		while(1){
			printf("fork return:%d\t",pid1);
			printf("This is a child process.Pid:%d\n",getpid());
			if(count == 5){
				exit(13);	// 必须正常退出子进程,才不会破坏数据(变量)
			}
			count++;
			sleep(1);
		}
		
	}else{	// 调用失败,返回-1
		printf("fork return:%d\t",pid1);
	}
	
	return 0;
}

孤儿进程

概念:父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程

Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程

#include <stdio.h>
#include <sys/types.h>  // wait头文件
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>   // wait头文件
int main()
{
        pid_t pid1;
        int count = 0;
        int status = 999;
​
        pid1 = fork();
​
        if(pid1 > 0){   // 返回值大于0时父进程
//              wait(&status);          // 父进程收集子进程退出状态信息,父进程等待子进程退出后再运行
                waitpid(pid1,&status,WNOHANG);  // 不阻塞方式运行 子进程也会变成僵死进程
                printf("PID:%d Exit status code of the child process:%d\n",pid1,WEXITSTATUS(status));   // WEXITSTATUS(status)这个是解析子进程退出状态码 使用这个就没有什么意义
        //      while(1){
                        printf("fork return:%d\t",pid1);
                        printf("This is the parent process.Pid:%d\n",getpid());
                        printf("count:%d\n",count);
        //      }
        }else if(pid1 == 0){    // 等于0时是子进程
                while(1){
                        printf("fork return:%d\t",pid1);
                        printf("This is a child process.Pid:%d,father PID:%d.\n",getpid(),getppid());   //父进程退出
后,init进程接收子进程继续运行       init的PID是1
                        if(count == 5){
                                exit(13);       // 必须正常退出子进程,才不会破坏数据(变量)
                        }
                        count++;
                        sleep(1);
                }
​
        }else{  // 调用失败,返回-1
                printf("fork return:%d\t",pid1);
        }
​
        return 0;
}

exex族函数

函数原型

#include <unistd.h>
​
extern char **environ;
​
int execl(const char *pathname, const char *arg, .../* (char  *) NULL */);
​
int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
​
int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);
​
int execv(const char *pathname, char *const argv[]);
​
int execvp(const char *file, char *const argv[]);
​
int execvpe(const char *file, char *const argv[],char *const envp[]);
int execve(const char *pathname, char *const argv[],char *const envp[]);    // ???
​
参数说明
l   参数以列表list   传递
v   参数以数组       传递
p   不带p需要传入文件的路径    带p是环境变量总有执行文件的路径,也可带路径直接传入文件/命令
e   环境列表数组???   传递      // 应该不常用

execl参数说明

int execl (const char *pathname, const char *arg, ...,NULL) 
/*
参数是列表 必须NULL结尾
执行成功无返回值,失败返回-1
调用失败perror("execl");    使用这个打印错误信息
*/
参数说明
pathname        可执行文件路径及其文件/命令
arg             执行文件的参数 // 第一个参数最好用要执行命令/文件     最后一个参数NULL
​
execl("执行(文件/命令)路径","参数1","参数2",...,NULL);
    
​
int ret = execl("/bin/ls","ls",NULL);   // 参数1是必须的,最好写执行命令/文件,可以是其他字符"~"/"s"
int ret = execl("/bin/ls","ls","-a","-l",NULL);     // 等同于ls -a -l
​
int ret = execl("/usr/bin/gcc","gcc","11dome.c","-o","sss",NULL);   // 参数1不能是其他字符必须是gcc
​
int ret = execl("./sss","sss",NULL);    // 执行目录下syh程序

execl实例

#include <stdio.h>
#include <unistd.h>
int main()
{
        int ret = execl("/bin/ls","ls",NULL);   // 等同于执行ls
        if(ret == -1){
                perror("execl");    // 打印出错信息
                return -1;
        }
        return 0;
}
#include <stdio.h>
#include <unistd.h>
int main()
{
        int ret = execl("/bin/ls","ls","-a","-l",NULL); // 等同于执行 ls -a -l
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}
#include <stdio.h>
#include <unistd.h>
int main()
{
        int ret = execl("/usr/bin/gcc", "gcc", "./11dome.c","-o","sss",NULL);   // 使用GCC编译目录下11dome.c 并生成>别名sss的文件
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}
#include <stdio.h>
#include <unistd.h>
int main()
{
        int ret = execl("./sss","sss",NULL);    // 执行目录下sss文件
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}
 

execlp参数说明

int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
​
file    直接写执行文件或者命令     // 前提条件是环境变量PATH下有这个文件/命令的路径
arg     参数  // 第一个参数最好用要执行命令/文件     最后一个参数NULL
​
execlp("执行文件/命令","参数1","参数2",...,NULL);
    
​
int ret = execlp("ls","ls",NULL);   // 参数1是必须的,最好写执行命令/文件,可以是其他字符"~"/"s"
int ret = execlp("ls","ls","-a","-l",NULL);     // 等同于ls -a -l
​
int ret = execlp("gcc","gcc","11dome.c","-o","sss",NULL);   // 参数1不能是其他字符必须是gcc
int ret = execlp("/usr/bin/gcc", "gcc", "./11dome.c","-o","sss",NULL);//也可以带路径
​
/*****/
int ret = execlp("sss","sss",NULL); // 这个无法执行,没有配环境变量
int ret = execl("./sss","sss",NULL);    // 执行目录下syh程序
int ret = execlp("./sss","sss",NULL);   // 执行目录下syh程序

execlp实例

#include <stdio.h>
#include <unistd.h>
int main()
{
        int ret = execlp("gcc", "gcc", "./11dome.c","-o","sss",NULL);   // 使用GCC编译目录下11dome.c 并生成别名sss的文件
//      int ret = execlp("/usr/bin/gcc", "gcc", "./11dome.c","-o","sss",NULL);//也可以带路径
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}

execv参数说明

int execv(const char *pathname, char *const argv[]);
​
参数说明
pathname    可执行文件路径及其文件/命令
argv[]      数组参数    // 第一个参数最好用要执行命令/文件     最后一个参数NULL
    
char *str[] = {"ls","-l","-a",NULL}; 

execvp参数说明

int execvp(const char *file, char *const argv[]);
file    直接写执行文件或者命令     // 前提条件是环境变量PATH下有这个文件/命令的路径
argv[]      数组参数    // 第一个参数最好用要执行命令/文件     最后一个参数NULL
    
char *str[] = {"ls","-l","-a",NULL}; 

execv***execvp实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        char *str[] = {"ls","-l","-a",NULL};    // 定义参数数组
//      int ret = execv("/usr/bin/ls",str);     // 执行ls -l -a命令
        int ret = execvp("ls",str);             // 执行ls -l -a
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}
 

***输出环境变量

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
​
int main(int num, char *parameter[])    // num参数个数  parameter参数数组
{
        printf("Main parameter list:\n");
​
        int i;
        for(i = 0;i < num; i++){
                printf("%*s\n",4,parameter[i]);
        }
        printf("**************************\n");
        printf("环境列表:\n");
        extern const char **environ;
        const char **env = environ;
        for(;*env != NULL; env++){
                printf("%*s\n",4,*env);
        }
        return 0;
}

execle参数说明

int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);
​

execle实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        char *envp[] = {"name = nianxing_su","age = 27","PATH = /usr/bin:/bin/",NULL};  // 定义参数数组
        int ret = execle("test","test","s","y","h",NULL,envp);  // 执行ls -l -a命令
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}

execve参数说明

int execve(const char *pathname, char *const argv[],char *const envp[]);

execve实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        char *str[] = {"test","s","y","h",NULL};
        char *envp[] = {"name = nianxing_su","age = 27","PATH = /usr/bin:/bin/",NULL};  // 定义参数数组
      //  int ret = execve("./test",str,envp);    
        int ret = execve("test",str,envp); 
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}

execvpe参数说明

int execvpe(const char *file, char *const argv[],char *const envp[]);

execvpe实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        char *str[] = {"test","s","y","h",NULL};
        char *envp[] = {"name = nianxing_su","age = 27","PATH = /usr/bin:/bin/",NULL};  // 定义参数数组
        int ret = execvpe("./test",str,envp);
        if(ret == -1){
                perror("execl");        // 打印出错信息
                return -1;
        }
        return 0;
}

fork/vfork与exec配合使用

exec族函数不会单独使用,一般配合fork/vfork函数使用
单独使用会替换掉主函数main函数
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
​
int main()
{
//      pid_t id = vfork();
        pid_t id = fork();
​
        if(id == -1){
                perror("fork\n");
                return -1;
        }
​
        if(id == 0){    // 创建子进程不会拷贝地址空间
                char *str[] = {"test","s","y","h",NULL};
                char *envp[] = {"name = nianxing_su","age = 27","PATH = /usr/bin:/bin/",NULL};  // 定义参数数组
                int ret = execve("./test",str,envp);    // 启动一个进程替换当前子进程    调用其他程序test
                if(ret == -1){ 
                        perror("exec\n");       // 打印出错信息
                        exit(-1);
                }
                printf("Will not execute.\n");
        }
        id = wait(NULL);        // 等待子进程结束,父进程回收
        printf("%d子进程结束了.\n",id);
        return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
        pid_t pid;
        int data = -3;
        while(1){       // 父进程
                printf("please input a data:\n");
                scanf("%d",&data);
                if(data == 1){  // 当用户输入1生成一个子进程
                //      int fdsrc;
                        pid = fork();
                        if(pid >0){
                                wait(NULL);     // 父进程等待
                                printf("pid:%d.Completed the task.\n",pid);
                        }else if(pid == 0){     // 子进程执行程序
                                int fdsrc;
                                char* readbuf = NULL;
                                fdsrc = open("./config.txt",O_RDWR);    // 打开文件
                                int size = lseek(fdsrc,0,SEEK_END);     // 计算字符串长度
                                lseek(fdsrc,0,SEEK_SET);        // 移动光标到头部
                                readbuf = (char*)malloc(sizeof(char)*size + 8); // 开辟空间
                                int n_read = read(fdsrc,readbuf,size);          // 读数据
                                char *p = strstr(readbuf,"LENG = ");    // 查找字符串,返回一个指针
                                if(p == NULL){
                                        printf("not found.\n");
                                        exit(-1);
                                }
                                p = p + strlen("LENG = ");      // 指针偏移
                                *p = 'S';       // 改数据
                                lseek(fdsrc,0,SEEK_SET);        // 光标移动到头部
                                int n_write = write(fdsrc,readbuf,strlen(readbuf));     // 写入修改后数据
                                printf("pid:%d.Completed the task.\n",pid);
​
                                close(fdsrc);   // 关闭文件,释放资源
                                exit(1);        // 子进程退出
                        }else{
​
                        }
                }else{
                        printf("wait do nothing.\n");
                }
        }
        return 0;
}

封装子进程任务 修改配置文件

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
​
int main()
{
        int fdsrc;
        char* readbuf = NULL;
        fdsrc = open("./config.txt",O_RDWR);    // 打开文件
        int size = lseek(fdsrc,0,SEEK_END);     // 计算字符串长度
        lseek(fdsrc,0,SEEK_SET);        // 移动光标到头部
        readbuf = (char*)malloc(sizeof(char)*size + 8); // 开辟空间
        int n_read = read(fdsrc,readbuf,size);          // 读数据
        char *p = strstr(readbuf,"LENG = ");    // 查找字符串,返回一个指针
        if(p == NULL){
                printf("not found.\n");
                exit(-1);
        }
        p = p + strlen("LENG = ");      // 指针偏移
        *p = 'S';       // 改数据
        lseek(fdsrc,0,SEEK_SET);        // 光标移动到头部
        int n_write = write(fdsrc,readbuf,strlen(readbuf));     // 写入修改后数据
​
        close(fdsrc);   // 关闭文件,释放资源
        return 0;
}

父进程使用exec函数调用子进程任务

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
        pid_t pid;
        int data = -3;
        while(1){       // 父进程
                printf("please input a data:\n");
                scanf("%d",&data);
                if(data == 1){  // 当用户输入1生成一个子进程
                //      int fdsrc;
                        pid = fork();
                        if(pid >0){
                                wait(NULL);     // 父进程等待
                                printf("pid:%d.Completed the task.\n",pid);
                        }else if(pid == 0){     // 子进程执行程序
                                execl("./syh","syh","config.txt",NULL);
                                exit(1);        // 子进程退出
                        }else{
​
                        }
                }else{
                        printf("wait do nothing.\n");
                }
        }
        return 0;
}

grep exit * // 查找文件内容的

system函数

函数原型

就是封装的execl函数

system函数执行的程序,可传多个参数,前提条件是执行程序可接收多个参数,否则会忽略程名后的参数

#include <stdlib.h>
​
int system(const char *command);
​
​
/*
system("./syh");     //传入一个参数给main函数程序名称argv[0]
system("./syh config.txt");       // 传入两个参数argv[0]:程序名 argv[1]:参数2
// 前提条件是执行程序可接受其他参数传参,要不忽略程名后的参数
*/
​
/*
system函数返回值
成功返回进程状态值
当sh不能执行时,返回127
失败返回-1
*/
int system(const char * cmdstring)
{
  pid_t pid;
  int status;
​
  if(cmdstring == NULL){
      
      return (1);
  }
​
​
  if((pid = fork())<0){
​
        status = -1;
  }
  else if(pid == 0){
    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
    -exit(127); //子进程正常执行则不会执行此语句
    }
  else{
        while(waitpid(pid, &status, 0) < 0){
          if(errno != EINTER){
            status = -1;
            break;
          }
        }
    }
    return status;
}

system实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
        pid_t pid;
        int data = -3;
        while(1){       // 父进程
                printf("please input a data:\n");
                scanf("%d",&data);
                if(data == 1){  // 当用户输入1生成一个子进程
                //      int fdsrc;
                        pid = fork();
                        if(pid >0){
                                wait(NULL);     // 父进程等待
                                printf("pid:%d.Completed the task.\n",pid);
                        }else if(pid == 0){     // 子进程执行程序
                //              execl("./syh","syh","config.txt",NULL);
                //              system("./syh");     //传入一个参数给main函数程序名称argv[0]
                                system("./syh config.txt");       // 传入两个参数argv[0]:程序名 argv[1]:参数2
                                                                  // 前提条件是执行程序可接受其他参数传参,要不忽略程名后的参数
序名后的参数,而且可以传多个参数,不止两个
                        }else{
​
                        }
                }else{
                        printf("wait do nothing.\n");
                }
        }
        return 0;
}

main函数原型

c
int main(int argc, char *argv[]);
或者(对于C++,尽管它们通常是兼容的):
​
cpp     
int main(int argc, char **argv);
其中:
​
int argc:参数数量(Argument Count)。这是一个整数,表示传递给main函数的命令行参数的数量。至少为1,因为argv[0]通常是程序的名称。
char *argv[] 或 char **argv:参数向量(Argument Vector)。这是一个指向字符指针的数组,其中每个指针指向一个字符串。这些字符串是程序从命令行接收的参数。argv[argc]通常是一个NULL指针,表示参数列表的结束。

popen函数

函数原型

比system在应用中的好处:

可以获取运行的输出结果

#include <stdio.h>
​
FILE *popen(const char *command, const char *type);
​
int pclose(FILE *stream);
​
执行结果存入一个文件中?
    
/*
command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令。
​
  mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。
​
返回值:
​
  如果调用成功,则返回一个读或者打开文件的指针,如果失败,返回NULL,具体错误要根据errno判断
​
  int pclose (FILE* stream)
​
  参数说明:
​
  stream:popen返回的文件指针
​
  返回值:
​
  如果调用失败,返回 -1
  */

popen实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
        char ret[1024] = {0};   // 读取popen执行程序返回的数据
        FILE *fp;       // 定义一个变量接收popen返回值
        fp = popen("ps","r");   // 执行ps命令
        int nread = fread(ret,1,1024,fp);       //从fp流读取1个字节读取1024次给ret
        printf("read ret %d byte. ret:%s\n",nread,ret);
        return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
        char ret[1024] = {0};   // 读取popen执行程序返回的数据
        FILE *fp;       // 定义一个变量接收popen返回值
        fp = popen("ps","r");   // 执行ps命令
        int nread = fread(ret,1,1024,fp);       //从fp流读取1个字节读取1024次给ret
        printf("read ret %d byte. ret:%s\n",nread,ret);
        pclose(fp);     // 关闭
        // 这下面还可以打开新的文件,把读取到的数据写入新的文件中***日志文件
        FILE *fp1;
        fp1 = fopen("./sss.txt","w+");  // 打开目录下sss.txt文件,如果文件不存在则新建
        fwrite(ret,strlen(ret)*sizeof(char),1,fp1);     // 写入数据
        fclose(fp1);    // 关闭文件
        printf("************cut-off rule***********\n");
        system("cat sss.txt");  // 用system执行cat命令
        return 0;
}

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值