【Linux系统编程(进程编程)】exec族函数、system、popen

一、exec族函数

为什么要使用exec族函数,有什么作用

linux进程—exec族函数(execl, execlp, execle, execv, execvp, execvpe)_云英的博客-CSDN博客
image.png
从第二个角度来讲

答:执行一个不同的程序。

exec族函数函数的作用:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。
当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

#include <unistd.h>
extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

返回值:
exec函数族的执行成功后,不会返回。调用失败时,会设置errno并返回-1,然后从源程序的调用点接着往下执行。
参数说明:

  • path:可执行文件的路径名字
  • arg:可执行程序所带的参数,第一个参数为可执行文件的名字,没有带路径且必须以NULL结束
  • file:如果file中包含/,则将其视为路径名,否则就将其按环境变量处理,在它所指定的目录中搜寻可执行文件。

execl函数

image.png
execl函数就是,第一个参数是程序的path,最后一个参数是NULL。第二个参数为程序名,其余中间可以放不同数量的参数作为想执行程序参数。

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

//int execl(const char *path, const char *arg, ... (char  *) NULL );

int main()
{
	printf("before execl");

	if(execl("./echoarg", "echoarg", "abc", "n", NULL) == -1)
	{
		printf("execl failed!\n");
	}
	
	printf("after execl");
	return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(int argc, char *argv[])
{

	int i = 0;
	for (i=0; i<argc; i++){
		printf("argv[%d]:%s\n", i, argv[i]);
	}
	return 0;
}

调用成功不会执行之下的其它代码,替换原来进程,pid号不变。

使用execl调用ls指令

查看ls指令在那个path中:
image.png

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

//int execl(const char *path, const char *arg, ... (char  *) NULL );

int main()
{
	printf("before execl\n");

	if(execl("/bin/ls", "ls", "-l", NULL) == -1)
	{
		printf("execl failed!\n");
		perror("why");
	}
	
	printf("after execl\n");
	return 0;
}

exec好处:
需求让你获取系统时间。直接调用系统指令,便捷可节省时间。

exec获取时间

image.png

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

//int execl(const char *path, const char *arg, ... (char  *) NULL );

int main()
{
	printf("this pro get sys time\n");

	if(execl("/bin/date", "date", NULL) == -1)
	{
		printf("execl failed!\n");
		perror("why");
	}
	
	printf("after execl\n");
	return 0;
}

找不到系统路径路径?execlp来帮忙

execlp多于execl的p就是path的意思,多的p能从环境变量中查找指令
使用execlimage.png

int main()
{
	if(execl("ps", "ps", NULL) == -1)
	{
		printf("execl failed!\n");
		perror("why");
	}
	
	return 0;
}

使用execlpimage.png

int main()
{
	if(execlp("ps", "ps", NULL) == -1)
	{
		printf("execl failed!\n");
		perror("why");
	}
	
	return 0;
}

扩展环境变量

image.png

同execlp结果一样的execvp仅仅是使用不一样而已

仅仅是把参数放入一个指针数组里
image.png

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

//int execl(const char *path, const char *arg, ... (char  *) NULL );

int main()
{
	char * argv[] = {"ps", NULL};
	if(execvp("ps", argv) == -1)
	{
		printf("execl failed!\n");
		perror("why");
	}
	
	return 0;
}

execv

execv第一个参数需要加绝对路径

二、exec族函数配合fork

需求:实现功能,当父进程检测到输入为1时,创建子进程把配置文件的字段值修改掉。
image.png

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


int main(int argc, char* argv[])
{
        pid_t pid;
        int data = 10;

        while(1){
                printf("please input a data:");
                scanf("%d", &data);
                if(data == 1){
                        pid = fork();

                        if(pid > 0)
                        {
                        	wait(NULL);
                        }
                        else if(pid == 0)
                        {
                                int fd;
                                char *readBuf=NULL;

                                fd = open(argv[1], O_RDWR);

        /*
        int num = lseek(fd, 0, SEEK_CUR);
        printf("how many %d\n", num);//0
        */

                                int size = lseek(fd, 0, SEEK_END);
                                lseek(fd, 0, SEEK_SET);

                                readBuf = (char *)malloc(size + 8);
                                int n_read = read(fd, readBuf, size);

                                char *p = strstr(readBuf, "LENG=");//修改5为其他
                                if(p==NULL){
                                    printf("not found]\n");
                                    exit(-1);
                                }
                                p = p + strlen("LENG=");
                                *p = '9';

                                lseek(fd, 0, SEEK_SET);
                                int n_write = write(fd, readBuf, strlen(readBuf));
                                close(fd);
                                exit(0);
                        }

                }else{
                        printf("wait, do nothing\n");
                }
        }


        return 0;
}


利用exec函数修改配置文件
image.png

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

int main(int argc, char* argv[])
{
	pid_t pid;
	int data = 10;

	while(1){
		printf("please input a data:");
		scanf("%d", &data);
		if(data == 1){
			pid = fork();
			
			if(pid > 0)
			{
				wait(NULL);
			}
			else if(pid == 0)
			{
				execl("./changeData", "changeData", "test.config", NULL);
				exit(0);//调用exec成功则无用	
			}

		}else{
			printf("wait, do nothing\n");
		}
	}

	
	return 0;
}

image.png

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

int main(int argc, char **argv)
{
	if(argc != 2){
		printf("param error\n");
		exit(-1);
	}


	int fd;
	char *readBuf=NULL;
	
	fd = open(argv[1], O_RDWR);
	
	/*
	int num = lseek(fd, 0, SEEK_CUR);
	printf("how many %d\n", num);//0
	*/

	int size = lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET);
	
	
	readBuf = (char *)malloc(size + 8);
	int n_read = read(fd, readBuf, size);

	// modify
	char *p = strstr(readBuf, "LENG=");//修改5为其他
	p = p + strlen("LENG=");
	*p = '9';

	
	lseek(fd, 0, SEEK_SET);
	int n_write = write(fd, readBuf, strlen(readBuf));
	
	close(fd);
	
	return 0;
}

