复习

第一章: 1.linux系统架构

QQ截图20171120080445.png
            application  应用程序

            系统接口(系统调用、C库函数、其它库函数(图片压缩、转换)

操作系统: 进程管理、内存管理、设备管理、文件管理、网络管理

        设备驱动(字符型设备驱动、块设备驱动、网络设备驱动)

        硬件(键盘、鼠标、显示器、硬盘、内存、USB、串口)
2.错误处理函数
    errno  一个整数、标志当前内核中的错语的一个整数
    perror 打印当前程序的错语信息(自动根据系统的errno来区分当前到底是哪一个错误)
    strerror 用于把一个errno转换成对应的错语的描述字符串

strerror.c 包含头文件<errno.h> 把 int的整数转换成字符串 man error


#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(void)
{
    if(open("1.txt", O_RDONLY) < 0)
    {
        //printf("%d\n", errno);
        printf("%s\n", strerror(errno));
        perror("open");
    }

    return 0;
}

第三章: 对文件的数据进行读写


linux的文件IO(不带缓冲的文件IO、open家族的文件IO)
        open 打开或者创建文件,返回的是一个文件描述符
            0:代表标准输入(键盘)
            1:标准输出(显示器)
            2:标准出错(显示器)
                int open(const char *pathname, int flags);
                int open(const char *pathname, int flags, mode_t mode);
        flags:   O_RDONLY:只读的方式打开文件
                 O_WRONLY:只写方式打开文件
                 O_RDWR:读写方式打开文件
                 O_APPEND:追加的方式打开文件
                 O_CREAT:  如果文件不存在,就创建
                 O_TRUNC:  如果文件存在就清0
                 O_NONBLOCK: 以非阻塞的方式打开文件(字符型设备有效)
                 O_SYNC:每次写入数据到文件、都会等待直到数据真正的写到磁盘中才会继续向下运行
        mode:代表文件被创建的权限,一般用八进制整数表示0777 0666 0644

        read:读文件
        write:写文件
        close:关闭一个打开的文件
open write sync 函数
 

open.c 第一个参数是文件名字 第二个参数: 读|创建 返回一个文件描述符


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

struct task
{
    int id; 
    char file[64];
    int threadnum;
    int filesize;
};

struct task t = {
    1, "1.dat", 5, 1024 * 1024,
};
int main(void)
{
    //以读写方式打开文件、如果不存在就创建
    int fd = open("1.dat", O_RDWR | O_CREAT, 0777);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }

    //把数据写入到文件中
    int ret = write(fd, &t, sizeof(t));
    if(ret != sizeof(t))
    {
        perror("write");
        return -1;
    }

    //把文件指针移动到文件首
    lseek(fd, 0, SEEK_SET);

    //从文件中读取数据
    struct task tmp;        
    ret = read(fd, &tmp, sizeof(tmp));
    if(ret == sizeof(tmp))  
    {
        printf("%d %s %d %d\n", tmp.id, tmp.file, tmp.threadnum, tmp.filesize);
    }

    close(fd);
    return 0;
}










    文件的下载:任务 文件名 线程 文件大小
    把数据写入到文件中, 以二进制形式写入 write第一个参数: 文件描述符  第二个:地址 

        lseek:移动文件指针
            SEEK_SET  文件首
            SEEK_CUR  文件指针当前位置
            SEEK_END  文件尾
        fsync:把缓存在磁盘队列中的数据刷新到磁盘中
        fdopen:  把一个文件描述符转换成FILE类型的指针  int fd-->FILE *fp
        fileno:  把一个FILE *fp转换成一个文件描述符fd  FILE *fp ---> int fd

write.c

Wirte 函数  不带缓冲的
printf带缓冲的文件IO

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

int main(void)
{
    //write(1, "hello", strlen("hello")); //不带缓冲的文件IO
    printf("hello");//带缓冲的文件IO
    sleep(10);
    //write(1, "world", strlen("world"));
    printf("world");

    return 0;
}

第四章:对文件的属性进行操作(大小、权限、拥有者)、目录

stat.c()

stat参数: 文件属性,地址
        文件大小st.st_size  
        文件类型  S_ISREG(st.st_mode)
                    S_I     目录的大小是4k


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

