• Linux系统编程常见函数 (进程/线程)


    Lunix系统编程函数多且复杂,本文记录一下Linux系统编程最最常用的哪些函数以及这些函数的常见用法。当然本文不可能囊括所有,所以在编程时候最好的学习方法还是查看manpage,多看多写,熟能生巧。

    而且这里只是总结一下函数的最常用的用法和参数,相关知识还是要看书看视频学习的。

    博主学习Linux编程不久,水平有限文章有错误还请各位不吝指出。废话不多说,开始咯。

    这篇主要归纳进程和线程相关的函数,文件和目录操作看上一篇:https://www.cnblogs.com/clno1/p/12935626.html

    进程相关:

    fork函数:

    pid_t fork(void);   作用是创建一个子进程。

    失败返回-1;成功返回:① 父进程返回子进程的 ID(非负) ②子进程返回 0   (亦即我们用fork返回值判断当前是父/子进程)

    父子相同处: 全局变量、.data、.text、栈、堆、环境变量、用户 ID、宿主目录、进程工作目录、信号处理方式...

    父子不同处: 1.进程 ID 2.fork 返回值 3.父进程 ID 4.进程运行时间 5.闹钟(定时器) 6.未决信号集

    父子进程的copyonwrite机制:https://www.pianshen.com/article/4305691855/

    getpid函数:

    pid_t getpid(void);  作用是获取当前进程 ID(返回值)

    getppid函数:

    pid_t getppid(void);  获取当前进程的父进程 ID(返回值)

    getuid 函数:

    获取当前进程实际用户 ID  uid_t getuid(void);

    获取当前进程有效用户 ID  uid_t geteuid(void);

    getgid 函数

    获取当前进程使用用户组 ID  gid_t getgid(void);

    获取当前进程有效用户组 ID  gid_t getegid(void);

    孤儿进程与讲师进程,以及回收子进程:

    https://www.cnblogs.com/clno1/p/12937547.html

    守护进程相关: 

     https://www.cnblogs.com/clno1/p/12941308.html

    进程间通信:

    在操作系统历史中出现了很多IPC,如文件、管道、信号、共享内存、消息队列、套接字、命名管道等。但是现今常用的进程间通信方式有:① 管道 (使用最简单) ,② 信号 (开销最小),③ 共享映射区 (无血缘关系),④ 本地套接字 (最稳定

    匿名管道PIPE:

    int pipe(int pipefd[2]);   函数作用创建匿名管道。成功:0;失败:-1,设置 errno;

    函数调用成功返回 r/w 两个文件描述符。无需 open,但需手动 close。规定:fd[0] → r; fd[1] → w,就像 0对应标准输入,1 对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 #include<unistd.h>
     5 #include<errno.h>
     6 #include<pthread.h>
     7 
     8 void sys_err(const char *str) {
     9     perror(str);
    10     exit(1);
    11 }
    12 
    13 int main(int argc,char *argv[])
    14 {
    15     int fd[2];
    16     int ret;
    17     pid_t pid;
    18 
    19     ret=pipe(fd);        //创建管道
    20     if (ret==-1) sys_err("pipe error");
    21 
    22 
    23     pid=fork();        //创建子进程
    24     
    25     //父子进程合作完成ls wc -l
    26     if (pid==-1)         //失败
    27         sys_err("fork error");    
    28     else if (pid>0) {    //父进程完成wc -l
    29         close(fd[1]);    //父进程读,关闭写端
    30         dup2(fd[0],STDIN_FILENO);    //有读端且为空,pipe会阻塞到读到数据
    31         execlp("wc","wc","-l",NULL);
    32         sys_err("exclp wc error");    //一旦执行了execlp就结束了,没法走到这里,所以这里可以用来判错
    33     } else if (pid==0) {    //子进程完成ls
    34         close(fd[0]);    //子进程写,关闭读端
    35         dup2(fd[1],STDOUT_FILENO);
    36         execlp("ls","ls",NULL);
    37         sys_err("exclp ls error");
    38     }    
    39     return 0;
    40 }
    PIPE完成ls wc -l

    命名管道FIFO:

    匿名管道(pipe)只能用于“有血缘关系”的进程间。但通过 FIFO,不相关的进程也能交换数据。FIFO 是 Linux 基础文件类型中的一种。但,FIFO 文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各

    进程可以打开这个文件进行 read/write,实际上是在读写内核通道,这样就实现了进程间通信。

    创建方式:1. 命令:mkfifo 管道名  2. 库函数:int mkfifo(const char *pathname, mode_t mode); 成功:0; 失败:-1

    一旦使用 mkfifo 创建了一个 FIFO,就可以使用 open 打开它,常见的文件 I/O 函数都可用于 fifo。如:close、read、write、unlink 等

    共享映射区MMap:

    原理是将一个磁盘文件映射到内存地址空间缓冲区,于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。

    mmap 函数
    void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);  函数作用是建立映射文件。
    返回:成功:返回创建的映射区首地址; 失败:MAP_FAILED 宏
    addr: 建立映射区的首地址,由 Linux 内核指定。使用时,直接传递 NULL
    length: 欲创建映射区的大小
    prot: 映射区权限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
    flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
        MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。
        MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
    fd: 用来建立映射区的文件描述符
    offset: 映射文件的偏移(4k 的整数倍)

    父子等有血缘关系的进程之间也可以通过 mmap 建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数 flags:

    MAP_PRIVATE: (私有映射) 父子进程各自独占映射区;  MAP_SHARED: (共享映射) 父子进程共享映射区;

    munmap 函数

    int munmap(void *addr, size_t length);  函数作用是释放刚刚建立映射的内存缓冲区。

    返回值: 成功:0; 失败:-1  errno

    信号:

     信号重要且相对复杂,开新篇总结:https://www.cnblogs.com/clno1/p/12941316.html

    线程相关:

    线程(ight weight process)称为轻量级的进程,本质仍是进程(在 Linux 环境下),一个区别在于:进程,独立地址空间,拥有 PCB 。 线程,有独立的 PCB,但没有独立的地址空间(共享)。

    线程共享资源  1.文件描述符表  2.每种信号的处理方式  3.当前工作目录  4.用户 ID 和组 ID  5.内存地址空间 (.text/.data/.bss/heap/共享库)

    线程非共享资源  1.线程 id  2.处理器现场和栈指针(内核栈)  3.独立的栈空间(用户空间栈)  4.errno 变量  5.信号屏蔽字  6.调度优先级

    pthread_create 函数

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);  创建一个新线程。 其作用,对应进程中 fork() 函数。

    返回值:成功:0; 失败:错误号 -----Linux 环境下,所有线程特点,失败均直接返回错误号。

    参数:pthread_t:当前 Linux 中可理解为:typedef unsigned long int pthread_t;

    参数 1:传出参数,保存系统为我们分配好的线程 ID
    参数 2:通常传 NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数
    参数 3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
    参数 4:线程主函数执行期间所使用的参数。  (即参数3和4描述了线程的函数)

    pthread_self 函数

    pthread_t pthread_self(void);   获取线程 ID。其作用对应进程中 getpid() 函数。  返回值:成功:0; 失败:无!

    线程 ID:pthread_t 类型,本质:在 Linux 下为无符号整数(%lu),其他系统中可能是结构体实现

    线程 ID 是进程内部,识别标志。(两个进程间,线程 ID 允许相同)

    注意:不应使用全局变量 pthread_t tid,在子线程中通过 pthread_create 传出参数来获取线程 ID,而应使用pthread_self。

    pthread_exit 函数

    void pthread_exit(void *retval);  将单个线程退出  参数:retval 表示线程退出状态,通常传 NULL

    pthread_join 函数

    int pthread_join(pthread_t thread, void **retval);   阻塞等待线程退出,获取线程退出状态 其作用,对应进程中 waitpid() 函数。  成功:0;失败:错误号

    参数:thread:线程 ID (【注意】:不是指针);retval:存储线程结束状态。

    pthread_detach 函数

    int pthread_detach(pthread_t thread);   实现线程分离  成功:0;失败:错误号

    线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络、多线程服务器常用。

    一般情况下,线程终止后,其终止状态一直保留到其它线程调用 pthread_join 获取它的状态为止。但是线程也可以被置为 detach 状态, 这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。

    不能对一个已经处于 detach 状态的线程调用 pthread_join,这样的调用将返回 EINVAL 错误。也就是说,如果已经对一个线程调用了 pthread_detach 就不能再调用 pthread_join 了。

    也可使用 pthread_create 函数参 2(线程属性)来设置线程分离。

    pthread_cancel 函数

    int pthread_cancel(pthread_t thread);   杀死(取消)线程 其作用,对应进程中 kill() 函数。  成功:0;失败:错误号

    值得注意的是:线程的取消并不是实时的,而有一定的延时。需要等待线程到达某个取消点(检查点)。

    取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用 creat,open,pause,close,read,write..... 执行命令 man 7 pthreads 可以查看具备这些取消点的系统调用列表。

    可粗略认为一个系统调用(进入内核)即为一个取消点。如线程中没有取消点,可以通过调用 pthread_testcancel函数自行设置一个取消点。


    总结:终止某个线程而不终止整个进程,有三种方法:
    1. 从线程主函数 return。这种方法对主控线程不适用,从 main 函数 return 相当于调用 exit。
    2. 一个线程可以调用 pthread_cancel 终止同一进程中的另一个线程。
    3. 线程可以调用 pthread_exit 终止自己。

    线程同步:https://www.cnblogs.com/clno1/p/12942972.html

  • 相关阅读:
    error: Microsoft Visual C++ 9.0 is required. Get it from http://aka.ms/vcpython27
    C# Console 运行之后最小化到状态栏
    CentOS7 设置防火墙端口
    Spring boot 与quart集成并在Job中注入服务
    Cron表达式周1至周5,每天上午8点至下午18点,每分钟执行一次
    Electron 调用系统Office软件
    jquery之超简单的div显示和隐藏特效demo
    IE系列不支持圆角等CSS3属性的解决方案
    使用CSS3建立不可选的的文字
    ASP.NET中使用TreeView显示文件
  • 原文地址:https://www.cnblogs.com/clno1/p/12935702.html
Copyright © 2020-2023  润新知