UNIX-Linux环境编程(五):进程管理

深入UNIX环境高级编程:全面课件指南 UNIX操作系统是一种多用户、多任务的操作系统。其设计哲学强调简洁性和模块化,它为现代操作系统的设计提供了许多重要的概念,如进程、文件和权限等。UNIX系统在计算机网络、服务器和开发领域中具有广泛的应用。 阅读详情

一、基本概念

1. 进程与程序
1) 进程就是运行中的程序。 

一个运行着的程序,可能有多个进程。进程在操作系统中执行特定的任务。
2) 程序是存储在磁盘上, 包含可执行机器指令和数据的静态实体。
进程或者任务是处于活动状态的计算机程序。

2. 进程的分类
1) 进程一般分为**交互进程**、**批处理进程**和**守护进程**三类。
2) 守护进程总是活跃的,一般是后台运行。 

守护进程一般是由系统在开机时通过脚本自动激活启动,
或者由超级用户root来启动。

3. 查看进程
1) 简单形式 

ps
以简略方式显示当前用户有控制终端的进程信息。

2) BSD风格常用选项 

ps axu
a - 所有用户有控制终端的进程
x - 包括无控制终端的进程
u - 以详尽方式显示
w - 以更大列宽显示

3) SVR4风格常用选项 

ps -efl
-e或-A - 所有用户的进程
-a - 当前终端的进程
-u 用户名或用户ID - 特定用户的进程
-g 组名或组ID - 特定组的进程
-f - 按完整格式显示
-F - 按更完整格式显示
-l - 按长格式显示

4) 进程信息列表
◦USER/UID: 进程属主。
◦PID: 进程ID。
◦%CPU/C: CPU使用率。
◦%MEM: 内存使用率。
◦VSZ: 占用虚拟内存大小(KB)。
◦RSS: 占用物理内存大小(KB)。
◦TTY: 终端次设备号,“?”表示无控制终端,如后台进程。
◦STAT/S: 进程状态。可取如下值: 
		◾O - 就绪。等待被调度。
		◾R - 运行。Linux下没有O状态,就绪状态也用R表示。
		◾S - 可唤醒睡眠。系统中断,获得资源,收到信号,都可被唤醒,转入运行状态。
		◾D - 不可唤醒睡眠。只能被wake_up系统调用唤醒。
		◾T - 暂停。收到SIGSTOP信号转入暂停状态, 收到SIGCONT信号转入运行状态。
		◾W - 等待内存分页(2.6内核以后被废弃)。
		◾X - 死亡。不可见。
		◾Z - 僵尸。已停止运行,但其父进程尚未获取其状态。
		◾< - 高优先级。
		◾N - 低优先级。
		◾L - 有被锁到内存中的分页。实时进程和定制IO。
		◾s - 会话首进程。
		◾l - 多线程化的进程。
			◾
				◾
					◾+ - 在前台进程组中。
◦PSR: 进程被绑定到哪个处理器。
  1. 父进程、子进程、孤儿进程和僵尸进程

    1. 父进程启动子进程后,子进程在操作系统的调度下与其父进程同时运行。
    2. 子进程先于父进程结束,子进程向父进程发送SIGCHLD(17)信号,父进程回收子进程的相关资源。
    3. 父进程先于子进程结束,子进程成为孤儿进程,同时被init进程收养,即成为init进程的子进程。
    4. 子进程先于父进程结束,但父进程没有回收子进程的相关资源,该子进程即成为僵尸进程。
  2. 进程标识符(进程ID)

    1. 每个进程都有一个以非负整数表示的唯一标识,即进程ID/PID。
    2. 进程ID在任何时刻都是唯一的,但可以重用,当一个进程退出时,其进程ID就可以被其它进程使用。
    3. 延迟重用。
      a.out - 1000
      a.out - 1010
      a.out - 1020
      范例:delay.c
#include <stdio.h>
#include <unistd.h>

int main () 
{
    printf ("进程ID:%u\n", getpid ());
    return 0;
}

二、getxxxid

#include <unistd.h>
getpid  - 获取进程ID
getppid - 获取父进程ID
getuid  - 获取实际用户ID
geteuid - 获取有效用户ID
getgid  - 获取实际组ID
getegid - 获取有效组ID