三、system函数

linux system函数详解 - 南哥的天下 - 博客园
image.png
system()函数的返回值如下: 成功,则返回进程的状态值; 当sh不能执行时,返回127; 失败返回-1;

执行指令的方式:(两种)
image.png
返回值
如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值。如果 system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。

调用system代码:
使用system也能修改配置文件的值
image.png

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

int main(int argc, char* argv[])
{
        pid_t pid;

        int data = 10;

        while(1){
                printf("please input a data:");
                scanf("%d", &data);
                if(data == 1){
                        pid = fork();

                        if(pid > 0)
                        {
                                wait(NULL);
                        }
                        else if(pid == 0)
                        {
                                //execl("./changeData", "changeData", "test.config", NULL);
                                system("changeData test.config");
                        }

                }else{
                        printf("wait, do nothing\n");
                }
        }


        return 0;
}

system最后还是会返回到原始程序中执行原来的代码“after system”
image.png


system返回值比较奇怪吗???
为什么时32512???
image.png
原因在这:
system函数返回值探究_天一涯的博客-CSDN博客
system函数返回值探究-Bean_lee-ChinaUnix博客

四、popen函数

函数原型
image.png

参考博文:
linux下popen的使用心得_linux popen结束了能读到吗_libinbin_1014的博客-CSDN博客

比system在应用中的好处:可以获取运行的输出结果

前言
想要获取system输出,通过网络发送出去(其它用途),应该如何实现?
直接system不能获取输出
image.png

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

//int execl(const char *path, const char *arg, ... (char  *) NULL );

int main()
{
	
	system("ps");
	
	return 0;
}

使用popen,获取程序运行的输出
image.png

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

//int execl(const char *path, const char *arg, ... (char  *) NULL );

