【Linux】---进程控制(创建、终止、等待、替换)

2023-11-18

进程创建

fork()

fork函数在之前的文章中也已经提到过了。其主要作用是从已存在的进程中创建一个新的进程,也就是新建的进程为子进程,原进程为父进程

当一个进程调用fork函数后,内核会做几件事:

  1. 分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构内容拷贝给子进程
  3. 添加子进程到系统进程列表中
  4. fork返回后,开始调度器调度

下面来看看进程创建的一段简单代码

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

int main(){
    pid_t id = fork();
    
    if(id == 0){
        printf("I am child process, pid = %d, ppid = %d\n", getpid(), getppid());
    }
    
    printf("I am parent process, pid = %d, ppid = %d\n", getpid(), getppid());
    return 0;
}

image-20221128143753830

当其返回值为0时,说明创建出了子进程。

进程退出

进程退出场景

进程退出总共会有三种情况,也就是我们平常写代码执行的时候也是会遇到这三种情况:

  1. 代码运行完毕,结果正确
  2. 代码运行完毕,结果不正确
  3. 代码遇到异常终止执行

进程退出方法

对于进程退出而言,可以有两种方法退出。一种就是正常的程序运行完毕终止执行,另一种就是程序遇到异常信号终止运行。

那么现在有一个问题,我们平常写代码的时候为什么总是会带上一个 return 0 呢?这里就涉及到一个知识点—退出码

退出码

其实return 0这个0并没有什么特殊的意思,返回的是0就代表着程序执行正常退出,非0就是程序有错误,每一个非0的退出码都代表着不同的错误信息。可以通过程序看看

#include<stdio.h>
#include<string.h>

int main(){
	for(int i = 0; i < 20; i++)
		printf("%d: %s\n", i, strerror(i));
	
	return 0;
}

image-20221128145109320

exit、_exit

那么除了return可以返回退出码退出程序外,exit和**_exit**也是可以的。不过这两者之间还是会有所区别的。

  1. exit是库函数,_exit是系统调用
  2. exit会刷新缓冲区,_exit不会

进程等待

进程等待是非常重要的。之前在进程状态里面谈到了一种状态—僵尸状态。这种状态是非常危险的,会造成内存泄漏。并且一旦进程变成了僵尸状态,那么即使使用kill -9都无法将其杀死。

所以父进程想要获取子进程的任务完成的程度如何,就必须通过进程等待的方式,回收子进程资源,获取子进程退出信息

进程等待的方法

进程等待有两种方法:1、阻塞等待;2、非阻塞等待。可以使用两个函数去实现:wait和waitpid

image-20221128150111166

wait

wait等待成功会返回被等待的进程的pid,失败则返回-1,下面来一段代码感受一下

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

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

  if(id == 0){
    int cnt = 5;
    while(cnt--){
      printf("I am child process, pid = %d\n", getpid());
      sleep(1);
    }
    exit(1);
  }

  pid_t ret = wait(NULL);
  printf("%d\n", ret);

  return 0;
}

image-20221128151017389

可以看到进程最后打印出的是子进程的pid,说明等待成功了。

waitpid

waitpid 相对于 wait 来说能够获取的信息就更多了,可以获取子进程的退出码和子进程返回的状态。

如果子进程是正常终止,那么返回的状态为0,如果收到了异常信号终止则非0

但是这里还要注意的是,waitpid 返回的子进程的数据是有自己的存储方式的。例如 waitpid 返回了一个变量 status 那么这个变量的**高八位为退出状态,低八位为终止信号。

image-20221128151748607

如果进程是被信号所杀,则退出状态就没有用到,终止信号根据实际。如果进程正常终止,则退出状态根据实际,终止信号为0.

所以当waitpid 返回了一个值,我们想要获取终止信号就得用这个值 & 0x7f;获取退出状态就得用这个变量 向右移动8位再 & 0xff

来一段代码感受一下

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