int main(void)
{
    struct stat st;

    //获取文件属性
    //stat("1.dat", &st);
    //stat("/", &st);
    //stat("/dev/input/mouse0", &st);

    int fd = open("1.dat", O_RDONLY);
    fstat(fd, &st);

    printf("文件大小:%ld\n", st.st_size);

    if(S_ISREG(st.st_mode))
    {
        printf("这是一个普通文件\n");
    }
    else if(S_ISDIR(st.st_mode))
    {
        printf("这是一个目录文件\n");
    }
    else if(S_ISCHR(st.st_mode))
    {
        printf("这是一个字符设备文件\n");
    }

    return 0;
}
fsata是传递文件描述符(文件已经打开)

1.stat、fstat 获取一个文件的I结点信息(属性)
         struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* protection */
               nlink_t   st_nlink;   /* number of hard links */
               uid_t     st_uid;     /* user ID of owner */
               gid_t     st_gid;     /* group ID of owner */
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */
               blksize_t st_blksize; /* blocksize for file system I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */
               time_t    st_ctime;   /* time of last status change */
           };
        获取文件类型:
           S_ISREG(m)  is it a regular file?
           S_ISDIR(m)  directory?
           S_ISCHR(m)  character device?
           S_ISBLK(m)  block device?
           S_ISFIFO(m) FIFO (named pipe)?
           S_ISLNK(m)  symbolic link? (Not in POSIX.1-1996.)
           S_ISSOCK(m) socket? (Not in POSIX.1-1996.)


fileno.c


    fileno把一个FILE类型指针转换成一个文件描述符
        strlen是获取字符串的大小
        sizeof是获取字符串占据空间的大小
        fpopen把一个文件描述符转换成file类型指针

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

int main(void)
{
    FILE *fp;

    fp = fopen("2.txt", "w");
    if(fp == NULL)
    {
        perror("fopen");
        return -1;
    }

    //把一个FILE *转换成一个文件描述符
    int fd = fileno(fp);

    write(fd, "hello", strlen("hello"));
    //write(fd, "hello", sizeof("hello"));

    struct stat st;
    fstat(fd, &st);
    printf("%d\n", st.st_size);

    return 0;
}

演示函数fdopen.c


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

int main(void)
{
    int fd = open("2.txt", O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }

    FILE *fp;
    fp = fdopen(fd, "r"); //把一个文件描述符转换成一个FILE类型的指针
    if(fp == NULL)
    {
        perror("fdopen");
        return -1;
    }

    char buf[100] = {0};
    fgets(buf, sizeof(buf), fp);
    printf("%s\n", buf);

    fclose(fp);
    return 0;
}






        fgets读取数据
lsata获取软链接文件

    2.access判断一个文件是否存在、是否可读、是否可写、是否可执行
        R_OK是否可读
        W_OK是否可写
        X_OK是否可执行
        F_OK是否存在

access.c

        access(… … )      F_OK这个文件是否存在  等于0表示存在

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

int main(void)
{
    //判断一个文件是否存在
    int ret = access("2.txt", F_OK);
    if(ret == 0)
    {
        printf("这个文件存在\n");
    }
    else
    {
        printf("这个文件不存在\n");
    }

    return 0;
}


    3.umask修改进程创建文件的屏蔽字

umas.c umask()函数先取反,再与操作

    一,创建文件
      remove()删除文件函数
    二,获取文件属性  st.st_mode 获取文件低九位

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

int main(void)
{
    struct stat st;

    //修改文件屏蔽字
    umask(033);

    //如果这个文件存在,就删除
    if(access("3", F_OK) == 0)
    {
        remove("3");
    }

    //创建这个文件,以0777的方式创建
    int fd = open("3", O_RDONLY | O_CREAT, 0777);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }

    //获取这个文件的属性
    fstat(fd, &st);
    printf("文件的权限是%o\n", st.st_mode & 0x1ff);
    return 0;
}







    4.修改文件长度 truncate    ftruncate

truncate.c 修改文件大小

        参数: 文件名字 文件大小
        用于多线程下载
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
    //修改文件大小
    truncate("1.txt", 1024 * 1024 * 400);

    return 0;
}


    5.unlink  删除一个文件
    

unlink.c access(1.dat F_OK)=0 判断文件存在

        unlink()

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

int main(void)
{
    if(access("1.dat", F_OK) == 0)
    {
        printf("删除前存在1.dat\n");
    }
    else
    {
        printf("删除前不存在1.dat\n");
    }

    //删除文件
    //unlink("1.dat");
    remove("1.dat");

    if(access("1.dat", F_OK) == 0)
    {
        printf("删除后存在1.dat\n");
    }
    else
    {
        printf("删除后不存在1.dat\n");
    }

    return 0;
}


      rmdir  删除一个空目录
      remove 删除一个文件或者一个空目录
    6.mkdir 创建目录
    