范例:id.c

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

int main () 
{
    printf ("    进程ID:%u\n", getpid ());
    printf ("  父进程ID:%u\n", getppid ());
    printf ("实际用户ID:%u\n", getuid ());
   printf ("有效用户ID:%u\n", geteuid ());
    printf ("  实际组ID:%u\n", getgid ());
    printf ("  有效组ID:%u\n", getegid ());

    return 0;
}

三、fork

#include <unistd.h>
pid_t fork (void);
  1. 创建一个子进程,失败返回-1
  2. 调用一次,返回两次
    分别在父子进程中返回子进程的PID和0。
    用返回值的不同,可以分别为父子进程编写不同的处理分支。
    范例:fork.c
#include <stdio.h>
#include <unistd.h>

int main () 
{
    printf ("%u进程:我要调用fork()了...\n", getpid ());

    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0) 
    {
        printf ("%u进程:我是%u进程的子进程。\n", getpid (),
            getppid ());
        return 0;
    }

    printf ("%u进程:我是%u进程的父进程。\n", getpid (), pid);
    sleep (1);

    return 0;
}
  1. 子进程是父进程的副本
    子进程获得父进程数据段和堆栈段(包括I/O流缓冲区)的拷贝,但子进程共享父进程的代码段。
    范例:mem.c、os.c、is.c 、mem.c

mem.c

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

int global = 100;

int main () 
{
    int local = 200;

    char* heap = (char*)malloc (256 * sizeof (char));
    sprintf (heap, "ABC");

    printf ("父进程:%d %d %s\n", global, local, heap);

    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0) 
    {
        global++;
        local++;
        sprintf (heap, "XYZ");
        printf ("子进程:%d %d %s\n", global, local, heap);
        free (heap);

        return 0;
    }

    sleep (1);
    printf ("父进程:%d %d %s\n", global, local, heap);
    free (heap);

    return 0;
}

os.c

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

int main () 
{
    printf ("ABC");

    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0) 
    {
        printf ("XYZ\n");
        return 0;
    }

    sleep (1);
    printf ("\n");

    return 0;
}
  1. 函数调用后父子进程各自继续运行
    其先后顺序不确定, 某些实现可以保证子进程先被调度。
  2. 共享文件表
    函数调用后,父进程的文件描述符表(进程级)也会被复制到子进程中,二者共享同一个文件表(内核级)。
    范例:ftab.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

int main () 
{
    int fd = open ("ftab.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) 
    {
        perror ("open");
        return -1;
    }

    const char* text = "Hello, World !";
    if (write (fd, text, strlen (text) * sizeof (text[0])) == -1) 
    {
        perror ("write");
        return -1;
    }

    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0) 
    {
        if (lseek (fd, -7, SEEK_CUR) == -1) 
        {
            perror ("lseek");
            return -1;
        }

        close (fd);

        return 0;
    }

    sleep (1);

    text = "Linux";
    if (write (fd, text, strlen (text) * sizeof (text[0])) == -1) 
    {
        perror ("write");
        return -1;
    }

    close (fd);

    return 0;
}

  1. 总进程数或实际用户ID所拥有的进程数,超过系统限制,该函数将失败。
  2. 一个进程如果希望创建自己的副本并执行同一份代码,或希望与另一个程序并发地运行,都可以使用该函数。
  3. 孤儿进程与僵尸进程。
    范例:orphan.c、zombie.c
    orphan.c
#include <stdio.h>
#include <unistd.h>

int main (void) 
{
    printf ("%u进程:我要调用fork()了...\n", getpid ());

    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0) 
    {
        sleep (1);
        printf ("\n%u进程:我是被%u进程收养的孤儿进程。", getpid (),
            getppid ());

        return 0;
    }

    printf ("%u进程:我是%u进程的父进程。\n", getpid (), pid);

    return 0;
}

zombie.c

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

int main () 
{
    printf ("%u进程:我要调用fork()了...\n", getpid ());

    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0)
    {
        printf ("%u进程:我是%u进程的子进程,即将成为僵尸...\n",
            getpid (), getppid ());
        return 0;
    }

    sleep (1);
    printf ("%u进程:我是%u进程的父进程。\n", getpid (), pid);

    printf ("执行ps -efl | grep %u,按<回车>退出...", pid);
    getchar ();

    return 0;
}