int main()
{
        char ret[1024] = {0};//开辟一兆内存空间
        FILE* fp = NULL;

        fp = popen("ps", "r");
        int n_read = fread(ret, 1, 1024, fp);


        printf("read %d bytes, ret=\n%s\n", n_read, ret);
        return 0;
}

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 封面 -12 封底 -11 扉页 -10 版权 -9 版权声明 -8 前言 -7 目录 -3 第一部分 简介 1 第1章 简介 2 1.1 概述 2 1.2 进程、线程与信息共享 3 1.3 IPC对象的持续性 4 1.4 名字空间 5 1.5 fork、exec和exit对IPC对象的影响 7 1.6 出错处理:包裹函数 8 1.7 Unix标准 9 1.8 书中IPC例子索引表 11 1.9 小结 13 习题 13 第2章 Posix IPC 14 2.1 概述 14 2.2 IPC名字 14 2.3 创建与打开IPC通道 16 2.4 IPC权限 18 2.5 小结 19 习题 19 第3章 System V IPC 20 3.1 概述 20 3.2 key_t键和ftok函数 20 3.3 ipc_perm结构 22 3.4 创建与打开IPC通道 22 3.5 IPC权限 24 3.6 标识符重用 25 3.7 ipcs和ipcrm程序 27 3.8 内核限制 27 3.9 小结 28 习题 29 第二部分 消息传递 31 第4章 管道和FIFO 32 4.1 概述 32 4.2 一个简单的客户-服务器例子 32 4.3 管道 32 4.4 全双工管道 37 4.5 popen和pclose函数 39 4.6 FIFO 40 4.7 管道和FIFO的额外属性 44 4.8 单个服务器,多个客户 46 4.9 对比迭代服务器与并发服务器 50 4.10 字节流与消息 51 4.11 管道和FIFO限制 55 4.12 小结 56 习题 57 第5章 Posix消息队列 58 5.1 概述 58 5.2 mq_open、mq_close和mq_unlink函数 59 5.3 mq_getattr和mq_setattr函数 61 5.4 mq_send和mq_receive函数 64 5.5 消息队列限制 67 5.6 mq_notify函数 68 5.7 Posix实时信号 78 5.8 使用内存映射I/O实现Posix消息队列 85 5.9 小结 101 习题 101 第6章 System V消息队列 103 6.1 概述 103 6.2 msgget函数 104 6.3 msgsnd函数 104 6.4 msgrcv函数 105 6.5 msgctl函数 106 6.6 简单的程序 107 6.7 客户-服务器例子 112 6.8 复用消息 113 6.9 消息队列上使用select和poll 121 6.10 消息队列限制 122 6.11 小结 124 习题 124 第三部分 同步 125 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 互斥锁和条件变量的属性 136 7.8 小结 139 习题 139 第8章 读写锁 140 8.1 概述 140 8.2 获取与释放读写锁 140 8.3 读写锁属性 141 8.4 使用互斥锁和条件变量实现读写锁 142 8.5 线程取消 148 8.6 小结 153 习题 153 第9章 记录上锁 154 9.1 概述 154 9.2 对比记录上锁与文件上锁 157 9.3 Posix fcntl记录上锁 158 9.4 劝告性上锁 162 9.5 强制性上锁 164 9.6 读出者和写入者的优先级 166 9.7 启动一个守护进程的唯一副本 170 9.8 文件作锁用 171 9.9 NFS上锁 173 9.10 小结 173 习题 174 第10章 Posix信号量 175 10.1 概述 175 10.2 sem_open、sem_close和sem_unlink函数 179 10.3 sem_wait和sem_trywait函数 180 10.4 sem_post和sem_getvalue函数 180 10.5 简单的程序 181 10.6 生产者-消费者问题 186 10.7 文件上锁 190 10.8 sem_init和sem_destroy函数 191 10
两卷本的《UNIX网络编程》是已故著名技术作家W. Richard Stevens的传世之作。卷2着重讨论如何让应用程序与在其他机器上的应用程序进行对话。良好的进程间通信(IPC)机制是提高UNIX程序性能的关键。本书全面深入地讲解了各种进程间通信形式,包括消息传递、同步、共享内存及远程调用(RPC)。书中包含了大量经过优化的源代码,帮助读者加深理解。这些源代码可以从图灵网站本书网页免费注册下载。 本书是网络研究和开发人员公认的权威参考书,深入理解本书内容,方能设计出良好的UNIX软件 目录回到顶部↑ 第一部分 简介 第1章 简介 2 1.1 概述 2 1.2 进程、线程与信息共享 3 1.3 IPC对象的持续性 4 1.4 名字空间 5 1.5 fork、exec和exit对IPC对象的影响 7 1.6 出错处理:包裹函数 8 1.7 Unix标准 9 1.8 书中IPC例子索引表 11 1.9 小结 13 习题 13 第2章 Posix IPC 14 2.1 概述 14 2.2 IPC名字 14 2.3 创建与打开IPC通道 16 2.4 IPC权限 18 2.5 小结 19 习题 19 第3章 System V IPC 20 .3.1 概述 20 3.2 key_t键和ftok函数 20 3.3 ipc_perm结构 22 3.4 创建与打开IPC通道 22 3.5 IPC权限 24 3.6 标识符重用 25 3.7 ipcs和ipcrm程序 27 3.8 内核限制 27 3.9 小结 28 习题 29 第二部分 消息传递 第4章 管道和FIFO 32 4.1 概述 32 4.2 一个简单的客户-服务器例子 32 4.3 管道 32 4.4 全双工管道 37 4.5 popen和pclose函数 39 4.6 FIFO 40 4.7 管道和FIFO的额外属性 44 4.8 单个服务器,多个客户 46 4.9 对比迭代服务器与并发服务器 50 4.10 字节流与消息 51 4.11 管道和FIFO限制 55 4.12 小结 56 习题 57 第5章 Posix消息队列 58 5.1 概述 58 5.2 mq_open、mq_close和mq_unlink函数 59 5.3 mq_getattr和mq_setattr函数 61 5.4 mq_send和mq_receive函数 64 5.5 消息队列限制 67 5.6 mq_notify函数 68 5.7 Posix实时信号 78 5.8 使用内存映射I/O实现Posix消息队列 85 5.9 小结 101 习题 101 第6章 System V消息队列 103 6.1 概述 103 6.2 msgget函数 104 6.3 msgsnd函数 104 6.4 msgrcv函数 105 6.5 msgctl函数 106 6.6 简单的程序 107 6.7 客户-服务器例子 112 6.8 复用消息 113 6.9 消息队列上使用select和poll 121 6.10 消息队列限制 122 6.11 小结 124 习题 124 第三部分 同步 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 互斥锁和条件变量的属性 136 7.8 小结 139 习题 139 第8章 读写锁 140 8.1 概述 140 8.2 获取与释放读写锁 140 8.3 读写锁属性 141 8.4 使用互斥锁和条件变量实现读写锁 142 8.5 线程取消 148 8.6 小结 153 习题 153 第9章 记录上锁 154 9.1 概述 154 9.2 对比记录上锁与文件上锁 157 9.3 Posix fcntl记录上锁 158 9.4 劝告性上锁 162 9.5 强制性上锁 164 9.6 读出者和写入者的优先级 166 9.7 启动一个守护进程的唯一副本 170 9.8 文件作锁用 171 9.9 NFS上锁 173 9.10 小结 173 习题 174 第10章 Posix信号量 175 10.1 概述 175 10.2 sem_open、sem_close和sem_ unlink函数 179 10.3 sem_wait和sem_trywait函数 180 10.4 sem_post和sem_getvalue函数 180 10.5 简单的程序 181 10.6 生产者-消费者问题 186 10.7 文件上锁 190 10.8 sem_init和sem_destroy函数 191 10.9 多个生产者,单个消费者 193 10.10 多个生产者,多个消费者 196 10.11 多个缓冲区 199 10.12 进程间共享信号量 205 10.13 信号量限制 206 10.14 使用FIFO实现信号量 206 10.15 使用内存映射I/O实现信号量 210 10.16 使用System V信号量实现Posix信号量 218 10.17 小结 224 习题 225 第11章 System V 信号量 226 11.1 概述 226 11.2 semget函数 227 11.3 semop函数 229 11.4 semctl函数 231 11.5 简单的程序 232 11.6 文件上锁 237 11.7 信号量限制 238 11.8 小结 242 习题 242 第四部分 共享内存区 第12章 共享内存区介绍 244 12.1 概述 244 12.2 mmap、munmap和msync函数 247 12.3 在内存映射文件中给计数器持续加1 250 12.4 4.4BSD匿名内存映射 254 12.5 SVR4 /dev/zero内存映射 255 12.6 访问内存映射的对象 255 12.7 小结 259 习题 260 第13章 Posix共享内存区 261 13.1 概述 261 13.2 shm_open和shm_unlink函数 261 13.3 ftruncate和fstat函数 262 13.4 简单的程序 263 13.5 给一个共享的计数器持续加1 267 13.6 向一个服务器发送消息 270 13.7 小结 275 习题 275 第14章 System V共享内存区 276 14.1 概述 276 14.2 shmget函数 276 14.3 shmat函数 277 14.4 shmdt函数 277 14.5 shmctl函数 277 14.6 简单的程序 278 14.7 共享内存区限制 281 14.8 小结 282 习题 283 第五部分 远程过程调用 第15章 门 286 15.1 概述 286 15.2 door_call函数 291 15.3 door_create函数 292 15.4 door_return函数 293 15.5 door_cred函数 294 15.6 door_info函数 294 15.7 例子 295 15.8 描述符传递 306 15.9 door_sever_create函数 310 15.10 door_bind、door_unbind和door_revoke函数 315 15.11 客户或服务器的过早终止 315 15.12 小结 321 习题 322 第16章 Sun RPC 323 16.1 概述 323 16.2 多线程化 330 16.3 服务器捆绑 333 16.4 认证 336 16.5 超时和重传 338 16.6 调用语义 342 16.7 客户或服务器的过早终止 343 16.8 XDR:外部数据表示 345 16.9 RPC分组格式 361 16.10 小结 365 习题 366 后记 368 附录A 性能测量 371 附录B 线程入门 406 附录C 杂凑的源代码 409 附录D 精选习题解答 417 参考文献 433 索引 435

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值