mkdir.c


#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(void)
{
    //创建一个叫aaa的目录
    mkdir("aaa", 0777);

    return 0;
}


    7.opendir  readdir  closedir  rewinddir  打开目录、读取目录项、关闭目录

opendir.c man 3 dir 读取目录下的文件 目录的递归操作


#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main(void)
{
    //打开目录          
    DIR *dir = opendir("/");
    if(dir == NULL)
    {
        perror("opendir");
        return -1;
    }

    struct dirent *dent;
    while(1)
    {
        dent = readdir(dir); //读取目录的一项数据
        if(dent != NULL)
        {
            printf("%s\n", dent->d_name);
        }
        else
            break;
    }

    closedir(dir);
    return 0;
}


    8.getcwd  获取当前工作路径

getcwd.c getwcd返回一个当前工作路径

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

int main(void)
{
    char buf[100] = {0};
    //printf("当前工作路径是%s\n", getcwd(NULL, 0));
    printf("当前工作路径是%s\n", getcwd(buf, sizeof(buf)));
    printf("当前工作路径是%s\n", buf);

    //修改当前工作路径
    chdir("/opt");
    printf("当前工作路径是%s\n", getcwd(buf, sizeof(buf)));
    printf("当前工作路径是%s\n", buf);

    return 0;
}


    9.chdir   修改当前工作路径

第六章:

1.   密码文件的读取  /etc/passwd   存放的是用户的信息
        getpwnam  根据用户名获取用户信息
        getpwuid  根据用户ID获取用户信息
        getpwent  从/etc/passwd文件中读取一行数据

getpwname.c


        shell 中false是完全不能使用的
        getpwname()函数返回一个结构体 struct passed


#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>

int main(void)
{
    struct passwd *pw = NULL;
    //pw = getpwnam("root");
    pw = getpwuid(1);
    if(pw == NULL)
    {
        perror("getpwnam");
    }
    else
    {
        printf("%s:%s:%d:%d:%s:%s:%s\n", pw->pw_name, 
                pw->pw_passwd, 
                pw->pw_uid, 
                pw->pw_gid, 
                pw->pw_gecos, 
                pw->pw_dir, 
                pw->pw_shell);
    }

    return 0;
}

2.   阴影文件的读取  /etc/shadow   存放的是用户的密码相关的信息
        getspnam  根据用户名获取这个用户的密码相关的信息
        getspent

getspname.c 获取用户密码

#include <shadow.h>
#include <stdio.h>

int main(void)
{
    struct spwd *sp = getspnam("root");
    if(sp == NULL)      
    {
        perror("getspnam");
    }
    else
    {
        printf("%s\n", sp->sp_pwdp);
    }

    return 0;
}

3.时间相关的函数
    time   获取一个从1970.1.1 0:0:0到当前一个秒数
    gettimeofday   获取一个更精确的时间(微秒)
    settimeofday   设置系统的当前时间
    ctime  把一个time转换成字符串表示的年月日时分秒
    asctime  把一个struct tm的结构体转换成字符串的年月日时分秒
    localtime  获得一个年月日时分秒的结构体(本地时间)
    gmtime  获得一个年月日时分秒的结构体(全球时间)
    mktime  把一个struct tm的结构体转换成time_t的秒数

time.c


#include <stdio.h>
#include <time.h>

int main(void)
{
    printf("当前时间是%d\n", time(NULL));

    time_t t;

    time(&t);
    printf("当前时间是%d\n", (int)t);

    return 0;
}

ctime.c


#include <stdio.h>
#include <time.h>

int main(void)
{
    time_t t;

    time(&t);
    printf("当前时间是%d %s\n", (int)t, ctime(&t));

    return 0;
}

localtime.c gmtime(全球时间)

settime()(设置时间)     需要加sudo
#include <stdio.h>
#include <time.h>

int main(void)
{
    time_t t = time(NULL);
    //struct tm *tm = localtime(&t);        
    struct tm *tm = gmtime(&t);     
    
    printf("%d年%d月%d日 %d:%d:%d\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 
                tm->tm_hour, tm->tm_min, tm->tm_sec);
    return 0;
}

settimeofday.c 设置时间为微秒