注意:fork之前的代码只有父进程执行,fork之后的代码父子进程都有机会执行,受代码逻辑的控制而进入不同分支。

四、vfork

#include <unistd.h>
pid_t vfork (void);

该函数的功能与fork基本相同,二者的区别:

  1. 调用vfork创建子进程时并不复制父进程的地址空间
    子进程可以通过exec函数族,直接启动另一个进程替换自身,进而提高进程创建的效率。
  2. vfork调用之后,子进程先被调度。

五、进程的正常退出

  1. 从main函数中return。
    int main (…)
    {
    …
    return x;
    }
    等价于:
    int main (…)
    {
    …
    exit (x);
    }

  2. 调用标准C语言的exit函数。

     #include <stdlib.h>
     void exit (int status);
    
    1. 调用进程退出
      其父进程调用wait/waitpid函数返回status的低8位。

    2. 进程退出之前
      先调用所有事先通过atexit/on_exit函数注册的函数,
      冲刷并关闭所有仍处于打开状态的标准I/O流,
      删除所有通过tmpfile函数创建的文件。

    3. 用EXIT_SUCCESS/EXIT_FAILURE常量宏(可能是0/1)作参数,调用exit()函数表示成功/失败,提高平台兼容性。

    4. 该函数不会返回。

    5. 该函数的实现调用了_exit/_Exit函数。

       #include <stdlib.h>
       int atexit (void (*function) (void));
       function - 函数指针,
      指向进程退出前需要被调用的函数。
      该函数既没有返回值也没有参数。
       成功返回0,失败返回非零。
      
       int on_exit (void (*function) (int, void*), void* arg);
       function - 函数指针,
      指向进程退出前需要被调用的函数。
      该函数没有返回值但有两个参数:
      第一参数来自exit函数的status参数,
      第二个参数来自on_exit函数的arg参数。
       arg      - 任意指针,
      将作为第二个参数被传递给function所指向的函数。
       成功返回0,失败返回非零。
      
  3. 调用_exit/_Exit函数

     #include <unistd.h>
     void _exit (int status);
     该函数有一个完全等价的标准C版本:
     #include <stdlib.h>
     void _Exit (int status);
    
  4. 进程的最后一个线程执行了返回语句。

  5. 进程的最后一个线程调用pthread_exit函数。

六、进程的异常终止

  1. 调用abort函数,产生SIGABRT信号。
  2. 进程接收到某些信号(kill -9)。
  3. 最后一个线程对“取消”请求做出响应(pthread_exit())。

七、wait/waitpid

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait (int* status);

pid_t waitpid (pid_t pid, int* status, int options);
  1. 当一个进程正常或异常终止时,内核向其父进程发送SIGCHLD信号。

父进程可以忽略该信号,或者提供一个针对该信号的信号处理函数,默认为忽略。

  1. 父进程调用wait函数:

    1. 若所有子进程都在运行,则阻塞。
    2. 若有一个子进程已终止,则返回该子进程的PID和终止状态(通过status参数)。
    3. 若没有需要等待子进程,则返回失败,errno为ECHILD。
    1. 在任何一个子进程终止前,wait函数只能阻塞调用进程,而waitpid函数可以有更多选择。

    2. 如果有一个子进程在wait函数被调用之前,已经终止并处于僵尸状态,wait函数会立即返回,并取得该子进程的终止状态。

    3. 子进程的终止状态通过输出参数status返回给调用者,若不关心终止状态,可将此参数置空。

    4. 子进程的终止状态可借助sys/wait.h中定义的参数宏查看:
      •WIFEXITED()
      子进程是否正常终止,是则通过WEXITSTATUS()宏,获取子进程调用exit/_exit/_Exit函数,所传递参数的低8位。
      因此传给exit/_exit/_Exit函数的参数最好不要超过255。
      •WIFSIGNALED()
      子进程是否异常终止,是则通过WTERMSIG()宏获取终止子进程的信号。
      •WIFSTOPPED()
      子进程是否处于暂停,是则通过WSTOPSIG()宏获取暂停子进程的信号。
      •WIFCONTINUED()
      子进程是否在暂停之后继续运行
      范例:wait.c、loop.c
      wait.c

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