int main(){
  pid_t id = fork();
  assert(id != -1);

  if(id == 0){
    int cnt = 5;
    while(cnt){
      printf("child running pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt--);
      sleep(1);
    }

    exit(0);
  }

  int status = 0;
  while(1){
    pid_t ret = waitpid(id, &status, 0);
    if(ret == 0){
      //子进程没有退出,waitpid没有等待失败,仅仅是检测到子进程没退出
      //waitpid调用成功 && 子进程没有退出
      printf("wait done, but child is running....., parent running other things\n");
    }
        
    else if(ret > 0){
      //waitpid调用成功 && 子进程已退出
      printf("wait success, exit code: %d, sig: %d\n", (status >> 8) & 0xff, (status & 0x7f));
      break;
    }

    else{
      //waitpid调用失败
      printf("waitpid call failed\n");
      break;
    }

    sleep(1);
  }

  return 0;
}

image-20221128160417922

下面再来看看当子进程收到异常信号时退出 waitpid 返回的结果。现在假设一个野指针的情况

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

int main(){
  pid_t id = fork();
  assert(id != -1);

  if(id == 0){
    int cnt = 5;
    while(cnt){
      printf("child running pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt--);
      sleep(1);
    }
	int* p;
	*p = 100;
	
    exit(0);
  }

  int status = 0;
  while(1){
    pid_t ret = waitpid(id, &status, 0);
    if(ret == 0){
      //子进程没有退出,waitpid没有等待失败,仅仅是检测到子进程没退出
      //waitpid调用成功 && 子进程没有退出
      printf("wait done, but child is running....., parent running other things\n");
    }
        
    else if(ret > 0){
      //waitpid调用成功 && 子进程已退出
      printf("wait success, exit code: %d, sig: %d\n", (status >> 8) & 0xff, (status & 0x7f));
      break;
    }

    else{
      //waitpid调用失败
      printf("waitpid call failed\n");
      break;
    }

    sleep(1);
  }

  return 0;
}

image-20221128160758879

可以看到,此时waitpid接收到了异常信号退出并返回异常信号的值,可以通过kill -9查看对应的异常信息

阻塞和非阻塞

上面提到了等待可以分为阻塞等待和非阻塞等待,那么这两种有什么区别呢。

通俗点理解

阻塞等待就是父进程在等待子进程退出时并不会再去做其他的事情

非阻塞等待则是父进程在等待时如果他检测到子进程还没有退出,那它就退出检测去做自己的事情。做完自己的事情后又会检测,直至检测到子进程退出

在 waitpid 中可以通过传入 WNOHANG 表示非阻塞等待,传入0则表示阻塞等待。具体来看一段代码感受一下

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

#define N 10
typedef void (*func_t)();//函数指针
func_t handlerTask[N];

void task1(){
  printf("task1\n");
}

void task2(){
  printf("task2\n");
}

void task3(){
  printf("task3\n");
}

void loadTask(){
  memset(handlerTask, 0, sizeof(handlerTask));
  handlerTask[0] = task1;
  handlerTask[1] = task2;
  handlerTask[2] = task3;
}

int main(){
  pid_t id = fork();
  assert(id != -1);

  if(id == 0){
    int cnt = 5;
    while(cnt){
      printf("child running pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt--);
      sleep(1);
    }

    exit(0);
  }

  loadTask();

  int status = 0;
  while(1){
    pid_t ret = waitpid(id, &status, WNOHANG);//WNOHANG: 非阻塞-> 子进程没有退出,父进程检测时立即退出
    if(ret == 0){
      //子进程没有退出,waitpid没有等待失败,仅仅是检测到子进程没退出
      //waitpid调用成功 && 子进程没有退出
      printf("wait done, but child is running....., parent running other things\n");
      for(int i = 0; handlerTask[i] != NULL; i++)
        handlerTask[i]();

    }

    else if(ret > 0){
      //waitpid调用成功 && 子进程已退出
      printf("wait success, exit code: %d, sig: %d\n", (status >> 8) & 0xff, (status & 0x7f));
      break;
    }

    else{
      //waitpid调用失败
      printf("waitpid call failed\n");
      break;
    }

    sleep(1);
  }

  return 0;
}

image-20221128161320762

可以看到父进程在等待的同时还会去执行指定的任务。等父进程检测到子进程还没有退出时,它就会退出等待去做他的事情。

进程替换

替换的原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

简单理解:替换只是把代码和数据换掉而已,并没有换进程去执行。我们可以通过替换去用A程序执行B程序。

替换所用到的函数

一般来说 实现替换有六种函数选择

#include <unistd.h>`
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 execve(const char *path, char *const argv[], char *const envp[]);

这六个函数各有不同的参数,因此实现的方法不同,但是都是为了替换。

execl

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


int main(){ 
    printf("process running ...\n");

    //.c -> exe -> load -> process -> 运行 -> 执行所写代码
    printf("process is running ....\n");

    //load -> exe
    //第一个参数是告诉系统要执行谁,第二个是要怎么执行
    execl("/usr/bin/ls", "ls", NULL);// all exec* end of NULL

    printf("process running done...\n");

    return 0;
}

execl需要传入替换程序的地址。

image-20221128162635610

替换完成后,当我们执行程序就会有替换程序的效果了。要注意:当execl调用成功后,之后的语句就不再执行了。还可以在execl里面传入选项,已完成更全面的功能实现

 execl("/usr/bin/ls", "ls", "-a", "-l", NULL);// all exec* end of NULL

execlp

execlp相较于execl是不需要传入地址的,它会自动在环境变量里找。只需要传入需要替换的程序即可

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


int main(){ 
    printf("process running ...\n");

    //.c -> exe -> load -> process -> 运行 -> 执行所写代码
    printf("process is running ....\n");

    //load -> exe
    execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);

    printf("process running done...\n");

    return 0;
}

image-20221128163246360

execle

exec* 的函数不仅可以替换系统中的程序,也可以替换我们自己的程序。现在我写一个程序,程序的主要功能是打印环境变量。然后我再用另一个程序替换

mybin.c

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

int main(){
  printf("这是另一个C程序\n");
  printf("PATH: %s\n", getenv("PATH"));
  printf("PWD: %s\n", getenv("PWD"));
  printf("MYENV: %s\n", getenv("MYENV"));

  return 0;
}

myexec.c

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


int main(){ 
  printf("process running ...\n");
  pid_t id = fork();
  assert(id != -1);

  if(id == 0){
    sleep(1);
    char* const envp_[] = {(char*)"MYENV=1234565", NULL};
    execle("./mybin", "mybin", NULL, envp_);

    exit(1);
  }

  return 0;
}

image-20221128163826589

如果我们自定义了环境变量,那么系统本身的环境变量就不会点出来了。

剩下的几个函数都是一样的道理,这里就不说了,可以自行查文档实现

image-20221128164255263

简易的shell

前面讲完了进程的控制,包括:创建、终止、等待、替换。那么结合这些知识我们就可以自己去写一个简单的shell外壳了。

  1. 获取命令行
  2. 解析命令行
  3. 建立一个子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程退出(wait)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<assert.h>

#define NUM 1024
#define OPT_NUM 100
char lineCommand[NUM];
char *myargv[OPT_NUM];//指针数组
int  lastCode = 0;
int  lastSig = 0;

int main(){
  while(1){
    //输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);
  
    //获取输入,输入结束要有'\n'
    char* s = fgets(lineCommand, sizeof(lineCommand) - 1, stdin);
    assert(s != NULL);
  
    (void)s;
    //清除最后一个'\n'
    lineCommand[strlen(lineCommand) - 1] = 0;
  
    //字符串切割
    myargv[0] = strtok(lineCommand, " ");
    int i = 1;
  
    //将颜色选项放入ls命令中
    if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
      myargv[i++] = (char*)"--color=auto";
  
    //没有子串的话,strtok返回NULL
    while(myargv[i++] = strtok(NULL, " "))
      ;
  
    //cd命令不会创建子进程,就让shell自己执行对应命令,执行系统接口
    if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0){
              if(myargv[1] != NULL) 
                chdir(myargv[1]);
              continue;
    }
  
    //执行命令
    pid_t id = fork();
    assert(id != -1);
  
    if(id == 0){
      execvp(myargv[0], myargv);
      exit(1);
    }
  
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    assert(ret > 0);
    (void) ret;
  
    lastCode = ((status>>8) & 0xFF);
    lastSig = (status & 0x7F);
  
  }

}

image-20221128170019119

运行起来之后,虽然还有很多bug,还是能够执行一些简单的指令的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux】---进程控制(创建、终止、等待、替换) 的相关文章

  • fopen 不返回

    我在 C 程序中使用 fopen 以只读模式 r 打开文件 但就我而言 我观察到 fopen 调用没有返回 它不返回 NULL 或有效指针 执行在 fopen 调用时被阻止 文件补丁绝对正确 我已经验证过 并且不存在与权限相关的问题 任何人
  • 域套接字“sendto”遇到“errno 111,连接被拒绝”

    我正在使用域套接字从另一个进程获取值 就像 A 从 B 获取值一样 它可以运行几个月 但最近 A 向 B 发送消息时偶尔会失败 出现 errno 111 连接被拒绝 我检查了B域套接字绑定文件 它是存在的 我也在另一台机器上做了一些测试 效
  • Android 时钟滴答数 [赫兹]

    关于 proc pid stat 中应用程序的总 CPU 使用率 https stackoverflow com questions 16726779 total cpu usage of an application from proc
  • 如何有效截断文件头?

    大家都知道truncate file size 函数 通过截断文件尾部将文件大小更改为给定大小 但是如何做同样的事情 只截断文件的尾部和头部呢 通常 您必须重写整个文件 最简单的方法是跳过前几个字节 将其他所有内容复制到临时文件中 并在完成
  • arm64和armhf有什么区别?

    Raspberry Pi Type 3 具有 64 位 CPU 但其架构不是arm64 but armhf 有什么区别arm64 and armhf armhf代表 arm hard float 是给定的名称Debian 端口 https
  • 如何在 Linux shell 中将十六进制转换为 ASCII 字符?

    假设我有一个字符串5a 这是 ASCII 字母的十六进制表示Z 我需要找到一个 Linux shell 命令 它将接受一个十六进制字符串并输出该十六进制字符串代表的 ASCII 字符 所以如果我这样做 echo 5a command im
  • Pyaudio 安装错误 - “命令‘gcc’失败,退出状态 1”

    我正在运行 Ubuntu 11 04 Python 2 7 1 并想安装 Pyaudio 于是我跑了 sudo easy install pyaudio 在终端中 进程退出并显示以下错误消息 Searching for pyaudio Re
  • NPTL 和 POSIX 线程有什么区别?

    NPTL 和 POSIX 线程之间的基本区别是什么 这两者是如何演变的 POSIX 线程 pthread 不是一个实现 它是几个函数的 API 规范 纸上的标准 英文 其名称以pthread 以及定义在
  • Linux 可执行文件与 OS X“兼容”吗?

    如果您在基于 Linux 的平台上用 C 语言编译一个程序 然后将其移植以使用 MacOS 库 它会工作吗 来自编译器的核心机器代码在 Mac 和 Linux 上兼容吗 我问这个问题的原因是因为两者都是 基于 UNIX 的 所以我认为这是真
  • 使用 sh 运行 bash 脚本

    我有 bash 脚本 它需要 bash 另一个人尝试运行它 sh script name sh 它失败了 因为 sh 是他的发行版中 dash 的符号链接 ls la bin sh lrwxrwxrwx 1 root root 4 Aug
  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • 如何确保应用程序在 Linux 上持续运行

    我试图确保脚本在开发服务器上保持运行 它会整理统计数据并提供网络服务 因此它应该会持续存在 但一天中有几次 它会因未知原因而消失 当我们注意到时 我们只需再次启动它 但这很麻烦 并且某些用户没有权限 或专有技术 来启动它 作为一名程序员 我
  • 如何更改 Apache 服务器的根目录? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何更改 Apache 服务器的文档根目录 我基本上想要localhost从 来 users spencer projects目录而不是
  • CoAP数据包的大小是多少?

    我是这项技术的新手 有人可以帮助我了解一些疑问吗 Q 1 CoAP数据包的大小是多少 我知道有 4 字节固定标头 但是包括标头 选项和负载在内的最大大小限制是多少 Q 2 有像MQTT那样的Keep Alive的概念吗 它在UDP上工作 它
  • Linux 上的用户空间能否实现本机代码的抢占式多任务处理?

    我想知道是否可以在 Linux 用户空间的单个进程中实现本机代码的抢占式多任务处理 也就是说 从外部暂停一些正在运行的本机代码 保存上下文 交换到不同的上下文 然后恢复执行 所有这些都由用户空间精心安排 但使用可能进入内核的调用 我认为这可
  • 删除 Git 存储库,但保留所有文件

    在我使用 Linux 的过程中的某个时刻 我决定将我的主目录中的所有内容都放入源代码管理中是个好主意 我不是在问这是否是一个好主意 我是在问如何撤销它 删除存储库的原因是我最近安装了 Oh My Zsh 而且我非常喜欢它 问题是我的主目录有
  • “make install”将库安装在 /usr/lib 而不是 /usr/lib64

    我正在尝试在 64 位 CentOS 7 2 上构建并安装一个库 为了这个目的我正在跑步 cmake DCMAKE BUILD TYPE Release DCMAKE INSTALL PREFIX usr DCMAKE C COMPILER
  • 如何在基于 Linux 的系统上的 C 程序中使用 mqueue?

    如何在基于 Linux 的系统上的 C 程序中使用 mqueue 消息队列 我正在寻找一些好的代码示例 可以展示如何以正确且正确的方式完成此操作 也许是一个操作指南 下面是一个服务器的简单示例 该服务器接收来自客户端的消息 直到收到告诉其停
  • Linux:如何设置进程的时区?

    我需要设置在 Linux 机器上启动的各个进程的时区 我尝试设置TZ变量 在本地上下文中 但它不起作用 有没有一种方法可以使用与系统日期不同的系统日期从命令行运行应用程序 这可能听起来很愚蠢 但我需要一种sandbox系统日期将被更改的地方
  • 我如何知道 C 程序的可执行文件是在前台还是后台运行?

    在我的 C 程序中 我想知道我的可执行文件是否像这样在前台运行 a out 或者像这样 a out 如果你是前台工作 getpgrp tcgetpgrp STDOUT FILENO or STDIN FILENO or STDERR FIL

随机推荐

  • 【C语言】C语言入门经典题目(范围广,内容多)

    作者 小孙的代码分享 专栏 C语言入门 送给各位的一句话 空杯心态 才能学到新知 希望大家看完这些题目有所收获 别忘了 点赞 评论 目录 前言 字符转ASCII码 判断闰年 简单计算器 包含数字9的数 十六进制转十进制 获得月份天数 求质数
  • MybatisPlus的注解

    MybatisPlus的注解 TableField exist false 表示该属性不为数据库表字段 但又是必须使用的 TableField exist true 表示该属性为数据库表字段 Mybatis Plus 插件有这个功能 可以看
  • RestTemplate使用详解

    文章目录 一 RestTemplate 简单使用 1 创建 RestTemplate 2 接口调用 二 其他设置 1 拦截器配置 2 ErrorHandler 配置 3 HttpMessageConverter 配置 在项目中 当我们需要远
  • 用Vscode编辑verilog代码配置

    这篇教程感觉很详细了 我这里分享一下vscode和插件的安装包链接 都是官网下载的 放心食用 用VSCode编辑verilog代码 iverilog编译 自动例化 自动补全 自动格式化等常用插件 链接 https pan baidu com
  • linux找共享内存

    find proc maps type f exec grep l lt 34865209 gt
  • Spring的IOC,DI和AOP

    spring的优点 IOC和DI降低了组件之间的耦合性 让程序员更专注于业务逻辑 容器提供了众多的辅助类 能加快应用的开发 spring对于主流的应用框架提供了集成支持 如hibernate mybatis Struts等 spring属于
  • 成都瀚网科技有限公司:新手如何做直播?

    越来越多的人涌入这一领域 希望通过直播实现爆发式销售 然而 要想在竞争激烈的市场中脱颖而出 新手需要掌握一些关键技巧和策略 在这篇文章中 我们将分享一些实用的方法 帮助新手实现抖音销量爆发的目标 1 新手如何做直播 选择合适的产品 作为新手
  • 认识常见的显卡外接口

    以下几种为显卡接口的常见类型 1 VGA接口 Video Graphics Array 视频图形阵列 VGA接口是最常见的 应用最广的显卡接口 电脑显示器连接的很多都是这种接口 以前的CRT显示器用的也是这种接口 它也称为D Sub 它支持
  • 伯努利分布、二项分布、多项分布、贝塔分布、狄利克雷分布、高斯分布

    文章目录 伯努利分布 二项分布 多项分布 贝塔分布 狄利克雷分布 高斯分布 伯努利分布 伯努利分布 又名两点分布或0 1分布 介绍伯努利分布前首先需要引入伯努利试验 伯努利试验是只有两种可能结果的单次随机试验 即对于一个随机变量X而言 P
  • ggplot2作图之系统发育树

    library ape tree lt read tree text A B C D E F G H I J K L M N O P library ggtree ggtree tree branch length none ladderi
  • BQ40Z50 调试

    一 简介 BQ40Z50 R1的特点与功能 1 这是一颗单芯片解决方案 集成电池 充放电保护 均衡 电量测量三大主要功能 2 支持1 2 3 4节串联锂离子或锂聚合物电池组 二 使用 1 BQ40Z50 R1 评估版一块 2 调试器一个 3
  • 什么是COBOL? COBOL编程说明

    有些技术永不消亡 它们只是逐渐消失在木制品中 向普通软件开发人员询问有关COBOL 面向通用商业语言 的信息 他们会看着您 就像您提到复写纸 含铅汽油或78 RPM记录一样 与Go或Python甚至Pascal或C 之类的现代语言相比 CO
  • winform多文件上传接口服务器,winform向云服务器上传文件

    winform向云服务器上传文件 内容精选 换一换 安装传输工具在本地主机和Windows云服务器上分别安装数据传输工具 将文件上传到云服务器 例如QQ exe 在本地主机和Windows云服务器上分别安装数据传输工具 将文件上传到云服务器
  • C++ tuple元组的基本用法(总结)

    1 元组简介 tuple是一个固定大小的不同类型值的集合 是泛化的std pair 我们也可以把他当做一个通用的结构体来用 不需要创建结构体又获取结构体的特征 在某些情况下可以取代结构体使程序更简洁 直观 std tuple理论上可以有无数
  • 操作型bi研究报告(转)

    操作型bi是bi发展过程中的转折点 传统上认为 bi是从海量历史数据中利用成熟的分析工具发现业务中的模式和趋势 从战略上和战术上辅助决策的一种技术 但是操作型bi改变了这种观点 它将bi从后台搬出来并且嵌入到业务操作流程的细节中 驱动着日以
  • 影之刃3服务器维护,影之刃33月4日维护公告 内容公告预览

    影之刃3 将于3月4日10 00 12 00对全部服务器停服维护 维护期间将无法登录游戏 给您带来的不便敬请谅解 感谢您的理解和支持 本次维护预计持续2小时 维护时间可能延长或提前结束 具体时间请以开服时间为准 维护结束后 我们将为全服玩家
  • 域名能查到服务器信息么,域名查服务器信息

    域名查服务器信息 内容精选 换一换 用户可以通过查询域名注册信息 确认域名所属的DNS服务器信息 然后再根据域名所属的DNS服务器信息进行DNS验证的相关操作 当 Name Servers 显示如所图1示时 则表示域名所属的DNS服务器为华
  • RTX 3080 Linux和Windows 平台兼容性问题

    好不容易 在某电商平台抢到了一块3080显卡 高高兴兴的装机准备大搞游戏开始深度学习 却遇到了很多麻烦 当然经过多方探索 终于也是解决了linux和Windows双平台的兼容性问题 目前Pytorch和TensorFlow都能使用 首先是l
  • Windows系统下如何运行.sh脚本文件

    前言 sh文件是一种命令脚本文件 在Windows系统下可以通过命令行工具打开运行 通常可以使用Git工具来打开运行 sh脚本文件 不过很多第一次使用Git的人 可能对Git工具不熟悉 sh文件在命令行运行时是有固定写法的 下面介绍详细步骤
  • 【Linux】---进程控制(创建、终止、等待、替换)

    文章目录 进程创建 fork 进程退出 进程退出场景 进程退出方法 退出码 exit exit 进程等待 进程等待的方法 wait waitpid 阻塞和非阻塞 进程替换 替换的原理 替换所用到的函数 execl execlp execle