#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int main(void)
{
    struct tm tm;
    tm.tm_sec = 30;
    tm.tm_min = 30;
    tm.tm_hour = 8;
    tm.tm_mday = 12;
    tm.tm_mon = 4;
    tm.tm_year = 2019 - 1900;
    time_t t = mktime(&tm);
    printf("%d\n", t);

    //printf("%s\n", ctime(&t));
    struct timeval tv;  
    tv.tv_sec = t;
    tv.tv_usec = 0;
    settimeofday(&tv, NULL);

    return 0;
}

第七章:进程入门

1.进程正常结束:从main函数直接返回、执行exit结束
    1.关闭所有文件、刷新所有缓冲区中的数据到文件中
    2.执行进程的清理函数

   进程不正常结束:不会执行以上两步。被信号杀死、_exit函数结束

2.进程的清理函数  atexit注册进程的清理函数

atexit.c

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

void do_quit(void);
int main(void)
{
    //注册清理函数        
    atexit(do_quit);

    sleep(5);
    printf("进程结束了\n");

    return 0;
}

void do_quit(void)
{
    printf("进程被清理了\n");
}

3.main的参数:main函数在被系统调用时、系统也是可以传递参数给main函数
    int main(int argc, char *argv[]);
    int main(int argc, char *argv[], char *env[]);

mian的参数.c

    argc 指传递给main函数的命令行中字符串的个数
    argv中存放的是命令行中的字符串

#include <stdio.h>

//main的参数
int main(int argc, char *argv[])
{
    //argc指传递给main的命令行中字符串的个数
    //argv中存放的是命令行中的字符串
    //  ./kkk  1 2 3 ---> argc = 4  argv[0] ="./kkk" argv[1] = "1" argv[2] = "2" ....

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

    return 0;
}

4. 进程环境变量表:
        通过main函数的第三个参数来访问
            int main(int argc, char *argv[], char *env[]);
        通过linux中定义的一个全局变量 environ来访问

环境变量表.c

声明全局变量 extern 指向系统的环境表

#include <stdio.h>

int main(int argc, char *argv[], char **env)
{
    //声明全局变量
    //extern char **environ;    
    int i;
    //for(i = 0; environ[i] != NULL; i++)
    for(i = 0; env[i] != NULL; i++)
    {
        //printf("%s\n", environ[i]);
        printf("%s\n", env[i]);
    }

    return 0;
}
5. 
        获取进程的某一个环境变量   getenv
        设置进程的环境变量   setenv

getenv.c 获取环境变量,可获得当前工作路径

getcpwd()相比较

设置进程的环境变量 setenv 修改PWD环境变量
参数

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

int main(void)
{
    char *pwd = getenv("PWD");
    printf("%s\n", pwd);
    printf("%s\n", getcwd(NULL, 0));

    chdir("/opt");
    //修改PWD环境变量
    setenv("PWD", getcwd(NULL, 0), 1);

    pwd = getenv("PWD");
    printf("%s\n", pwd);
    printf("%s\n", getcwd(NULL, 0));

    return 0;
}

6.  getrlimit 获取系统对资源的限制值
    setrlimit 修改系统对资源的限制值

第八章:

getpid  获取进程的PID
getppid  获取进程的父进程的PID
fork    创建子进程

getpid.c 函数获取当前进程pid

getppid()函数获取父进程pid


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

int main(void)
{
    pid_t pid;

    //获取当前进程ID
    pid = getpid();

    pid_t ppid;
    //获取当前进程父进程ID
    ppid = getppid();

    printf("进程ID是%d, 父进程ID是%d\n", pid, ppid);

    return 0;
}

fork.c 创建子进程

    fork()=0,子进程创建成功
    创建指针函数作为子进程,对fork进程封装
    read(0,buf,sizeof(buf))读取键盘操作
    bzero(buf,sizeof(buf)) buf清零
    读取鼠标必须先打开鼠标 mouse = open(“/dev/inout/mice, O_RDONLY”)

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

int main(void)
{
    int mouse;

    mouse = open("/dev/input/mice", O_RDONLY);
    if(mouse < 0)
    {
        perror("open");
        return -1;
    }

    int ret;
    char buf[100] = {0};
    if(fork() == 0) //子进程
    {
        while(1)
        {
            ret = read(0, buf, sizeof(buf));
            if(ret > 0)
            {
                printf("键盘说:%s\n", buf);
                bzero(buf, sizeof(buf));
            }
        }
    }
    else
    {
        while(1)
        {
            ret = read(mouse, buf, sizeof(buf));
            if(ret > 0)
            {
                printf("mouse say: %s\n", buf);
                bzero(buf, sizeof(buf));
            }
        }   
    }

    return 0;
}