int main () 
{
    pid_t pid = fork ();
    if (pid == -1) 
    {
        perror ("fork");
        return -1;
    }

    if (pid == 0) 
    {
        int status = 0x12345678;

        printf ("子进程:我是%u进程。我要以%#x状态退出。\n",
            getpid (), status);

        return status;
    }

   printf ("父进程:我要等待子进程...\n");

    int status;
    pid = wait (&status);

    printf ("父进程:发现%u进程以%#x状态退出了。\n", pid,
.        WEXITSTATUS (status));

    return 0;
}

loop.c

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

int main () 
{
    int i;
    for (i = 0; i < 3; i++) 
    {
        pid_t pid = fork ();
        if (pid == -1) 
        {
            perror ("fork");
            return -1;
        }

        if (pid == 0) 
        {
            printf ("子进程:我是%u进程。我要退出了。\n", getpid ());
            return 0;
        }
    }

    for (;;) 
    {
        printf ("父进程:我要等待子进程...\n");

        pid_t pid = wait (0);
        if (pid == -1) 
        {
            if (errno != ECHILD) 
            {
                perror ("wait");
                return -1;
            }

            printf ("父进程:已经没有子进程可等了,走喽!\n");
            break;
        }

        printf ("父进程:发现%u进程退出了。\n", pid);
    }

    return 0;
}
  1. 如果同时存在多个子进程,又需要等待特定的子进程,可使用waitpid函数,其pid参数:

     -1 - 等待任一子进程,此时与wait函数等价。
     > 0 - 等待由该参数所标识的特定子进程。
      0 - 等待其组ID等于调用进程组ID的任一子进程,即等待与调用进程同进程组的任一子进程。
     <-1 - 等待其组ID等于该参数绝对值的任一子进程,即等待隶属于特定进程组内的任一子进程。
    

范例:waitpid.c

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

int main () 
{
    pid_t cpid[3];
    int i;
    for (i = 0; i < sizeof (cpid) / sizeof (cpid[0]); i++) 
    {
        cpid[i] = fork ();
        if (cpid[i] == -1) 
        {
            perror ("fork");
            return -1;
        }

        if (cpid[i] == 0) 
        {
            printf ("子进程:我是%u进程。我要退出了。\n", getpid ());
            return 0;
        }
    }

    for (i = 0; i < sizeof (cpid) / sizeof (cpid[0]); i++) 
    {
        printf ("父进程:我要等待%u进程...\n", cpid[i]);

        pid_t pid = waitpid (cpid[i], 0, 0);
        if (pid == -1) 
        {
            perror ("waitpid");
            return -1;
        }

        printf ("父进程:发现%u进程退出了。\n", pid);
    }

    return 0;
}

  1. waitpid函数的options参数可取0(忽略)或以下值的位或:

     •WNOHANG 
     非阻塞模式,若没有可用的子进程状态,则返回0。 
     	◦WUNTRACED 
     	若支持作业控制,且子进程处于暂停态,则返回其状态。
     	◦WCONTINUED 
     	若支持作业控制,且子进程暂停后继续,则返回其状态。
    

范例:nohang.c

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>

int main (void) 
{
    int i;
    for (i = 0; i < 3; i++) 
    {
        pid_t pid = fork ();
        if (pid == -1) 
        {
            perror ("fork");
            return -1;
        }

        if (pid == 0) 
        {
            printf ("子进程:我是%u进程。我要退出了。\n", getpid ());
            return 0;
        }
    }

    for (;;) 
    {
        printf ("父进程:我要等待子进程...\n");

        pid_t pid = waitpid (-1, 0, WNOHANG);
        if (pid == -1) 
        {
            if (errno != ECHILD) 
            {
                perror ("waitpid");
                return -1;
            }

            printf ("父进程:已经没有子进程可等了,走喽!\n");
            break;
        }

        if (pid)
            printf ("父进程:发现%u进程退出了。\n", pid);
        else
            printf ("父进程:没发现子进程退出,干点儿别的...\n");
    }

    return 0;
}

