精华内容
下载资源
问答
  • linux进程通讯和线程同步知识点

    千次阅读 2015-11-23 20:27:20
    linux进程通讯和线程同步笔记主要参考《unix高级环境编程》,主要对进程和线程的部分的常用函数进行说明和总结。linux进程通讯和线程同步 进程控制 子进程退出状态的查询 进程通信-管道 匿名管道 进程通讯-XSI信号量...

    linux进程通讯和线程同步

    笔记主要参考《unix高级环境编程》,主要对进程和线程的部分的常用函数进行说明和总结。

    进程控制

    使用fork可以创建子进程,fork返回0为子进程,返回值大于0为父进程,子进程可以使用getppid来获得父进程的进程ID。若要编写守护进程( daemon),编写守护进程的步骤:
    (1)在父进程中执行fork并exit推出;
    (2)在子进程中调用setsid函数创建新的会话;
    (3)在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;
    (4)在子进程中调用umask函数,设置进程的umask为0;
    (5)在子进程中关闭任何不需要的文件描述符

    子进程退出状态的查询

    相关函数:

    *取得子进程终止状态函数,回收资源
    #include <sys/wait.h>
    pid_t wait(int *statloc);
    pid_t waitpid(pid_t pid, int *statloc, int options);
    *取得进程终止状态函数
    #include <sys/wait.h>
    int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
    *内核返回的终止进程及其所有子进程使用资源
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    pid_t wait3(int *statloc, int options, struct rusage *rusage);
    pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);

    在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程. 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程。如何查看僵尸进程: $ ps -el 其中,有标记为Z的进程就是僵尸进程 S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;Z代表僵死状态;T代表停止或跟踪状态。
    僵尸进程的避免
    1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起;
    2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收 ;
    3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号 ;
    4. 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。

    服务器采用了fork的话,要收集垃圾进程,防止僵死进程的产生,可以这样处理:signal(SIGCHLD,SIG_IGN); 交给系统init去回收。这里子进程就不会产生僵死进程了。

    void sig_chld(int signo) 
    { 
           pid_t   pid; 
           int     stat; 
           while((pid = waitpid(-1, &stat, WNOHANG)) > 0){ 
                   printf("child %d terminated\n", pid); 
           } 
            return; 

    进程通信-管道

    管道是单向的、先进先出的。它将一个程序的输入和另一个程序的输出连接起来。数据被一个进程读出后,将被从管道中删除。分为匿名和有名管道两种。前者用于父进程和子进程间的通信,后者用于同一系统的两个进程间通信。管道一般是半双工的(数据只能在一个方向流动)。

    匿名管道

    #include <unistd.h>
    int pipe(int fd[2]);
    其中,fd[0]用于读管道,fd[1]用于写管道。若成功则返回零,否则返回-1,错误原因存于errno中。

     int main(void)
    {
        int n;
        int fd[2];
        pid_t pid;
        char line[MAXLINE];
        if (pipe(fd) < 0)
           err_sys("pipe error");
        if ((pid = fork()) < 0) {
            err_sys("fork error");
        } else if (pid > 0) {   /* parent */
            close(fd[0]);
            write(fd[1], "hello world\n", 12);
        } else {     /* child */
            close(fd[1]);
            n = read(fd[0], line, MAXLINE);
            write(STDOUT_FILENO, line, n);
        }
        exit(0);
    }

    #### 使用命名管道

    命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。
    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char *filename, mode_t mode);
    int mkfifoat(int fd, const char *path, mode_t mode);
    int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);
    与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数只是创建一个FIFO文件,要使用命名管道还是将其打开。
    参考Linux进程间通信——使用命名管道
    程序一:

    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        const char *fifo_name = "/tmp/my_fifo";
        int pipe_fd = -1;
        int data_fd = -1;
        int res = 0;
        const int open_mode = O_WRONLY;
        int bytes_sent = 0;
        char buffer[PIPE_BUF + 1];
    
        if(access(fifo_name, F_OK) == -1)
        {
            //管道文件不存在
            //创建命名管道
            res = mkfifo(fifo_name, 0777);
            if(res != 0)
            {
                fprintf(stderr, "Could not create fifo %s\n", fifo_name);
                exit(EXIT_FAILURE);
            }
        }
    
        printf("Process %d opening FIFO O_WRONLY\n", getpid());
        //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
        pipe_fd = open(fifo_name, open_mode);
        data_fd = open("Data.txt", O_RDONLY);
        printf("Process %d result %d\n", getpid(), pipe_fd);
    
        if(pipe_fd != -1)
        {
            int bytes_read = 0;
            //向数据文件读取数据
            bytes_read = read(data_fd, buffer, PIPE_BUF);
            buffer[bytes_read] = '\0';
            while(bytes_read > 0)
            {
                //向FIFO文件写数据
                res = write(pipe_fd, buffer, bytes_read);
                if(res == -1)
                {
                    fprintf(stderr, "Write error on pipe\n");
                    exit(EXIT_FAILURE);
                }
                //累加写的字节数,并继续读取数据
                bytes_sent += res;
                bytes_read = read(data_fd, buffer, PIPE_BUF);
                buffer[bytes_read] = '\0';
            }
            close(pipe_fd);
            close(data_fd);
        }
        else
            exit(EXIT_FAILURE);
    
        printf("Process %d finished\n", getpid());
        exit(EXIT_SUCCESS);
    }

    程序二:

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <limits.h>
    #include <string.h>
    
    int main()
    {
        const char *fifo_name = "/tmp/my_fifo";
        int pipe_fd = -1;
        int data_fd = -1;
        int res = 0;
        int open_mode = O_RDONLY;
        char buffer[PIPE_BUF + 1];
        int bytes_read = 0;
        int bytes_write = 0;
        //清空缓冲数组
        memset(buffer, '\0', sizeof(buffer));
    
        printf("Process %d opening FIFO O_RDONLY\n", getpid());
        //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
        pipe_fd = open(fifo_name, open_mode);
        //以只写方式创建保存数据的文件
        data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
        printf("Process %d result %d\n",getpid(), pipe_fd);
    
        if(pipe_fd != -1)
        {
            do
            {
                //读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中
                res = read(pipe_fd, buffer, PIPE_BUF);
                bytes_write = write(data_fd, buffer, res);
                bytes_read += res;
            }while(res > 0);
            close(pipe_fd);
            close(data_fd);
        }
        else
            exit(EXIT_FAILURE);
    
        printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
        exit(EXIT_SUCCESS);
    }

    进程通讯-XSI信号量

    frok可以使用一个路径和项目ID(项目ID是0~255之间的字符值)产生一个IPC结构的键,键的数据结构为key_t;
    #include <sys/ipc.h>
    key_t ftok(const char *path, int id);

    信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。
    semget函数
    它的作用是创建一个新信号量或取得一个已有信号量,当我们使用XSI信号量时,首先需要调用semget来获得一个ID。原型为:
    #include <sys/sem.h>
    int semget(key_t key, int num_sems, int sem_flags);
    第二个参数num_sems指定需要的信号量数目,一个信号时它的值几乎总是1。
    semop函数
    它的作用是改变信号量的值,原型为:
    #include <sys/sem.h>
    int semop(int semid, struct sembuf semoparray[], size_t nops);
    sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:

    struct sembuf{
        short sem_num;//除非使用一组信号量,否则它为0
        short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
                        //一个是+1,即V(发送信号)操作。
        short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
                        //并在进程没有释放该信号量而终止时,操作系统释放信号量
    };

    semctl函数
    该函数用来直接控制信号量信息,它的原型为:
    #include <sys/sem.h>
    int semctl(int semid, int semnum, int cmd, ... /* union semun arg */ );
    第四个参数是可选的,取决于第三个参数cmd。
    具体示例参照例程Linux进程间通信——使用信号量具体代码如下:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/sem.h>
    
    union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *arry;
    };
    
    static int sem_id = 0;
    
    static int set_semvalue();
    static void del_semvalue();
    static int semaphore_p();
    static int semaphore_v();
    
    int main(int argc, char *argv[])
    {
        char message = 'X';
        int i = 0;
    
        //创建信号量
        sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
    
        if(argc > 1)
        {
            //程序第一次被调用,初始化信号量
            if(!set_semvalue())
            {
                fprintf(stderr, "Failed to initialize semaphore\n");
                exit(EXIT_FAILURE);
            }
            //设置要输出到屏幕中的信息,即其参数的第一个字符
            message = argv[1][0];
            sleep(2);
        }
        for(i = 0; i < 10; ++i)
        {
            //进入临界区
            if(!semaphore_p())
                exit(EXIT_FAILURE);
            //向屏幕中输出数据
            printf("%c", message);
            //清理缓冲区,然后休眠随机时间
            fflush(stdout);
            sleep(rand() % 3);
            //离开临界区前再一次向屏幕输出数据
            printf("%c", message);
            fflush(stdout);
            //离开临界区,休眠随机时间后继续循环
            if(!semaphore_v())
                exit(EXIT_FAILURE);
            sleep(rand() % 2);
        }
    
        sleep(10);
        printf("\n%d - finished\n", getpid());
    
        if(argc > 1)
        {
            //如果程序是第一次被调用,则在退出前删除信号量
            sleep(3);
            del_semvalue();
        }
        exit(EXIT_SUCCESS);
    }
    
    static int set_semvalue()
    {
        //用于初始化信号量,在使用信号量前必须这样做
        union semun sem_union;
    
        sem_union.val = 1;
        if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
            return 0;
        return 1;
    }
    
    static void del_semvalue()
    {
        //删除信号量
        union semun sem_union;
    
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
            fprintf(stderr, "Failed to delete semaphore\n");
    }
    
    static int semaphore_p()
    {
        //对信号量做减1操作,即等待P(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = -1;//P()
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1)
        {
            fprintf(stderr, "semaphore_p failed\n");
            return 0;
        }
        return 1;
    }
    
    static int semaphore_v()
    {
        //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = 1;//V()
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1)
        {
            fprintf(stderr, "semaphore_v failed\n");
            return 0;
        }
        return 1;
    }

    使用信号量集的例程参见:linux进程间通信-信号量(semaphore) 信号量集合的例子

    进程通讯 — 共享内存(存储)

    顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。
    shmget函数
    调用调用shmget,获得一个共享存储标识符:
    #include <sys/shm.h>
    int shmget(key_t key, size_t size, int flag);
    第二个参数,size以字节为单位指定需要共享的内存容量;
    第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。
    shmat函数
    第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。
    #include <sys/shm.h>
    void *shmat(int shmid, const void *addr, int flag);
    shmdt函数
    该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:
    #include <sys/shm.h>
    int shmdt(const void *addr);
    参数shmaddr是shmat函数返回的地址指针;
    shmctl函数
    与信号量的semctl函数一样,用来控制共享内存:
    #include <sys/shm.h>
    void *shmat(int shmid, const void *addr, int flag);
    第一个参数,shm_id是shmget函数返回的共享内存标识符。
    第二个参数,command是要采取的操作,它可以取下面的三个值 :
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段
    第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
    shmid_ds结构至少包括以下成员:
    struct shmid_ds
    {
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
    };
    具体的详细内容参考:Unix高级环境编程。
    具体代码参考:

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/shm.h>
    #include "shmdata.h"
    
    #define TEXT_SZ 2048
    struct shared_use_st
    {
        int written;//作为一个标志,非0:表示可读,0表示可写
        char text[TEXT_SZ];//记录写入和读取的文本
    };
    
    int main()
    {
        int running = 1;//程序是否继续运行的标志
        void *shm = NULL;//分配的共享内存的原始首地址
        struct shared_use_st *shared;//指向shm
        int shmid;//共享内存标识符
        //创建共享内存
        shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
        if(shmid == -1)
        {
            fprintf(stderr, "shmget failed\n");
            exit(EXIT_FAILURE);
        }
        //将共享内存连接到当前进程的地址空间
        shm = shmat(shmid, 0, 0);
        if(shm == (void*)-1)
        {
            fprintf(stderr, "shmat failed\n");
            exit(EXIT_FAILURE);
        }
        printf("\nMemory attached at %X\n", (int)shm);
        //设置共享内存
        shared = (struct shared_use_st*)shm;
        shared->written = 0;
        while(running)//读取共享内存中的数据
        {
            //没有进程向共享内存定数据有数据可读取
            if(shared->written != 0)
            {
                printf("You wrote: %s", shared->text);
                sleep(rand() % 3);
                //读取完数据,设置written使共享内存段可写
                shared->written = 0;
                //输入了end,退出循环(程序)
                if(strncmp(shared->text, "end", 3) == 0)
                    running = 0;
            }
            else//有其他进程在写数据,不能读取数据
                sleep(1);
        }
        //把共享内存从当前进程中分离
        if(shmdt(shm) == -1)
        {
            fprintf(stderr, "shmdt failed\n");
            exit(EXIT_FAILURE);
        }
        //删除共享内存
        if(shmctl(shmid, IPC_RMID, 0) == -1)
        {
            fprintf(stderr, "shmctl(IPC_RMID) failed\n");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    }

    其他程序的内容与之前相似,具体如:Linux进程间通信——使用共享内存
    进程间的同步可以使用共享内存中的互斥量来实现。若使用mutex互斥量,需要所有进程将相同文件映射到它们的地址空间里,并且使用PTHREAD_PROCESS_SHARED互斥属性在文件的相同偏移处初始化互斥量。

    进程通讯-文件记录锁

    记录锁(record locking)的功能是:当一个进程正在读或者修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一文件区域。
    函数 flock()锁住整个文件
    flock()会依参数operation所指定的方式对参数fd所指的文件做各种锁定或解除锁定的动作。此函数只能锁定整个文件,无法锁定文件的某一区域。
    #include<sys/file.h>
    int flock(int fd,int operation);
    参数 operation有下列四种情况:
      LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
      LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。
      LOCK_UN 解除文件锁定状态。
      LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做OR(|)组合。
      单一文件无法同时建立共享锁定和互斥锁定,而当使用dup()或fork()时文件描述词不会继承此种锁定。返回值 返回0表示成功,若有错误则返回-1,错误代码存于errno。

    记录锁控制函数fcnl()
    当一个进程正在读或修改文件的某个部分是,它可以阻止其他进程修改同一文件区。
    #include <fcntl.h>
    int fcntl(int fd, int cmd, ... /* struct flock *flockptr */ );
    参数:cmd = F_GETLK,测试能否建立一把锁;cmd = F_SETLK,设置锁;cmd = F_SETLKW, 阻塞设置一把锁。
    POSIX只定义fock结构中必须有以下的数据成员,具体实现可以增加:
    struct flock {
    short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
    short l_whence; /* 加锁的起始位置:SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start; /* 加锁的起始偏移,相对于l_whence */
    off_t l_len; /* 上锁的字节数*/
    pid_t l_pid; /* 已经占用锁的PID(只对F_GETLK 命令有效) */
    //
    };

    前面我们说了记录锁相当于读写锁的一种扩展类型,记录锁和读写锁一样也有两种锁:共享读锁(F_RDLCK)和独占写锁(F_WRLCK)。在使用规则上和读写锁也基本一样:
    * 文件给定字节区间,多个进程可以有一把共享读锁,即允许多个进程以读模式访问该字节区;
    * 文件给定字节区间,只能有一个进程有一把独占写锁,即只允许有一个进程已写模式访问该字节区;
    * 文件给定字节区间,如果有一把或多把读锁,不能在该字节区再加写锁,同样,如果有一把写锁,不能再该字节区再加任何读写锁。

    本部分内容可参考:Linux进程同步之记录锁(fcntl)和(UNIX环境高级编程(第3版))第14章 14.3节记录锁相关内容。关于fcntl函数的其他的用法参考(UNIX环境高级编程(第3版))第3章3.14节内容。

    进程通讯+线程同步-POSIX信号量

    POSIX信号量 :进程间通信中使用的XSI信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。POSIX信号量用在线程通信中。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。而只有0和1两种取值的信号量叫做二进制信号量,在这里将重点介绍。而信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。我们可以使用二进制信号量来完成这个工作。包含#include <semaphore.h>库即可使用下面的函数。
    创建函数sem_init/sem_open
    该函数用于创建信号量(作用相同),其原型如下:
    在单个进程中建议未命名:int sem_init(sem_t *sem, int pshared, unsigned int value);
    多个进程中建议命名:sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode,unsigned int value */ );
    可以使用sem_unlink来销毁一个命名信号量:int sem_unlink(const char *name);。如果信号量没有打开,则该信号量被销毁;否则,销毁延迟到最后一个打开的引用关闭。在单线程中,使用sem_open打开的后立即使用sem_unlink销毁命名。销毁了这个名字,其他进程就再也不能访问它,也简化了清理工作

    sem_wait函数
    该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。它的原型如下
    int sem_wait(sem_t *sem);
    sem_post函数
    该函数用于以原子操作的方式将信号量的值加1。它的原型如下:
    int sem_post(sem_t *sem);
    sem_destroy函数
    该函数用于对用完的信号量的清理。它的原型如下
    int sem_destroy(sem_t *sem);

    EINTR对POSIX信号量的影响(sem_wait、 EINTR)

    sem_wai和sem_timedwait如果超时值已经超过了调用规定的值,那么信号量不能被立即锁定,之后sem_timedwait() 为超时失败(error设置为ETIMEDOUT).
    所有的函数成功返回0,错误的话信号量的值不改动,返回-1.errno设定来标识错误.
    EINTR The call was interrupted by a signal handler; see signal(7).
      //调用被信号处理中断
    应该使用
      while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR)
    或者是:
      while((rv = sem_wait(&Poll_IN)) != 0 && (errno == EINTR))
    的方式;

    进程通讯+线程同步-socket套接字

    进程控制

    从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位;线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。
    “进程——资源分配的最小单位,线程——程序执行的最小单位”
    进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

    线程退出状态的查询

    相关函数:

    #include <pthread.h>

    nt pthread_equal(pthread_t tid1, pthread_t tid2);/是否相等
    pthread_t pthread_self(void);/获得自身线程ID

    int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void *), void *restrict arg); /创建线程

    void pthread_exit(void *rval_ptr);/线程结束
    int pthread_join(pthread_t thread, void **rval_ptr); /等待其他线程结束,阻塞的的函数
    int pthread_cancel(pthread_t tid); /请求取消同一进程中的其他线程

    void pthread_cleanup_push(void (*rtn)(void *), void *arg);
    void pthread_cleanup_pop(int execute); /线程清理处理程序

    int pthread_detach(pthread_t tid); /分离线程
    如果线程分离,底层的储存资源会在线程终止时立即被回收,而不用调用pthread_join进行回收。

    线程同步-互斥量

    互斥量从本质上说就是一把锁, 提供对共享资源的保护访问。一种用于多线程中的同步访问方法,它允许程序锁住某个对象,使得每次只能有一个线程访问它。
    相关函数:

     #include <pthread.h>
    int pthread_mutex_init(pthread_mutex_t *mutex, 
                            const pthread_mutexattr_t *mutexattr);
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    int pthread_mutex_destroy(pthread_mutex_t *mutex);

    进程间使用互斥量
    可以使用pthread_mutexattr_getpshared查询pthread_mutexattr_t结构,得到它的进程共享属性,使用pthread_mutexattr_setpshared寒素修改进程共享属性。参见本文档 进程通讯-共享内存(存储)最后一段,谈到进程中互斥量的使用方法。

    #include <pthread.h>
    int pthread_mutexattr_init(pthread_mutexattr_t *attr);
    int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
    
    int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr, int *restrict pshared);
    int pthread_mutexattr_setpshared(pthread_mutexattr *attr,int pshared);

    规定互斥量语句允许绑定线程的阻塞时间用函数: pthread_mutex_timedlock来实现,超时 pthread_mutex_timedlock不会对互斥量进行加锁,而是返回错误码(number on failure)ETIMEDOUT;

    #include <pthread.h>
    #include <time.h>
    int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                                const struct timespec *restrict tsptr);
    Returns: 0 if OK, error number on failure

    线程同步-读写锁

    相关函数:

    #include <pthread.h>
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                            const pthread_rwlockattr_t *restrict attr);
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    
    int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
                                const struct timespec *restrict tsptr);
    int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
                                const struct timespec *restrict tsptr);

    线程同步-条件变量

    相关函数

    #include <pthread.h>
    int pthread_cond_init(pthread_cond_t *restrict cond,
                        const pthread_condattr_t *restrict attr);
    int pthread_cond_destroy(pthread_cond_t *cond);
    
    int pthread_cond_wait(pthread_cond_t *restrict cond,
                        pthread_mutex_t *restrict mutex);
    int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                        pthread_mutex_t *restrict mutex,
                        const struct timespec *restrict tsptr);
    
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond);

    线程同步-屏障(barrier)

    屏障允许任意数量的线程等待,直到所有的线程完成处理工作,而且线程不用退出。所有的线程到达屏障后可以继续工作。
    相关函数

    int pthread_barrier_init(pthread_barrier_t *restrict barrier,
                        const pthread_barrierattr_t *restrict attr,
                        unsigned int count);
    int pthread_barrier_destroy(pthread_barrier_t *barrier);
    
    int pthread_barrier_wait(pthread_barrier_t *barrier);

    调用pthread_barrier_wait阻塞,直到有个pthread_barrier_wait调用满足pthread_barrier_init规定的数量,所有的线程都被唤醒。

    线程和信号(sigwait 、EINTR)

    相关函数

    #include <signal.h>
    int pthread_sigmask(int how, const sigset_t *restrict set,
                        sigset_t *restrict oset);
    int sigwait(const sigset_t *restrict set, int *restrict signop);
    int pthread_kill(pthread_t thread, int signo);

    sigwait碰到的问题主要参考:多线程下慎用sigwait 。问题线程大致流程如下:

    void thread(void *data)
    {
        int wait_sig = *(int*)data;
        sigset_t sigset;
        sigemptyset(&sigset);
        sigaddset(&sigset, wait_ig);
        while (1) {
            int signal;
            if (0 != sigwait(&sigset, &signal)) {
                break;
            }
        }
    }

    出现的问题是这个断言失败,也就是说sigwait失败了。后来打印出sigwait的返回值,发现sigwait的失败的原因时EINTR,也就说 sigwait被一个信号中断了,但是不知道信号来自何处。说到此处,先说明一下,sigwait这个函数很奇怪,跟一般的linux API不同。sigwait出错的时候,并不设置errno,而直接把errno错误值返回。
    通过增加一个新的判断

    int ret = sigwait(&sigset, &signal));
    if (EINTR == ret) {
        continue;
    }
    else if (ret) {
        break;
    }

    毕竟sigwait作为一个阻塞操作,因为收到信号而失败,是可以接受的行为,所以要对EINTR进行特殊处理。
    在POSIX标准中,当进程收到信号时,如果是多线程的情况,我们是无法确定是哪一个线程处理这个信号。而sigwait是从进程中pending的信号中,取走指定的信号。这样的话,如果要确保sigwait这个线程收到该信号,那么所有线程含主线程以及这个sigwait线程则必须block(pthread_sigmask和sigpromask)住这个信号。否则如果在两次sigwait之间,收到了指定信号,该信号很有可能被任意一个线程处理掉。

    线程同步相关代码详见《Unix环境高级编程(第3版)》

    其他知识点

    进程的间的相互通讯和线程间的同步是Linux/Unix类操作系统编程中比较重要的部分。虽然Linux和unix操作系统都提供了上述提供的机制,不同的通讯和同步方法在不同的操作系统体现出的性能差别较大,必要时可以使用系统特性的功能呢和内核函数(如:Linux 中sendfile拷贝,Epoll及其LT和ET模式;BSD的Kqueue事件队列)。其他一些通讯机制如:XSI消息队列等些许机制使用较少,且某些情况存在软件移植性的问题,本文不做讨论;UNIX套接字域可以用于进程通讯和线程同步,这个部分基本与Socket网络的函数基本一样,故此不做探讨。进程和线程的运行是一个十分复杂的系统,线程和进程的其他相关部分知识点也较为复杂。如:权限(系统权限,用户权限,组权限),I/O(文件I/O,标准I/O,非阻塞I/O,I/O多路转接-select/poll/epoll,异步I/O-AIO),守护进程,终端I/O,为终端等各种运行换件。另外进程和线程的信号(signal)系统也有相当的技巧。

    展开全文
  • Linux tty命令用于显示终端机连接标准输入...你可以执行tty(teletypewriter)指令查询目前使用的终端机的文件名称。 语法 tty [-s][--help][--version] 参数说明: -s或--silent或--quiet 不显示任何信息,只...

    tty命令用于显示终端机连接标准输入设备的文件名称。

    在Linux操作系统中,所有外围设备都有其名称与代号,这些名称代号以特殊文件的类型存放于/dev目录下。你可以执行tty(teletypewriter)指令查询目前使用的终端机的文件名称。

    语法

    tty [-s][--help][--version]

    参数说明

    • -s或--silent或--quiet 不显示任何信息,只回传状态代码。
    • --help 在线帮助。
    • --version 显示版本信息。

    实例

    显示当前终端

    # tty
    /dev/pts/4
    展开全文
  • netstat命令用于显示网络状态,利用netstat指令可以查询整个linux系统的网络情况。 常用功能 netstat是控制台命令,常用于监控TCP/IP网络,可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。 ...

    netstat命令

    netstat命令用于显示网络状态,利用netstat指令可以查询整个linux系统的网络情况。

    常用功能
    netstat是控制台命令,常用于监控TCP/IP网络,可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。
    netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。

    基本语法
    netstat [选项]

    选项说明
    -a(all):显示所有连线中的Socket,默认不显示LISTEN相关;
    -t(tcp):仅显示tcp相关的选项,TCP传输协议的连线状况;
    -u(udp) :仅显示udp相关的选项,UDP传输协议的连线状况;
    -c(continuous):持续列出网络状态;
    -g(groups):显示多重广播功能群组组员名单;
    在这里插入图片描述
    -i(interfaces) :显示网络界面信息表单;
    -l(listening):列出在监听(listening)的服务socket;
    -r(route):显示Routing tables;
    在这里插入图片描述
    -s(statistice):显示网络工作信息统计表;
    -v(verbose):显示指令执行过程;
    在这里插入图片描述

    显示详细的网络状况

    netstat -a
    

    在这里插入图片描述
    各参数说明:
    Proto:连接使用的协议;
    Recv-Q:接收队列;
    Send-Q:发送队列;
    Local Address:本地地址;
    Foreign Address:外部地址;
    state:socket状态;

    显示所有TCP端口

    netstat -at
    

    显示所有UDP端口

    netstat -au
    

    只显示监听端口

    netstat -l
    

    输出中不显示主机、端口和用户名

    netstat -n
    

    当不想显示主机、端口和用户名时,可以使用这一命令,将会使用数字代替。

    展开全文
  • iwlist :获取无线网卡相关数据语 法iwlist[网卡][必要参数][选择参数]功 能iwlist 命令:用于对/proc/net/wireless...执行权限: 超级用户 普通用户命令属性: 网络通讯参数必要参数scanning 搜索当前无线网络frequ...

    iwlist :获取无线网卡相关数据

    语 法

    iwlist[网卡][必要参数][选择参数]功 能iwlist 命令:用于对/proc/net/wireless文件进行分析,得出无线网卡相关信息

    类似命令: iwconfig  iwspy  iwevent  iwpriv

    相关教程: 暂缺,去论坛找找?

    执行权限: 超级用户 普通用户

    命令属性: 网络通讯

    参数必要参数

    scanning 搜索当前无线网络

    frequen  显示频道信息

    rate  显示连接速度

    power  显示电源模式

    txpower 显示功耗

    retry  显示重试连接次数(网络不稳定查看)

    ap 显示热点信息

    选择参数

    --help 显示帮助信息

    --version 显示版本信息

    范例

    范例1:显示频道信息

    snail@snail-laptop:~$ iwlist wlan0 frequen //显示频道信息

    wlan0   13 channels in total; available frequencies :

    Channel 01 : 2.412 GHz

    Channel 02 : 2.417 GHz

    Channel 03 : 2.422 GHz

    Channel 04 : 2.427 GHz

    Channel 05 : 2.432 GHz

    Channel 06 : 2.437 GHz

    Channel 07 : 2.442 GHz

    Channel 08 : 2.447 GHz

    Channel 09 : 2.452 GHz

    Channel 10 : 2.457 GHz

    Channel 11 : 2.462 GHz

    Channel 12 : 2.467 GHz

    Channel 13 : 2.472 GHz

    Current Frequency:2.412 GHz (Channel 1)

    范例2:搜索热点

    snail@snail-laptop:~$ iwlist wlan0 scanning  //搜索热点

    iwlist scanning

    lo    Interface doesn't support scanning.

    eth0   Interface doesn't support scanning.

    wlan0   Scan completed :

    Cell 01 - Address: E0:05:C5:B6:53:A0

    Channel:1

    Frequency:2.412 GHz (Channel 1)

    Quality=41/70 Signal level=-69 dBm

    Encryption key:off

    ESSID:"Web:192.168.1.2"

    Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 6 Mb/s; 9 Mb/s

    11 Mb/s; 12 Mb/s; 18 Mb/s

    Bit Rates:24 Mb/s; 36 Mb/s; 48 Mb/s; 54 Mb/s

    Mode:Master

    Extra:tsf=0000000342c72184

    Extra: Last beacon: 5220ms ago

    IE: Unknown: 000F5765623A3139322E3136382E312E32

    IE: Unknown: 010882848B0C12961824

    IE: Unknown: 030101

    IE: Unknown: 0706434E20010D14

    IE: Unknown: 200100

    IE: Unknown: 2A0100

    IE: Unknown: 32043048606C

    IE: Unknown: DD180050F2020101820003A4000027A4000042435E0062322F00

    Cell 02 - Address: 00:74:04:EE:45:83

    Channel:1

    Frequency:2.412 GHz (Channel 1)

    Quality=17/70 Signal level=-93 dBm

    Encryption key:off

    ESSID:"AR7WRD"

    Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 22 Mb/s

    Bit Rates:6 Mb/s; 9 Mb/s; 12 Mb/s; 18 Mb/s; 24 Mb/s

    36 Mb/s; 48 Mb/s; 54 Mb/s

    Mode:Master

    Extra:tsf=000000014e9071e2

    Extra: Last beacon: 5156ms ago

    IE: Unknown: 0006415237575244

    IE: Unknown: 010582848B962C

    IE: Unknown: 030101

    IE: Unknown: 050401030000

    IE: Unknown: 2A0104

    IE: Unknown: 32080C1218243048606C

    Cell 03 - Address: 00:15:EB:61:3C:9C

    Channel:6

    Frequency:2.437 GHz (Channel 6)

    Quality=23/70 Signal level=-87 dBm

    Encryption key:on

    ESSID:"ZTE-613C9C"

    Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s

    Bit Rates:6 Mb/s; 9 Mb/s; 12 Mb/s; 18 Mb/s; 24 Mb/s

    36 Mb/s; 48 Mb/s; 54 Mb/s

    Mode:Master

    Extra:tsf=00000001a74dcfdf

    Extra: Last beacon: 12800ms ago

    IE: Unknown: 000A5A54452D363133433943

    IE: Unknown: 010482848B96

    IE: Unknown: 030106

    IE: Unknown: 0706434E20010D14

    IE: Unknown: 2A0100

    IE: Unknown: 32080C1218243048606C

    Cell 04 - Address: D8:5D:4C:65:2E:A4

    Channel:6

    Frequency:2.437 GHz (Channel 6)

    Quality=29/70 Signal level=-81 dBm

    Encryption key:on

    ESSID:"xE9xC5xEDxC8"

    Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s

    12 Mb/s; 24 Mb/s; 36 Mb/s

    Bit Rates:9 Mb/s; 18 Mb/s; 48 Mb/s; 54 Mb/s

    Mode:Master

    Extra:tsf=0000003af3b62181

    Extra: Last beacon: 12460ms ago

    IE: Unknown: 0004E9C5EDC8

    IE: Unknown: 010882848B960C183048

    IE: Unknown: 030106

    IE: Unknown: 2A0100

    IE: Unknown: 32041224606C

    IE: IEEE 802.11i/WPA2 Version 1

    Group Cipher : TKIP

    Pairwise Ciphers (2) : TKIP CCMP

    Authentication Suites (1) : PSK

    Preauthentication Supported

    IE: WPA Version 1

    Group Cipher : TKIP

    Pairwise Ciphers (2) : TKIP CCMP

    Authentication Suites (1) : PSK

    Linux命令在线查询(http://www.lx138.com),iwlist  命令 详解:http://www.lx138.com/page.php?ID=427

    展开全文
  • 消息队列,Unix的通信机制之一,可以理解为是一个存放消息(数据)容器。...在系统中可以运行 ipcs -q 的命令查询消息队列。 1.键值生成 每一个消息队列都有一个对应的键值(key)相关联,如同共享内存和信号量一...
  • 先介绍一下IP和网卡的概念 网卡是专门负责网络通讯的硬件设备 IP地址每一台联网的电脑上...查询信息 ifconfig inet对应的地址是IPV4的地址,inet6对应的是IPV6的地址 一台计算机可能会有1个物理网卡和多个虚拟网卡...
  • 概述一个嵌入式系统通常需要通过串口与其主控系统进行全双工通讯,譬如我们的环境在线监测系统下的各类监测仪器如流量计,PH计,电机状态检测仪器就需要定时的接受控制系统发送来的查询和控制信息,并将执行结果或...
  • 登录、注册、私聊、群聊、注册vip、禁言、踢人、发送表情、发送短语、查询聊天记录、文件传输。 2、相关知识点 C/S架构 TCP、sqlite3、socket、基本文件操作 Makefile工程管理器进行项目工程管理, 用户界面...
  • 消息队列 消息:类型+数据 队列:先进先出(优先级队列) ... 消息队列可实现消息的随机查询,不一定要以先进先出的顺序读取,也可以按照类型进行读取。 2、相关函数 msgget(): 用来创建或访问一个...
  • -l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的程序) -p : 显示进程标识符和程序名称,每一个套接字/端口都属于一个程序。 -n : 不进行DNS轮询,显示IP(可以加速操作) 1...
  •  一个嵌入式系统通常需要通过串口与其主控系统进行全双工通讯,譬如我们的环境在线监测系统下的各类监测仪器如流量计,PH计,电机状态检测仪器就需要定时的接受控制系统发送来的查询和控制信息,并将执行结果或查询...
  • 1. 开放8080端口 firewall-cmd --zone=...--add-port=8080/tcp #添加端口,格式为:端口/通讯协议 --permanent #永久生效,没有此参数设置,重启后会失效。 2. 重启防火墙,输入命令: firewall-cmd --reload ...
  • linux异步通讯机制: Kernel ---> APP ① 查询方式② 中断方式③ 中断+Poll机制:指定超时时间以上三种机制都是APP主动去读取键值,那么第④种方式则是“异步通讯机制”,收发信号signal 小程序举例 signal.c ...
  • 汇总c,c++,c高级,shell,python,云计算linux汇总(个人收集–已开权限) 云计算案例与ppt(原版)汇总 云计算复习总结 云计算常用软件集合链接 本站针对md5、sha1等全球通用公开的加密算法进行反向查询 ...
  • Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -t : 指明显示TCP端口  -u : 指明显示...
  • linux查看端口

    2018-02-27 10:53:04
    Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -t : 指明显示TCP端口  -u : ...
  • linux端口占用

    2018-10-28 11:28:27
    Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -t : 指明显示TCP端口  -u : 指明显示UDP端口 ...
  • Linux Saltstack 简介

    2020-08-23 16:48:37
    Saltstack简介及部署 一、Saltstack简介二、saltstack通信验证机制SaltStack 的通讯架构模型saltstack通信机制Salt minion 验证机制:三、saltstack安装与配置主机准备master主机配置minion端配置配置连接minion端...
  • Linux系统端口命令

    2020-02-02 15:19:59
    Linux系统端口命令 netstat -an #查看本地监听端口和连接 netstat -ntlp #查看当前所有tcp端口 -t : 只显示TCP端口 -u : 只显示UDP端口 -l : 仅显示监听套接字(能够读写与收发通讯协议(protocol)的程序) -p : 显示...
  • Linux查看端口状态

    千次阅读 2018-06-02 21:01:15
    Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -t : 指明显示TCP端口   -u : 指明显示UDP...
  • 管道:速度慢,容量有限(64kB,ulimit -a可以查询的pipe size 指的是一次性写入的大小限制),只有父子进程能通讯 半双工的(即数据只能在一个方向上流动)----(匿名管道)int pipe(int fd[2]); // 返回值:若成功...
  • samba 做为主要的windown 和 Linux 通讯的服务器之一。 1. 查看是否安装了smb服务。  #rpm -ga|grep samba 或者 #service smb status 2.安装smb  # yum list samba 查询服务  # yum install samba 安装服务 ...
  • Linux如何查看端口状态

    万次阅读 2017-05-07 11:22:05
    Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -t : 指明显示TCP端口  -u : ...
  • linux-查看端口状态

    2016-04-26 10:10:20
    1、在Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询   2、netstat命令各个参数说明如下:  -t : 指明显示TCP端口  -u : 指明...
  • 日常Linux常用命令

    2020-05-18 14:55:30
    1、分割日志查询url 访问次数 cat filename | grep '... -l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的程序)  -p : 显示进程标识符和程序名称,每一个套接字/端口...
  • Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -a: 表示所有  -t : 指明显示...
  • Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下:  -t : 指明显示TCP端口  -u : 指明显示UDP端口 ...
  • linux常用配置文件

    2020-07-04 22:14:49
    nameserver(必须)–表名DNS服务器的IP地址,查询是从上到下,直到能通讯为止。 2 /etc/profile 配置系统的环境变量 变量是全局的,作用于任何用户。 此文件为系统的每个用户设置环境信息,当用户第一次登录时,该...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 147
精华内容 58
关键字:

linux查询通讯

linux 订阅