fork2.c


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

//创建一个子进程/这个子进程的目的就是为了执行函数fun
int newprocess( void (*fun)(void) )
{
    //创建一个子进程
    if(fork() == 0) 
    {
        fun(); //在子进程中执行fun函数
        exit(0); //子进程退出
    }

    return 0;
}

void readmouse(void);
int main(void)
{
    int ret;
    char buf[100] = {0};

    //创建子进程用于读取鼠标
    newprocess(readmouse);

    while(1)    
    {
        //读取键盘
        ret = read(0, buf, sizeof(buf));
        if(ret > 0)
        {
            printf("键盘说:%s\n", buf);
            bzero(buf, sizeof(buf));
        }
    }
}

void readmouse(void)
{
    int ret;
    int mouse;
    char buf[100] = {0};
    mouse = open("/dev/input/mice", O_RDONLY);
    if(mouse < 0)
    {
        perror("open");
        return -1;
    }

    while(1)    
    {
        ret = read(mouse, buf, sizeof(buf));
        if(ret > 0)
        {
            printf("mouse say:%s\n", buf);
            bzero(buf, sizeof(buf));
        }
    }
}










第十章:信号

1.linux内核提供的一种机制、用于通知系统中的应用程序发生了某种事件
2.kill -l这个命令来列出linux支持的信号
    SIGINT  中断信号     ctrl+c
    SIGQUIT 退出信号     ctrl+\
    SIGFPE  浮点异常     除0  
    SIGSEGV 段错误       当系统中检测到段错误发生时

signal.c

#include <stdio.h>
#include <signal.h>

int main(void)
{
    char *ptr = NULL;
    *ptr = 10; //段错语
    
    int a = 1/0; //浮点异常
    printf("%d\n", a);

    printf("lksjdflksjdflksdjflskdjf\n");
    return 0;
}


3.进程收到信号后会做出三种反映:
    1. 按照默认的方式处理这个信号(每个信号都有一个默认动作)
    2. 忽略这个信号(signal(信号,SIG_IGN));
    3. 执行一个信号处理函数(signal(信号,函数));

注册函数signal() 参数:SIGIGN忽略信号 第二个参数可以是个函数

signal2.c


#include <stdio.h>
#include <signal.h>

void sig_int(int sig);
int main(void)
{
    //忽略SIGINT信号
//  signal(SIGINT, SIG_IGN);

    //注册SIGINT的信号处理函数
    signal(SIGINT, sig_int);

    while(1)
    {
        sleep(1);
        printf("i am running\n");
    }

    return 0;
}

void sig_int(int sig)
{
    printf("sigint产生了\n");
}





4.  kill  发出信号
    raise 产生一个信号(发送一个信号给自己)
    alarm 设置一个定时器,时间到来后会自动发出SIGALRM信号
    pause 让进程暂停下来、可以被任意信号唤醒
    abort 产生一个SIGABRT的信号  (用于终止进程、不正常终止)

kill.c 发送一个信号


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

//kill 发送一个信号
int main(void)
{
    //创建子进程
    if(fork() == 0)     
    {
        int count = 5;
        while(count > 0)
        {
            count--;
            printf("距离下班还有%d秒\n", count);
            sleep(1);
        }

        printf("下班了\n");
        kill(getppid(), SIGINT); //发送一个SIGINT信号给父进程
        exit(0);
    }
    else
    {
        while(1)
        {
            printf("我是父进程,,,我还有上班\n");
            sleep(1);
        }
    }

    return 0;
}

定时器信号. alarm()函数, 产生信号 进程默认退出

pause()函数 产生暂停信号,直到另一个信号产生.

alarm.c


#include <stdio.h>

int main(void)
{
    printf("程序将在5秒后结束\n");

    //5秒之后自动发出SIGALRM信号
    alarm(5);

    pause(); //程序暂停了

#if 0
    while(1)
    {
        ;;;;;
    }
#endif

    return 0;
}

sleep.c signal()防止进程被信号杀死


#include <stdio.h>
#include <signal.h>