八、exec

1、exec函数会用新进程完全替代调用进程,并开始从main函数执行。

2、exec函数并非创建子进程,新进程取调用进程的PID。

3、exec函数所创建的新进程,完全取代调用进程的代码段、数据段和堆栈段。

4、exec函数若执行成功,则不会返回,否则返回-1。

5、exec函数包括六种形式:

#include <unistd.h>

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

int execv  (
   const char* path,
   char* const argv[]
);

int execle (
	const char* path,
	const char* arg,
    ...,
	char* const envp[]
);

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

int execlp (
	const char* file,
	const char* arg,
	...
);

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

l: 新程序的命令参数以单独字符串指针的形式传入(const char* arg, ...),参数表以空指针结束。
v: 新程序的命令参数以字符串指针数组的形式传入(char* const argv[]),数组以空指针结束。
e: 新程序的环境变量以字符串指针数组的形式传入(char* const envp[]),数组以空指针结束,无e则从调用进程的environ变量中复制。
p: 若第一个参数中不包含“/”,则将其视为文件名,根据PATH环境变量搜索该文件。

范例:argenv.c、exec.c
argenv.c

#include <stdio.h>

void printarg (int argc, char* argv[]) 
{
    printf ("---- 命令参数 ----\n");

    int i;
    for (i = 0; i < argc; i++)

       printf ("argv[%d] = %s\n", i, argv[i]);

    printf ("------------------\n");
}

void printenv () 
{
    printf ("---- 环境变量 ----\n");

    extern char** environ;
    char** env;
    for (env = environ; env && *env; env++)
        printf ("%s\n", *env);

    printf ("------------------\n");
}

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

    return 0;
}

exec.c

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

int main (void) 
{
    char* path = "./argenv";
    char* file = "argenv";
    char* argv[] = {path, "hello", "world", NULL};
    char* envp[] = {"USER=unknown", "PATH=/tmp", NULL};
    /*
    if (execl (path, argv[0], argv[1], argv[2], argv[3]) == -1) 
    {
        perror ("execl");
        return -1;
    }
    *//*
    if (execv (path, argv) == -1) 
    {
        perror ("execv");
        return -1;
    }
    *//*
    if (execle (path, argv[0], argv[1], argv[2], argv[3], envp) == -1) 
    {
        perror ("execle");
        return -1;
    }
    *//*
    if (execve (path, argv, envp) == -1) 
    {
        perror ("execve");
        return -1;
    }
    *//*
    if (execlp (file, argv[0], argv[1], argv[2], argv[3]) == -1) 
    {
        perror ("execlp");
        return -1;
    }
    */
    if (execvp (file, argv) == -1) 
    {
        perror ("execvp");
        return -1;
    }

    return 0;
}

九、system

#include <stdlib.h>
int system (const char* command);
  1. 标准C函数。执行command,成功返回command对应进程的终止状态,失败返回-1。
  2. 若command取NULL,返回非零表示shell可用,返回0表示shell不可用。
  3. 该函数的实现,调用了fork、exec和waitpid等函数,其返回值:
    1. 如果调用fork或waitpid函数出错,则返回-1。
    2. 如果调用exec函数出错,则在子进程中执行exit(127)。
    3. 如果都成功,则返回command对应进程的终止状态(由waitpid的status输出参数获得)。
    4. 用system函数而不用fork+exec的好处是,system函数针对各种错误和信号都做了必要的处理。
深入UNIX环境的高级编程实践指南 Unix/Linux系统编程是构建在操作系统提供的底层接口之上的高级编程技术,它允许程序员编写更为接近硬件的代码,以实现高效的资源管理和任务执行。本章将为读者概述系统编程的基础知识,包括进程管理、文件I/O、信号处理以及网络通信等关键概念。通过本章节的介绍,我们深入了解了文件I/O的基本操作、高级技术、文件系统的层次结构和性能调优方法。掌握这些知识对于进行高效和安全的文件管理至关重要。接下来,我们将继续探讨进程管理与控制的相关技术,进一步提升我们的系统编程能力。 阅读详情

相关推荐

UNIX系统环境与进程管理深入解析