void sig_proc(int sig);
int main(void)
{
    //signal(SIGALRM, SIG_IGN);
    signal(SIGALRM, sig_proc);

    alarm(5);

    printf("程序开始睡30秒\n");
    sleep(30);
    printf("程序退出\n");

    return 0;
}

void sig_proc(int sig)
{
    printf("sig\n");
}


第十一章:线程:

pthread_create:创建一个线程
pthread_self:获取线程的ID
pthread_join:等待其它线程结束(回收线程的资源)
pthread_cancel 通知一个线程结束
pthread_cleanup_push  为线程注册清理函数 
pthread_cleanup_pop

pthread_create.c 线程的封装

    编译的时候要链接线程库 -lpthread
    设置信号处理函数signal()
    通知一个线程结束 pthread_cancel  得先拿到线程的id号
    然后等待线程结束 pthread_join
    为线程注册清理函数 pthread_cleanup_push() 参数 :执行一个清理函数  arg
    必须调用pthread_cleanup_pop(1)函数

#include <pthread.h>
#include <stdio.h>
#include <signal.h>

int quit = 0;
void sig_quit(int sig);
void thread_quit(void *arg);
void *do_playmusic(void *arg);
int main(void)
{
    pthread_t tid[3];
    signal(SIGINT, sig_quit);

    //创建一个线程
    pthread_create(&tid[0], NULL, do_playmusic, (void *)"李香兰"); 
    pthread_create(&tid[1], NULL, do_playmusic, (void *)"霸王别姬");    
    pthread_create(&tid[2], NULL, do_playmusic, (void *)"北京北京");    

    while(quit == 0)
    {
        printf("打印----\n");
        sleep(1);
    }

    int i;
    for(i = 0; i < 3; i++)      
    {
        //通知这个线程退出
        pthread_cancel(tid[i]);
        //等待这个线程退出
        pthread_join(tid[i], NULL);
    }

    return 0;
}

void sig_quit(int sig)
{
    quit = 1;
}

void *do_playmusic(void *arg)
{
    char *music = arg;
    //为线程注册清理函数
    pthread_cleanup_push(thread_quit, arg);

    while(1)
    {
        printf("播放音乐---%s\n", music);
        sleep(1);
    }

    pthread_cleanup_pop(1);
}

void thread_quit(void *arg)
{
    printf("%s被清理了\n", (char *)arg);
}



==2017年3月2日11:27:20==

同步与互斥:
互斥锁:用于实现多个线程对同一个资源的访问是原子性
pthread_mutex_init
pthread_mutex_destroy
pthread_mutex_lock
pthread_mutex_unlock
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);

创建互斥锁pthread_mutex.c

    先进行初始化
    用完之后进行解散
    最后要进行销毁
缺点:效率比不佳锁低
    可能多个线程一起使用一个变量
    nanaosleep() 睡眠纳米级
有可能线程在锁的时候被通知取消。因此要取消线程的状态

如果一个锁加两次,就会出现死锁现象

#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>

int quit = 0;
int tick = 0;
pthread_mutex_t mutex;
void sig_quit(int sig);
void *thread(void *arg);
int main(void)
{
    int i;
    pthread_t tids[100];

    signal(SIGINT, sig_quit);

    pthread_mutex_init(&mutex, NULL);

    for(i = 0; i < 100; i++)    
    {
        pthread_create(&tids[i], NULL, thread, NULL);
    }

    while(quit == 0)
    {
        pause();    
    }

    for(i = 0; i < 100; i++)
    {
        pthread_cancel(tids[i]);
        pthread_join(tids[i], NULL);
    }

    pthread_mutex_destroy(&mutex);

    return 0;
}

void *thread(void *arg)
{
    struct timespec t;

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    while(1)
    {
        //加两次锁会导致死锁
        pthread_mutex_lock(&mutex); //加锁
        //pthread_mutex_lock(&mutex); //加锁
        tick++;
        t.tv_sec = 0;
        t.tv_nsec = 10;
        nanosleep(&t, NULL);
        printf("tick = %d\n", tick);
        //pthread_mutex_unlock(&mutex); //解锁
        pthread_mutex_unlock(&mutex); //解锁
    }
}

void sig_quit(int sig)
{
    quit = 1;
}

    如何防止死锁:1. 所有的锁只能加一次
                 2. 加锁顺序要一致
            
    读写锁:
        读锁与读锁之间是不互斥的
        读锁与写锁是互斥的
        写锁与写锁是互斥的
        pthread_rwlock_init
        pthread_rwlock_destroy
        pthread_rwlock_rdlock
        pthread_rwlock_wrlock
        pthread_rwlock_unlock

    条件变量:解决生产者与消费者之间同步的问题
        pthread_cond_init
        pthread_cond_destroy
        pthread_cond_wait   让线程等待条件满足
        pthread_cond_signal 通知等待的线程继续运行
        pthread_cond_broadcast   通知所有等待的线程继续运行

第十四章:高级IO
1. 非阻塞型IO
对于一些低速设备,linux对它们的读写操作使用的是阻塞型机制(由这些设备的驱动来决定的)。

block.c 阻塞型设备:如鼠标键盘,网络

        fcntl() 以键盘为例把键盘设置为非阻塞
                例:把键盘输入与 socket消息

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

int main(void)
{
    int ret;
    char buf[100];

    //把键盘设置为非阻塞
    fcntl(0, F_SETFL, O_NONBLOCK);

    while(1)
    {
        //阻塞型:如果没有数据可读,会阻塞,直到有数据可读为址
        ret = read(0, buf, sizeof(buf)); 
        if(ret > 0) 
        {
            printf("%s\n", buf);
        }
        else
        {
            printf("没有数据\n");
            sleep(1);
        }
    }

    return 0;
}

2. 把一个阻塞型的设备以非阻塞的方式进行读写操作
      以键盘为例:
        int fcntl(int fd, int cmd, ... /* arg */ );
        fcntl(0, F_SETFL, O_NONBLOCK);

3. 一个程序中同时要读写两个以上的阻塞型设备、此时可以采用IO多路转换的方式
        poll
        select

4. 异步IO机制
      1.  把设备设置为异步方式
            fcntl(fd, F_SETFL, O_ASYNC);

      2.  把进程的PID告诉给设备驱动
            fcntl(fd, F_SETOWN, getpid());

      3.  设置进程对SIGIO信号的处理函数
            signal(SIGIO, procdat); //procdat是一个函数,用于读取设备数据

第十五章:进程间通信

1.无名管道:只能用于有亲缘关系的进程之间的通信
    pipe
    它是一种单双工的通信

管道pipe.c

    无名pipe()  创建一个管道int fd=1,单双通,亲缘之间的通讯

#include <stdio.h>

int main(void)
{
    int fd[2];
    char buf[100] = {0};

    //创建一个无名管道
    pipe(fd);

    if(fork() == 0) //创建一个子进程
    {
        //子进程把数据写入到管道的写端
        write(fd[1], "hello", strlen("hello"));
    }
    else
    {
        //父进程
        read(fd[0], buf, sizeof(buf));
        printf("%s\n", buf);
    }
}

2.命名管道:能用于任意的两个进程之间的通信
    mkfifo
    它是一种单双工的通信

mkdir fifo

#### fifo1.c    fifo2.c
    mkfifo()函数创建管道文件   
        协同作用,你一句我一句(读取管道和键盘阻塞)
    打开读管道open(RFIFO, RDONLY)

### poll.c监听机制  poll() 参数 监听个数 -1  函数中有结构体{   }
    selet机制

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>

#define RFIFO "rfifo"
#define WFIFO "wfifo"
int main(void)
{
    int ret;
    char buf[100];

    //创建管道
    mkfifo(RFIFO, 0777);
    mkfifo(WFIFO, 0777);

    int rfd, wfd;   

    //打开RFIFO管道的读端
    rfd = open(RFIFO, O_RDONLY);
    if(rfd < 0)
    {
        perror("open");
        return -1;
    }

    //打开WFIFO管道的写端
    wfd = open(WFIFO, O_WRONLY);
    if(wfd < 0)
    {
        perror("open");
        return -1;
    }

//  fcntl(0, F_SETFL, O_NONBLOCK);
//  fcntl(rfd, F_SETFL, O_NONBLOCK);
    struct pollfd pfd[2];
    pfd[0].fd = 0;
    pfd[1].fd = rfd;
    pfd[0].events = POLLIN;
    pfd[1].events = POLLIN;
    while(1)
    {
        //监听0与rfd是否可读
        if(poll(pfd, 2, -1) > 0)
        {
            if(pfd[0].revents == POLLIN)    
            {
                //读键盘
                bzero(buf, sizeof(buf));
                ret = read(0, buf, sizeof(buf)); //阻塞
                if(ret > 0)
                {
                    //把数据写入到WFIFO的写端
                    write(wfd, buf, ret);
                }
            }

            if(pfd[1].revents == POLLIN)
            {
                //读管道RFIFO
                bzero(buf, sizeof(buf));
                ret = read(rfd, buf, sizeof(buf)); //阻塞
                if(ret > 0)
                {
                    printf("fifo2说:%s\n", buf);
                }
            }
        }
    }
    
    return 0;
}