本文深入探讨了UNIX系统的窗口管理器,X窗口系统的独特功能,UNIX编程环境的多样性,以及UNIX在个人电脑、分时和客户端-服务器环境中的应用。同时,详细解释了UNIX进程的属性、状态、执行模式和上下文,以及进程间的关系和UNIX系统的初始化进程。

weixin_35749796的博客 613

【Linux】请问下面的程序一共输出多少个“-”?

请问下面的程序一共输出多少个“-”? int main(void) { int i; for (i = 0; i < 2; i++) { fork(); printf("-"); } return 0; } 运行结果 [misaki@localhost test]$ ./main -------- 结果分析  ...

qq_41669298的博客 1722

Linux/Unix系统编程权威指南:深入实践手册

在Linux/Unix系统中,进程是一个程序的执行实例,它包含程序代码、其当前的活动、分配给它的处理器时间、分配给它的内存空间以及系统资源等。进程是系统进行资源分配和调度的一个独立单位。进程主要分为以下几类:用户进程:这是用户应用程序运行时创建的进程。用户进程通常在用户空间运行,并通过系统调用请求操作系统服务。系统进程:这是由操作系统启动并执行特定系统任务的进程,例如守护进程。这些进程通常拥有比用户进程更高的权限。内核线程:这些是内核自身执行的操作,表现为内核线程。

weixin_42376614的博客 1031

二级C原题错题记录

1 #include<stdio.h> main() { int x=1,y=0,a=0,b=0; switch(x) {case 1: switch(y) {case 0: a++;break; case1:b++;break; } case 2: a++;b++;break; case 3:a++;b++; } prient(“a=%d,b=?%d\n”,a,b); } 解题:x=...

weixin_43603998的博客 1443

全面掌握Unix/Linux系统编程实践教程

进程是操作系统进行资源分配和调度的一个独立单位。每个进程都拥有自己的地址空间,代码、数据和其他资源。现代操作系统通过进程控制块(PCB)来管理进程,PCB包含了进程的PID、状态、程序计数器、寄存器集合和内存管理信息等。进程具有并发性、独立性、异步性和结构性四大特性。并发性指进程在操作系统中可以同时存在和运行,独立性表明进程在执行时互不影响,异步性是指进程按照各自独立的、不可预知的速度向前推进,结构性体现在进程由代码段、数据段和PCB等组成。

weixin_35734408的博客 754

Linux/Unix编程实战教程:深入解析translator源码

在操作系统的领域内,进程是系统进行资源分配和调度的一个独立单位。它代表了程序在执行期间的所有动态变化。进程可以理解为处于活动状态的程序,它包含了一系列的指令集合以及必要的资源,如CPU时间、内存地址空间、文件句柄等。进程的生命周期由创建、运行、等待、终止四个基本状态组成。创建状态是指进程被创建时所处的状态,此时操作系统为其分配了唯一进程标识符PID并建立了进程控制块PCB。运行状态指的是进程正在处理器上执行。当进程需要等待资源时,它会进入等待(阻塞)状态。

weixin_33660045的博客 683

C语言上学期整理(第6章)

文章目录第6章 利用数组批量处理数据6.1怎样定义和引用一维数组6.2怎样定义和引用二维数组6.3字符数组 第6章 利用数组批量处理数据 6.1怎样定义和引用一维数组 6.2怎样定义和引用二维数组 6.3字符数组 ...

PayneZhai的博客 440

《LINUX与UNIX SHELL编程指南》读书笔记及实践

Linux与UNIX操作系统作为现代计算的基石,拥有悠久而光辉的历史,为许多现代操作系统提供了设计理念和技术基础。本章将带领读者深入探讨Linux与UNIX的世界,从它们的历史沿革开始,逐步解析其系统架构,最后介绍主流的发行版及其版本特性,为后续章节的Shell编程和系统管理打下坚实的基础。Shell编程是Linux与UNIX系统中重要的自动化和任务调度工具。掌握Shell编程能够极大提高工作效率,并且对于系统管理以及应用部署都具有不可替代的作用。

weixin_42584758的博客 1135

c语言期末考试复习

** 写在开头 卧槽卧槽,记得scanf要加&!!! 1.输出第二个整数 就是把它定义了,直接输出就好了。 #include<stdio.h> int main(){ int a; int b; int c; scanf("%d%d%d",&a,&b,&c); printf("%d",b); return 0; } 2.求两个数的和 求多个数的和,其他两个数的乘积*,差-,除数/,余数%,都是一样的。除数注意转换它的格式,比如两个整数型除不尽,记得

weixin_51335267的博客 750

深入理解Unix和C/C++编程环境

本文深入探讨了Unix和C/C++编程环境的核心概念和工作原理。首先介绍了Unix操作系统的设计原则、层次结构和文件系统。接着,详细解释了shell和命令行工具的使用,以及如何通过shell编写脚本以提高工作效率。文章还探讨了Linux作为Unix的一个免费开源版本,在个人电脑和嵌入式系统中的广泛应用。最后,针对程序员编程所需掌握的Unix基本命令进行了分类和实例演示。

weixin_34945060的博客 674

【深度学习3】ubuntu22.04从零开始搭建深度学习环境

ubuntu22.04从零开始搭建深度学习开发环境

weixin_43387635的博客 1万+

C语言作业

第一章 程序设计和C语言 例1.1要求在屏幕上输出以下一行信息。These is a C program 例1.2求两个整数之和 例1.3求两个整数中的较大者 #include&amp;amp;amp;amp;amp;lt;stdio.h&amp;amp;amp;amp;amp;gt; int main() { printf(&amp;amp;amp;amp;quot;This is a C program.\n&amp;amp;amp;amp;quot;);

wangyan0的博客 5076

matlab实现MSK调制解调

文章目录一、要求二、设计三、代码四、结果五、结论 一、要求 画出MSK信号产生的信息、I路、Q路信号对比图以及接收端解调的信息恢复的对比图。 二、设计 1.MSK调制 2.MSK解调 三、代码 clear all;clc; N=20; T=1; fc=10; Fs=100; bitstream=randi([0,1],1,N); bitstream1=2*bitstream-1; b0=1; for i=1:N encode_output(i)=b0*bitstream

Time_book的博客 1万+

UNIX-Linux进程管理