fifo2.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>

#define RFIFO "rfifo"
#define WFIFO "wfifo"
int main(void)
{
    int ret;
    char buf[100];

    //创建管道
    mkfifo(RFIFO, 0777);
    mkfifo(WFIFO, 0777);

    int rfd, wfd;   

    //打开RFIFO管道的写端
    wfd = open(RFIFO, O_WRONLY);
    if(wfd < 0)
    {
        perror("open");
        return -1;
    }

    rfd = open(WFIFO, O_RDONLY);
    if(rfd < 0)
    {
        perror("open");
        return -1;
    }

    struct pollfd pfd[2];
    pfd[0].fd = 0;
    pfd[1].fd = rfd;
    pfd[0].events = POLLIN;
    pfd[1].events = POLLIN;
    while(1)
    {
        //监听0与rfd是否可读
        if(poll(pfd, 2, -1) > 0)
        {
            if(pfd[0].revents == POLLIN)    
            {
                //读键盘
                bzero(buf, sizeof(buf));
                ret = read(0, buf, sizeof(buf)); //阻塞
                if(ret > 0)
                {
                    //把数据写入到WFIFO的写端
                    write(wfd, buf, ret);
                }
            }

            if(pfd[1].revents == POLLIN)
            {
                //读管道RFIFO
                bzero(buf, sizeof(buf));
                ret = read(rfd, buf, sizeof(buf)); //阻塞
                if(ret > 0)
                {
                    printf("fifo2说:%s\n", buf);
                }
            }
        }
    }
    
    return 0;
}

3.消息队列

        ftok    通过一个文件来生成一个key
        msgget  创建消息队列
        msgrcv  从消息队列中取消息
        msgsnd  把消息发送到消息队列中
        msgctl  对消息队列进行操作(销毁、设置)

msgsnd.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>


#define MSGFILE "msgfile"
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[100];    /* message data */
};

struct student
{
    int age;
    char name[20];
};

int main(void)
{
    //创建msgfile这个文件 
    close(open(MSGFILE, O_RDONLY | O_CREAT, 0777));

    //根据msgfile生成一个key
    key_t key = ftok(MSGFILE, 1);

    //创建消息队列
    int msgid = msgget(key, IPC_CREAT | 0777);
    if(msgid == -1)
    {
        perror("msgget");
        return -1;
    }

//  int a = 30;
    struct msgbuf  msg;
    msg.mtype = 1;
    //strcpy(msg.mtext, "lsdjflksdjflkdsjflskdjf");
//  memcpy(msg.mtext, &a, sizeof(a));

    struct student stu = {
        20, "aaa"
    };
    memcpy(msg.mtext, &stu, sizeof(stu));

    //向消息队列中发送消息
    //msgsnd(msgid, &msg, strlen(msg.mtext), 0);
    //msgsnd(msgid, &msg, sizeof(a), 0);
    msgsnd(msgid, &msg, sizeof(stu), 0);

    return 0;
}

msgrcv.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>


#define MSGFILE "msgfile"
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[100];    /* message data */
};

struct student
{
    int age;
    char name[20];
};

int main(void)
{
    //创建msgfile这个文件 
    close(open(MSGFILE, O_RDONLY | O_CREAT, 0777));

    //根据msgfile生成一个key
    key_t key = ftok(MSGFILE, 1);

    //获取到消息队列
    int msgid = msgget(key, IPC_CREAT | 0777);
    if(msgid == -1)
    {
        perror("msgget");
        return -1;
    }

    struct msgbuf  msg;
    struct student stu;

    //从消息队列中取消息
    if(msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) > 0)
    {
        //printf("%s\n", msg.mtext);
        //printf("%d\n", *((int *)msg.mtext));
        memcpy(&stu, msg.mtext, sizeof(stu));
        printf("%d %s\n", stu.age, stu.name);
    }

    

    return 0;
}
```
### 4.共享存储
```
        ftok
        shmget  创建共享内存
        shmat   获取到共享内存的地址
        shmdt   释放共享内存的地址
        shmctl  销毁共享内存
```
### 5.信号量
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容