UNIX-Linux进程管理基本概念进程标识符创建进程 基本概念 1、进程与程序 程序:存储在磁盘上的文件,包含可执行指令和数据的静态实体。 进程:运行中的程序(一个程序可以执行多次,加载出多个进程)。 进程就是处于活动状态的计算机程序。 2、进程的分类: 交互进程:有输入输出,用户可以根据自己的情况输入数据,得到想要的结果(一般进程)。 批处理进程:由脚本加载执行的程序(比如Linux下shel...

m0_37622246的博客 360

保姆级教程:用BMS的HVIL检测电路,手把手教你排查高压互锁故障(附PWM/直流源方案对比)

本文详细解析了BMS中的HVIL检测电路,通过对比直流源与PWM两种方案,提供了一套系统化的高压互锁故障诊断流程。从电路原理到实战技巧,帮助维修人员精准定位故障根源,提升电动汽车高压系统的安全性和可靠性。

weixin_29300241的博客 390

达梦数据库与sharding-jdbc的深度适配实践

本文详细介绍了达梦数据库与sharding-jdbc的深度适配实践,包括驱动与依赖管理、核心适配实现、注册与配置等关键步骤。通过扩展shardingSphere的方式,成功实现了达梦数据库的分库分表功能,并提供了常见问题解决方案和性能优化建议,助力企业级应用高效处理海量数据。

weixin_30300225的博客 623

【亲测免费】 《UNIX环境高级编程》中文第三版笔记教程

`APUE_notes` 是一个关于《UNIX环境高级编程》(Advanced Programming in the UNIX Environment)第三版的中文笔记项目。该项目由 huaxz1986 维护,旨在帮助读者更好地理解和掌握 UNIX 环境下的高级编程技术。笔记内容涵盖了书籍的主要章节,提炼了重点和关键点,并提供了示例代码和运行结果截图,帮助读者更好地理解和应用书中的知识。 ##

gitblog_00490的博客 587

LangChain开发避坑指南:为什么Pylance总提示‘无法解析导入‘?实测7种修复方案

本文深入解析了在VS Code中使用LangChain开发时,Pylance频繁提示‘无法解析导入’的根本原因——Python解释器与语言服务器的环境路径不一致。文章提供了从基础排查到深度配置的7种实测修复方案,包括切换解释器、配置extraPaths、使用pyrightconfig.json等,旨在帮助开发者彻底解决此问题,提升AI应用开发效率。

weixin_28826521的博客 444

探索UNIX/Linux系统编程的终极指南

探索UNIX/Linux系统编程的终极指南 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 欢迎来到UNIX环境高级编程的宝藏库!本仓库为广大开发者提供了一份珍贵的学习资源——《UNIX环境高级编程》(Third Edition)的中文版PDF,以及书中所有示例程序的源代码。无论你是初学者还是经验丰富的开发者,这份资源都将是你深入理解Unix/Linux系统编程的得力助...

gitblog_09700的博客 806

【数字孪生与仿真技术】14:数据驱动+机理模型:工业级混合建模实战(附MATLAB完整代码+案例解析)

摘要 本文针对工业建模中机理模型精度不足和数据驱动模型缺乏物理约束的痛点,提出混合建模技术作为解决方案。文章系统阐述了混合建模的3种主流架构(参数校正型、残差补偿型、组件替换型),结合清华大学催化裂化装置的实际案例,分析混合建模的工业应用价值。同时以连续搅拌反应釜(CSTR)为例,提供MATLAB环境下残差补偿型混合模型的完整实现代码,详细展示从机理模型构建到数据驱动补偿的全流程,并给出工程实践中的应对策略,为工业建模提供可落地的技术方案。 关键词 混合建模、机理模型、数据驱动、工业建模、MATLAB

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 621

华为OD新系统机试真题-勇攀数字高峰(Java/Py/C/C++/Js/Go)

新系统 华为OD机试新系统【勇登数字高峰】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD新系统机试真题【勇登数字高峰】

qq_45776114的博客 1544

【Linux】UNIX环境高级编程(知识点概括)

UNIX环境高级编程(知识点概括) 自学习总结整理,后期持续更新 文章目录UNIX环境高级编程(知识点概括)文件I/O文件和目录标准I/O库系统数据文件和信息进程环境进程控制进程关系 文件I/O 缓冲文件系统:在内存开辟一个“缓冲区”,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读出需要的数据 fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fsee

看!爪可摘星辰! 1769

【DRAM存储器四】DRAM存储器的架构演进-part1

详述以提升吞吐量为目标的DRAM架构变化

highman110的博客 1247

Linux/Uinx 系统编程:进程管理(1)

文章讲述了Linux/Uinx中进程的概念、进程的底层实现,PROC结构体、挂载、登录进程、操作系统初始化函数INIT、守护进程的概念、shell进程、进程的执行模式、与进程管理有关的一系列函数,例如fork()、wait()、waitpid()等,还有进程在系统中的执行顺序,以及终止的一些小细节。

Royi的学习博客 1320

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Linux UNIX系统编程手册(上册).pdf资源推荐

Linux UNIX系统编程手册(上册).pdf资源推荐 【下载地址】LinuxUNIX系统编程手册上册.pdf资源介绍 《Linux UNIX系统编程手册(上册)》是一本深入探讨Linux和UNIX系统编程的权威指南,适合希望提升系统级编程技能的开发者。本书详细讲解了进程管理、线程管理、文件系统操作、I/O系统编程及网...

gitblog_06708的博客 498

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

《Unix环境高级编程》与《Unix网络编程》学习资料集

《Unix环境高级编程》与《Unix网络编程》学习资料集 去发现同类优质开源项目:https://gitcode.com/ 此仓库提供了两本经典书籍《Unix环境高级编程》与《Unix网络编程》的资源文件,是学习Unix/Linux程序设计不可多得的参考资料。这两本书籍深入浅出地讲解了Unix系统的编程知识,无论是系统级的开发者,还是网络编程的实践者,都能够从中获得宝贵的知识。 《Unix环境高级...

gitblog_06782的博客 401

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

上一篇: UNIX-Linux环境编程(六):信号处理
下一篇: UNIX-Linux环境编程(七):进程间通信
perror_0
博客等级 码龄7年 14粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值